From: Mathieu Baudier Date: Fri, 22 Jul 2022 08:19:15 +0000 (+0200) Subject: Remove UI dependencies to JCR. X-Git-Tag: v2.3.10~92 X-Git-Url: https://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=1bd165d6454edb8a600ece696fab2a293e9eaf27 Remove UI dependencies to JCR. --- diff --git a/Makefile b/Makefile index 5c14dc8b1..b04cee306 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ swt/rap/org.argeo.cms.swt.rap \ swt/rap/org.argeo.cms.swt.rap.cli \ swt/rap/org.argeo.cms.e4.rap \ jcr/org.argeo.cms.jcr \ -jcr/org.argeo.cms.ui \ +jcr/org.argeo.cms.jcr.ui \ JAVADOC_BUNDLES = \ org.argeo.api.uuid \ diff --git a/jcr/org.argeo.cms.jcr.ui/.classpath b/jcr/org.argeo.cms.jcr.ui/.classpath new file mode 100644 index 000000000..81fe078c2 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/jcr/org.argeo.cms.jcr.ui/.project b/jcr/org.argeo.cms.jcr.ui/.project new file mode 100644 index 000000000..645296b6b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/.project @@ -0,0 +1,28 @@ + + + org.argeo.cms.jcr.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/jcr/org.argeo.cms.jcr.ui/META-INF/.gitignore b/jcr/org.argeo.cms.jcr.ui/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/jcr/org.argeo.cms.jcr.ui/bnd.bnd b/jcr/org.argeo.cms.jcr.ui/bnd.bnd new file mode 100644 index 000000000..c3c609c70 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/bnd.bnd @@ -0,0 +1,23 @@ +Bundle-Activator: org.argeo.cms.ui.internal.Activator +Bundle-ActivationPolicy: lazy + +Import-Package: org.eclipse.swt,\ +org.eclipse.jface.window,\ +org.eclipse.core.commands,\ +javax.jcr.security,\ +org.eclipse.rap.fileupload;version="[2.1,4)",\ +org.eclipse.rap.rwt;version="[2.1,4)",\ +org.eclipse.rap.rwt.application;version="[2.1,4)",\ +org.eclipse.rap.rwt.client;version="[2.1,4)",\ +org.eclipse.rap.rwt.client.service;version="[2.1,4)",\ +org.eclipse.rap.rwt.service;version="[2.1,4)",\ +org.eclipse.rap.rwt.widgets;version="[2.1,4)",\ +org.osgi.*;version=0.0.0,\ +javax.servlet.*;version="[3,5)",\ +* + +## TODO: in order to enable single sourcing, we have introduced dummy RAP packages +# in the RCP specific bundle org.argeo.eclipse.ui.rap. +# this was working with RAP 2.x but since we upgrade Rap to 3.1.x, +# there is a version range issue cause org.argeo.eclipse.ui.rap bundle is still in 2.x +# We enable large RAP version range as a workaround that must be fixed \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/build.properties b/jcr/org.argeo.cms.jcr.ui/build.properties new file mode 100644 index 000000000..c6baffa00 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + icons/ diff --git a/jcr/org.argeo.cms.jcr.ui/icons/loading.gif b/jcr/org.argeo.cms.jcr.ui/icons/loading.gif new file mode 100644 index 000000000..3288d1035 Binary files /dev/null and b/jcr/org.argeo.cms.jcr.ui/icons/loading.gif differ diff --git a/jcr/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png b/jcr/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png new file mode 100644 index 000000000..039650638 Binary files /dev/null and b/jcr/org.argeo.cms.jcr.ui/icons/noPic-goldenRatio-640px.png differ diff --git a/jcr/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png b/jcr/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png new file mode 100644 index 000000000..8e3abb518 Binary files /dev/null and b/jcr/org.argeo.cms.jcr.ui/icons/noPic-square-640px.png differ diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java new file mode 100644 index 000000000..fd1dda525 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiConstants.java @@ -0,0 +1,23 @@ +package org.argeo.cms.ui; + +/** Commons constants */ +@Deprecated +public interface CmsUiConstants { + // DATAKEYS +// public final static String STYLE = EclipseUiConstants.CSS_CLASS; +// public final static String MARKUP = EclipseUiConstants.MARKUP_SUPPORT; + @Deprecated + /* RWT.CUSTOM_ITEM_HEIGHT */ + public final static String ITEM_HEIGHT = "org.eclipse.rap.rwt.customItemHeight"; + + // EVENT DETAILS + @Deprecated + /* RWT.HYPERLINK */ + public final static int HYPERLINK = 1 << 26; + + // STANDARD RESOURCES + public final static String LOADING_IMAGE = "icons/loading.gif"; + + // MISCEALLENEOUS + String DATE_TIME_FORMAT = "dd/MM/yyyy, HH:mm"; +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java new file mode 100644 index 000000000..5f2377be5 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/CmsUiProvider.java @@ -0,0 +1,51 @@ +package org.argeo.cms.ui; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.acr.Content; +import org.argeo.cms.jcr.acr.JcrContent; +import org.argeo.cms.swt.acr.SwtUiProvider; +import org.argeo.jcr.JcrException; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Stateless factory building an SWT user interface given a JCR context. */ +public interface CmsUiProvider extends SwtUiProvider { + /** + * Initialises a user interface. + * + * @param parent the parent composite + * @param context a context node (holding the JCR underlying session), or null + */ + default Control createUi(Composite parent, Node context) throws RepositoryException { + // does nothing by default + return null; + } + + default Control createUiPart(Composite parent, Node context) { + try { + return createUi(parent, context); + } catch (RepositoryException e) { + throw new JcrException("Cannot create UI for context " + context, e); + } + } + + @Override + default Control createUiPart(Composite parent, Content context) { + if (context == null) + return createUiPart(parent, (Node) null); + if (context instanceof JcrContent) { + Node node = ((JcrContent) context).getJcrNode(); + return createUiPart(parent, node); + } else { +// CmsLog.getLog(CmsUiProvider.class) +// .warn("In " + getClass() + ", content " + context + " is not compatible with JCR."); +// return createUiPart(parent, (Node) null); + + throw new IllegalArgumentException( + "In " + getClass() + ", content " + context + " is not compatible with JCR"); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java new file mode 100644 index 000000000..5d77c156c --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java @@ -0,0 +1,11 @@ +package org.argeo.cms.ui; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +/** CmsUiProvider notified of initialisation with a system session. */ +public interface LifeCycleUiProvider extends CmsUiProvider { + public void init(Session adminSession) throws RepositoryException; + + public void destroy(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java new file mode 100644 index 000000000..4ce468826 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java @@ -0,0 +1,108 @@ +package org.argeo.cms.ui.eclipse.forms; +/** + * AbstractFormPart implements IFormPart interface and can be used as a + * convenient base class for concrete form parts. If a method contains + * code that must be called, look for instructions to call 'super' + * when overriding. + * + * @see org.eclipse.ui.forms.widgets.Section + * @since 1.0 + */ +public abstract class AbstractFormPart implements IFormPart { + private IManagedForm managedForm; + private boolean dirty = false; + private boolean stale = true; + /** + * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm) + */ + public void initialize(IManagedForm form) { + this.managedForm = form; + } + /** + * Returns the form that manages this part. + * + * @return the managed form + */ + public IManagedForm getManagedForm() { + return managedForm; + } + /** + * Disposes the part. Subclasses should override to release any system + * resources. + */ + public void dispose() { + } + /** + * Commits the part. Subclasses should call 'super' when overriding. + * + * @param onSave + * true if the request to commit has arrived as a + * result of the 'save' action. + */ + public void commit(boolean onSave) { + dirty = false; + } + /** + * Sets the overall form input. Subclases may elect to override the method + * and adjust according to the form input. + * + * @param input + * the form input object + * @return false + */ + public boolean setFormInput(Object input) { + return false; + } + /** + * Instructs the part to grab keyboard focus. + */ + public void setFocus() { + } + /** + * Refreshes the section after becoming stale (falling behind data in the + * model). Subclasses must call 'super' when overriding this method. + */ + public void refresh() { + stale = false; + // since we have refreshed, any changes we had in the + // part are gone and we are not dirty + dirty = false; + } + /** + * Marks the part dirty. Subclasses should call this method as a result of + * user interaction with the widgets in the section. + */ + public void markDirty() { + dirty = true; + managedForm.dirtyStateChanged(); + } + /** + * Tests whether the part is dirty i.e. its widgets have state that is + * newer than the data in the model. + * + * @return true if the part is dirty, false + * otherwise. + */ + public boolean isDirty() { + return dirty; + } + /** + * Tests whether the part is stale i.e. its widgets have state that is + * older than the data in the model. + * + * @return true if the part is stale, false + * otherwise. + */ + public boolean isStale() { + return stale; + } + /** + * Marks the part stale. Subclasses should call this method as a result of + * model notification that indicates that the content of the section is no + * longer in sync with the model. + */ + public void markStale() { + stale = true; + managedForm.staleStateChanged(); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java new file mode 100644 index 000000000..32b031b86 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java @@ -0,0 +1,730 @@ +package org.argeo.cms.ui.eclipse.forms; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +//import org.eclipse.swt.internal.graphics.Graphics; +import org.eclipse.swt.widgets.Display; + +/** + * Manages colors that will be applied to forms and form widgets. The colors are + * chosen to make the widgets look correct in the editor area. If a different + * set of colors is needed, subclass this class and override 'initialize' and/or + * 'initializeColors'. + * + * @since 1.0 + */ +public class FormColors { + /** + * Key for the form title foreground color. + * + * @deprecated use IFormColors.TITLE. + */ + public static final String TITLE = IFormColors.TITLE; + + /** + * Key for the tree/table border color. + * + * @deprecated use IFormColors.BORDER + */ + public static final String BORDER = IFormColors.BORDER; + + /** + * Key for the section separator color. + * + * @deprecated use IFormColors.SEPARATOR. + */ + public static final String SEPARATOR = IFormColors.SEPARATOR; + + /** + * Key for the section title bar background. + * + * @deprecated use IFormColors.TB_BG + */ + public static final String TB_BG = IFormColors.TB_BG; + + /** + * Key for the section title bar foreground. + * + * @deprecated use IFormColors.TB_FG + */ + public static final String TB_FG = IFormColors.TB_FG; + + /** + * Key for the section title bar gradient. + * + * @deprecated use IFormColors.TB_GBG + */ + public static final String TB_GBG = IFormColors.TB_GBG; + + /** + * Key for the section title bar border. + * + * @deprecated use IFormColors.TB_BORDER. + */ + public static final String TB_BORDER = IFormColors.TB_BORDER; + + /** + * Key for the section toggle color. Since 3.1, this color is used for all + * section styles. + * + * @deprecated use IFormColors.TB_TOGGLE. + */ + public static final String TB_TOGGLE = IFormColors.TB_TOGGLE; + + /** + * Key for the section toggle hover color. + * + * @deprecated use IFormColors.TB_TOGGLE_HOVER. + */ + public static final String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER; + + protected Map colorRegistry = new HashMap(10); + + private LocalResourceManager resources; + + protected Color background; + + protected Color foreground; + + private boolean shared; + + protected Display display; + + protected Color border; + + /** + * Creates form colors using the provided display. + * + * @param display + * the display to use + */ + public FormColors(Display display) { + this.display = display; + initialize(); + } + + /** + * Returns the display used to create colors. + * + * @return the display + */ + public Display getDisplay() { + return display; + } + + /** + * Initializes the colors. Subclasses can override this method to change the + * way colors are created. Alternatively, only the color table can be + * modified by overriding initializeColorTable(). + * + * @see #initializeColorTable + */ + protected void initialize() { + background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); + foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); + initializeColorTable(); + updateBorderColor(); + } + + /** + * Allocates colors for the following keys: BORDER, SEPARATOR and + * TITLE. Subclasses can override to allocate these colors differently. + */ + protected void initializeColorTable() { + createTitleColor(); + createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB()); + RGB black = getSystemColor(SWT.COLOR_BLACK); + RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); + createColor(IFormColors.BORDER, blend(borderRGB, black, 80)); + } + + /** + * Allocates colors for the section tool bar (all the keys that start with + * TB). Since these colors are only needed when TITLE_BAR style is used with + * the Section widget, they are not needed all the time and are allocated on + * demand. Consequently, this method will do nothing if the colors have been + * already initialized. Call this method prior to using colors with the TB + * keys to ensure they are available. + */ + public void initializeSectionToolBarColors() { + if (colorRegistry.containsKey(IFormColors.TB_BG)) + return; + createTitleBarGradientColors(); + createTitleBarOutlineColors(); + createTwistieColors(); + } + + /** + * Allocates additional colors for the form header, namely background + * gradients, bottom separator keylines and DND highlights. Since these + * colors are only needed for clients that want to use these particular + * style of header rendering, they are not needed all the time and are + * allocated on demand. Consequently, this method will do nothing if the + * colors have been already initialized. Call this method prior to using + * color keys with the H_ prefix to ensure they are available. + */ + protected void initializeFormHeaderColors() { + if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2)) + return; + createFormHeaderColors(); + } + + /** + * Returns the RGB value of the system color represented by the code + * argument, as defined in SWT class. + * + * @param code + * the system color constant as defined in SWT + * class. + * @return the RGB value of the system color + */ + public RGB getSystemColor(int code) { + return getDisplay().getSystemColor(code).getRGB(); + } + + /** + * Creates the color for the specified key using the provided RGB object. + * The color object will be returned and also put into the registry. When + * the class is disposed, the color will be disposed with it. + * + * @param key + * the unique color key + * @param rgb + * the RGB object + * @return the allocated color object + */ + public Color createColor(String key, RGB rgb) { + // RAP [rh] changes due to missing Color constructor +// Color c = getResourceManager().createColor(rgb); +// Color prevC = (Color) colorRegistry.get(key); +// if (prevC != null && !prevC.isDisposed()) +// getResourceManager().destroyColor(prevC.getRGB()); +// Color c = Graphics.getColor(rgb); + Color c = new Color(display, rgb); + colorRegistry.put(key, c); + return c; + } + + /** + * Creates a color that can be used for areas of the form that is inactive. + * These areas can contain images, links, controls and other content but are + * considered auxilliary to the main content area. + * + *

+ * The color should not be disposed because it is managed by this class. + * + * @return the inactive form color + */ + public Color getInactiveBackground() { + String key = "__ncbg__"; //$NON-NLS-1$ + Color color = getColor(key); + if (color == null) { + RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION); + // a blend of 95% white and 5% list selection system color + RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5); + color = createColor(key, ncbg); + } + return color; + } + + /** + * Creates the color for the specified key using the provided RGB values. + * The color object will be returned and also put into the registry. If + * there is already another color object under the same key in the registry, + * the existing object will be disposed. When the class is disposed, the + * color will be disposed with it. + * + * @param key + * the unique color key + * @param r + * red value + * @param g + * green value + * @param b + * blue value + * @return the allocated color object + */ + public Color createColor(String key, int r, int g, int b) { + return createColor(key, new RGB(r,g,b)); + } + + /** + * Computes the border color relative to the background. Allocated border + * color is designed to work well with white. Otherwise, stanard widget + * background color will be used. + */ + protected void updateBorderColor() { + if (isWhiteBackground()) + border = getColor(IFormColors.BORDER); + else { + border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + Color bg = getImpliedBackground(); + if (border.getRed() == bg.getRed() + && border.getGreen() == bg.getGreen() + && border.getBlue() == bg.getBlue()) + border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW); + } + } + + /** + * Sets the background color. All the toolkits that use this class will + * share the same background. + * + * @param bg + * background color + */ + public void setBackground(Color bg) { + this.background = bg; + updateBorderColor(); + updateFormHeaderColors(); + } + + /** + * Sets the foreground color. All the toolkits that use this class will + * share the same foreground. + * + * @param fg + * foreground color + */ + public void setForeground(Color fg) { + this.foreground = fg; + } + + /** + * Returns the current background color. + * + * @return the background color + */ + public Color getBackground() { + return background; + } + + /** + * Returns the current foreground color. + * + * @return the foreground color + */ + public Color getForeground() { + return foreground; + } + + /** + * Returns the computed border color. Border color depends on the background + * and is recomputed whenever the background changes. + * + * @return the current border color + */ + public Color getBorderColor() { + return border; + } + + /** + * Tests if the background is white. White background has RGB value + * 255,255,255. + * + * @return true if background is white, false + * otherwise. + */ + public boolean isWhiteBackground() { + Color bg = getImpliedBackground(); + return bg.getRed() == 255 && bg.getGreen() == 255 + && bg.getBlue() == 255; + } + + /** + * Returns the color object for the provided key or null if + * not in the registry. + * + * @param key + * the color key + * @return color object if found, or null if not. + */ + public Color getColor(String key) { + if (key.startsWith(IFormColors.TB_PREFIX)) + initializeSectionToolBarColors(); + else if (key.startsWith(IFormColors.H_PREFIX)) + initializeFormHeaderColors(); + return (Color) colorRegistry.get(key); + } + + /** + * Disposes all the colors in the registry. + */ + public void dispose() { + if (resources != null) + resources.dispose(); + resources = null; + colorRegistry = null; + } + + /** + * Marks the colors shared. This prevents toolkits that share this object + * from disposing it. + */ + public void markShared() { + this.shared = true; + } + + /** + * Tests if the colors are shared. + * + * @return true if shared, false otherwise. + */ + public boolean isShared() { + return shared; + } + + /** + * Blends c1 and c2 based in the provided ratio. + * + * @param c1 + * first color + * @param c2 + * second color + * @param ratio + * percentage of the first color in the blend (0-100) + * @return the RGB value of the blended color + */ + public static RGB blend(RGB c1, RGB c2, int ratio) { + int r = blend(c1.red, c2.red, ratio); + int g = blend(c1.green, c2.green, ratio); + int b = blend(c1.blue, c2.blue, ratio); + return new RGB(r, g, b); + } + + /** + * Tests the source RGB for range. + * + * @param rgb + * the tested RGB + * @param from + * range start (excluding the value itself) + * @param to + * range end (excluding the value itself) + * @return true if at least one of the primary colors in the + * source RGB are within the provided range, false + * otherwise. + */ + public static boolean testAnyPrimaryColor(RGB rgb, int from, int to) { + if (testPrimaryColor(rgb.red, from, to)) + return true; + if (testPrimaryColor(rgb.green, from, to)) + return true; + if (testPrimaryColor(rgb.blue, from, to)) + return true; + return false; + } + + /** + * Tests the source RGB for range. + * + * @param rgb + * the tested RGB + * @param from + * range start (excluding the value itself) + * @param to + * tange end (excluding the value itself) + * @return true if at least two of the primary colors in the + * source RGB are within the provided range, false + * otherwise. + */ + public static boolean testTwoPrimaryColors(RGB rgb, int from, int to) { + int total = 0; + if (testPrimaryColor(rgb.red, from, to)) + total++; + if (testPrimaryColor(rgb.green, from, to)) + total++; + if (testPrimaryColor(rgb.blue, from, to)) + total++; + return total >= 2; + } + + /** + * Blends two primary color components based on the provided ratio. + * + * @param v1 + * first component + * @param v2 + * second component + * @param ratio + * percentage of the first component in the blend + * @return + */ + private static int blend(int v1, int v2, int ratio) { + int b = (ratio * v1 + (100 - ratio) * v2) / 100; + return Math.min(255, b); + } + + private Color getImpliedBackground() { + if (getBackground() != null) + return getBackground(); + return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); + } + + private static boolean testPrimaryColor(int value, int from, int to) { + return value > from && value < to; + } + + private void createTitleColor() { + /* + * RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light + * if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80); + * else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK, + * 50); createColor(TITLE, rgb); + */ + RGB bg = getImpliedBackground().getRGB(); + RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION); + RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND); + RGB rgb = listSelection; + + // Group 1 + // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or + // between 0 and 120, then use 100% LIST_SELECTION as it is (no + // additions) + // Examples: XP Default, Win Classic Standard, Win High Con White, Win + // Classic Marine + if (testTwoPrimaryColors(listSelection, -1, 121)) + rgb = listSelection; + // Group 2 + // When LIST_BACKGROUND = white (255, 255, 255) or not black, text + // colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over + // LIST_BACKGROUND + // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or + // between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION + // foreground colour + // Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX + // Aqua, OSX Graphite, Linux GTK + else if (testTwoPrimaryColors(listSelection, 120, 256) + || (bg.red == 0 && bg.green == 0 && bg.blue == 0)) + rgb = blend(listSelection, listForeground, 50); + // Group 3 + // When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION + // @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND + // Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to + // LIST_SELECTION foreground colour + // Examples: Win High Con Black, Win High Con #1, Win High Con #2 + // (covered in the second part of the OR clause above) + createColor(IFormColors.TITLE, rgb); + } + + private void createTwistieColors() { + RGB rgb = getColor(IFormColors.TITLE).getRGB(); + RGB white = getSystemColor(SWT.COLOR_WHITE); + createColor(TB_TOGGLE, rgb); + rgb = blend(rgb, white, 60); + createColor(TB_TOGGLE_HOVER, rgb); + } + + private void createTitleBarGradientColors() { + RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + RGB bg = getImpliedBackground().getRGB(); + + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(tbBg, 179, 256)) + tbBg = blend(tbBg, bg, 30); + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(tbBg, 120, 180)) + tbBg = blend(tbBg, bg, 20); + + // Group 3 + // Rule: Everything else + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else { + tbBg = blend(tbBg, bg, 10); + } + + createColor(IFormColors.TB_BG, tbBg); + + // for backward compatibility + createColor(TB_GBG, tbBg); + } + + private void createTitleBarOutlineColors() { + // title bar outline - border color + RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + RGB bg = getImpliedBackground().getRGB(); + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(tbBorder, 179, 256)) + tbBorder = blend(tbBorder, bg, 70); + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + + // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(tbBorder, 120, 180)) + tbBorder = blend(tbBorder, bg, 50); + + // Group 3 + // Rule: Everything else + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + + // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + else { + tbBorder = blend(tbBorder, bg, 30); + } + createColor(FormColors.TB_BORDER, tbBorder); + } + + private void updateFormHeaderColors() { + if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) { + disposeIfFound(IFormColors.H_GRADIENT_END); + disposeIfFound(IFormColors.H_GRADIENT_START); + disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1); + disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2); + disposeIfFound(IFormColors.H_HOVER_LIGHT); + disposeIfFound(IFormColors.H_HOVER_FULL); + initializeFormHeaderColors(); + } + } + + private void disposeIfFound(String key) { + Color color = getColor(key); + if (color != null) { + colorRegistry.remove(key); + // RAP [rh] changes due to missing Color#dispose() +// color.dispose(); + } + } + + private void createFormHeaderColors() { + createFormHeaderGradientColors(); + createFormHeaderKeylineColors(); + createFormHeaderDNDColors(); + } + + private void createFormHeaderGradientColors() { + RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + Color bgColor = getImpliedBackground(); + RGB bg = bgColor.getRGB(); + RGB bottom, top; + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(titleBg, 179, 256)) { + bottom = blend(titleBg, bg, 30); + top = bg; + } + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(titleBg, 120, 180)) { + bottom = blend(titleBg, bg, 20); + top = bg; + } + + // Group 3 + // Rule: If at least 2 of the RGB values are equal to or between 0 and + // 120, then apply specified opacity for Group 3 + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND + // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND + else { + bottom = blend(titleBg, bg, 10); + top = bg; + } + createColor(IFormColors.H_GRADIENT_END, top); + createColor(IFormColors.H_GRADIENT_START, bottom); + } + + private void createFormHeaderKeylineColors() { + RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); + Color bgColor = getImpliedBackground(); + RGB bg = bgColor.getRGB(); + RGB keyline2; + // H_BOTTOM_KEYLINE1 + createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255)); + + // H_BOTTOM_KEYLINE2 + // Group 1 + // Rule: If at least 2 of the RGB values are equal to or between 180 and + // 255, then apply specified opacity for Group 1 + // Examples: Vista, XP Silver, Wn High Con #2 + // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND + if (testTwoPrimaryColors(titleBg, 179, 256)) + keyline2 = blend(titleBg, bg, 70); + + // Group 2 + // Rule: If at least 2 of the RGB values are equal to or between 121 and + // 179, then apply specified opacity for Group 2 + // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black + // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND + else if (testTwoPrimaryColors(titleBg, 120, 180)) + keyline2 = blend(titleBg, bg, 50); + + // Group 3 + // Rule: If at least 2 of the RGB values are equal to or between 0 and + // 120, then apply specified opacity for Group 3 + // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX + // Aqua, Wn High Con White, Wn High Con #1 + + // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND + else + keyline2 = blend(titleBg, bg, 30); + // H_BOTTOM_KEYLINE2 + createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2); + } + + private void createFormHeaderDNDColors() { + RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT); + Color bgColor = getImpliedBackground(); + RGB bg = bgColor.getRGB(); + RGB light, full; + // ALL Themes + // + // Light Highlight + // When *near* the 'hot' area + // Rule: If near the title in the 'hot' area, show background highlight + // TITLE_BACKGROUND_GRADIENT @ 40% + light = blend(titleBg, bg, 40); + // Full Highlight + // When *on* the title area (regions 1 and 2) + // Rule: If near the title in the 'hot' area, show background highlight + // TITLE_BACKGROUND_GRADIENT @ 60% + full = blend(titleBg, bg, 60); + // H_DND_LIGHT + // H_DND_FULL + createColor(IFormColors.H_HOVER_LIGHT, light); + createColor(IFormColors.H_HOVER_FULL, full); + } + + private LocalResourceManager getResourceManager() { + if (resources == null) + resources = new LocalResourceManager(JFaceResources.getResources()); + return resources; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java new file mode 100644 index 000000000..9e931ba50 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java @@ -0,0 +1,122 @@ +package org.argeo.cms.ui.eclipse.forms; + +import java.util.HashMap; + +import org.eclipse.jface.resource.DeviceResourceException; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +//import org.eclipse.swt.internal.graphics.Graphics; +import org.eclipse.swt.widgets.Display; + +public class FormFonts { + private static FormFonts instance; + + public static FormFonts getInstance() { + if (instance == null) + instance = new FormFonts(); + return instance; + } + + private LocalResourceManager resources; + private HashMap descriptors; + + private FormFonts() { + } + + private class BoldFontDescriptor extends FontDescriptor { + private FontData[] fFontData; + + BoldFontDescriptor(Font font) { + // RAP [if] Changes due to different way of creating fonts + // fFontData = font.getFontData(); + // for (int i = 0; i < fFontData.length; i++) { + // fFontData[i].setStyle(fFontData[i].getStyle() | SWT.BOLD); + // } + FontData fontData = font.getFontData()[0]; + // Font boldFont = Graphics.getFont( fontData.getName(), + // fontData.getHeight(), + // fontData.getStyle() | SWT.BOLD ); + Font boldFont = new Font(Display.getCurrent(), fontData.getName(), fontData.getHeight(), + fontData.getStyle() | SWT.BOLD); + fFontData = boldFont.getFontData(); + } + + public boolean equals(Object obj) { + if (obj instanceof BoldFontDescriptor) { + BoldFontDescriptor desc = (BoldFontDescriptor) obj; + if (desc.fFontData.length != fFontData.length) + return false; + for (int i = 0; i < fFontData.length; i++) + if (!fFontData[i].equals(desc.fFontData[i])) + return false; + return true; + } + return false; + } + + public int hashCode() { + int hash = 0; + for (int i = 0; i < fFontData.length; i++) + hash = hash * 7 + fFontData[i].hashCode(); + return hash; + } + + public Font createFont(Device device) throws DeviceResourceException { + // RAP [if] Changes due to different way of creating fonts + return new Font(device, fFontData[0]); + // return Graphics.getFont( fFontData[ 0 ] ); + } + + public void destroyFont(Font previouslyCreatedFont) { + // RAP [if] unnecessary + // previouslyCreatedFont.dispose(); + } + } + + public Font getBoldFont(Display display, Font font) { + checkHashMaps(); + BoldFontDescriptor desc = new BoldFontDescriptor(font); + Font result = getResourceManager().createFont(desc); + descriptors.put(result, desc); + return result; + } + + public boolean markFinished(Font boldFont) { + checkHashMaps(); + BoldFontDescriptor desc = (BoldFontDescriptor) descriptors.get(boldFont); + if (desc != null) { + getResourceManager().destroyFont(desc); + if (getResourceManager().find(desc) == null) { + descriptors.remove(boldFont); + validateHashMaps(); + } + return true; + + } + // if the image was not found, dispose of it for the caller + // RAP [if] unnecessary + // boldFont.dispose(); + return false; + } + + private LocalResourceManager getResourceManager() { + if (resources == null) + resources = new LocalResourceManager(JFaceResources.getResources()); + return resources; + } + + private void checkHashMaps() { + if (descriptors == null) + descriptors = new HashMap(); + } + + private void validateHashMaps() { + if (descriptors.size() == 0) + descriptors = null; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java new file mode 100644 index 000000000..99271046a --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java @@ -0,0 +1,913 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +//import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +// RAP [rh] Paint events missing +//import org.eclipse.swt.events.PaintEvent; +//import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +//RAP [rh] GC missing +//import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +//import org.eclipse.swt.graphics.RGB; +//import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +//import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +//import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.Widget; +//import org.eclipse.ui.forms.FormColors; +//import org.eclipse.ui.forms.HyperlinkGroup; +//import org.eclipse.ui.forms.IFormColors; +//import org.eclipse.ui.internal.forms.widgets.FormFonts; +//import org.eclipse.ui.internal.forms.widgets.FormUtil; + +/** + * The toolkit is responsible for creating SWT controls adapted to work in + * Eclipse forms. In addition to changing their presentation properties (fonts, + * colors etc.), various listeners are attached to make them behave correctly in + * the form context. + *

+ * In addition to being the control factory, the toolkit is also responsible for + * painting flat borders for select controls, managing hyperlink groups and + * control colors. + *

+ * The toolkit creates some of the most common controls used to populate Eclipse + * forms. Controls that must be created using their constructors, + * adapt() method is available to change its properties in the + * same way as with the supported toolkit controls. + *

+ * Typically, one toolkit object is created per workbench part (for example, an + * editor or a form wizard). The toolkit is disposed when the part is disposed. + * To conserve resources, it is possible to create one color object for the + * entire plug-in and share it between several toolkits. The plug-in is + * responsible for disposing the colors (disposing the toolkit that uses shared + * color object will not dispose the colors). + *

+ * FormToolkit is normally instantiated, but can also be subclassed if some of + * the methods needs to be modified. In those cases, super must + * be called to preserve normal behaviour. + * + * @since 1.0 + */ +public class FormToolkit { + public static final String KEY_DRAW_BORDER = "FormWidgetFactory.drawBorder"; //$NON-NLS-1$ + + public static final String TREE_BORDER = "treeBorder"; //$NON-NLS-1$ + + public static final String TEXT_BORDER = "textBorder"; //$NON-NLS-1$ + + private int borderStyle = SWT.NULL; + + private FormColors colors; + + private int orientation = Window.getDefaultOrientation(); + + // private KeyListener deleteListener; + // RAP [rh] Paint events missing +// private BorderPainter borderPainter; + + private BoldFontHolder boldFontHolder; + +// private HyperlinkGroup hyperlinkGroup; + + private boolean isDisposed = false; + + /* default */ + VisibilityHandler visibilityHandler; + + /* default */ + KeyboardHandler keyboardHandler; + + // RAP [rh] Paint events missing +// private class BorderPainter implements PaintListener { +// public void paintControl(PaintEvent event) { +// Composite composite = (Composite) event.widget; +// Control[] children = composite.getChildren(); +// for (int i = 0; i < children.length; i++) { +// Control c = children[i]; +// boolean inactiveBorder = false; +// boolean textBorder = false; +// if (!c.isVisible()) +// continue; +// /* +// * if (c.getEnabled() == false && !(c instanceof CCombo)) +// * continue; +// */ +// if (c instanceof Hyperlink) +// continue; +// Object flag = c.getData(KEY_DRAW_BORDER); +// if (flag != null) { +// if (flag.equals(Boolean.FALSE)) +// continue; +// if (flag.equals(TREE_BORDER)) +// inactiveBorder = true; +// else if (flag.equals(TEXT_BORDER)) +// textBorder = true; +// } +// if (getBorderStyle() == SWT.BORDER) { +// if (!inactiveBorder && !textBorder) { +// continue; +// } +// if (c instanceof Text || c instanceof Table +// || c instanceof Tree) +// continue; +// } +// if (!inactiveBorder +// && (c instanceof Text || c instanceof CCombo || textBorder)) { +// Rectangle b = c.getBounds(); +// GC gc = event.gc; +// gc.setForeground(c.getBackground()); +// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, +// b.height + 1); +// // gc.setForeground(getBorderStyle() == SWT.BORDER ? colors +// // .getBorderColor() : colors.getForeground()); +// gc.setForeground(colors.getBorderColor()); +// if (c instanceof CCombo) +// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, +// b.height + 1); +// else +// gc.drawRectangle(b.x - 1, b.y - 2, b.width + 1, +// b.height + 3); +// } else if (inactiveBorder || c instanceof Table +// || c instanceof Tree) { +// Rectangle b = c.getBounds(); +// GC gc = event.gc; +// gc.setForeground(colors.getBorderColor()); +// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, +// b.height + 1); +// } +// } +// } +// } + + private static class VisibilityHandler extends FocusAdapter { + public void focusGained(FocusEvent e) { + Widget w = e.widget; + if (w instanceof Control) { + FormUtil.ensureVisible((Control) w); + } + } + } + + private static class KeyboardHandler extends KeyAdapter { + public void keyPressed(KeyEvent e) { + Widget w = e.widget; + if (w instanceof Control) { + if (e.doit) + FormUtil.processKey(e.keyCode, (Control) w); + } + } + } + + private class BoldFontHolder { + private Font normalFont; + + private Font boldFont; + + public BoldFontHolder() { + } + + public Font getBoldFont(Font font) { + createBoldFont(font); + return boldFont; + } + + private void createBoldFont(Font font) { + if (normalFont == null || !normalFont.equals(font)) { + normalFont = font; + dispose(); + } + if (boldFont == null) { + boldFont = FormFonts.getInstance().getBoldFont(colors.getDisplay(), + normalFont); + } + } + + public void dispose() { + if (boldFont != null) { + FormFonts.getInstance().markFinished(boldFont); + boldFont = null; + } + } + } + + /** + * Creates a toolkit that is self-sufficient (will manage its own colors). + *

+ * Clients that call this method must call {@link #dispose()} when they + * are finished using the toolkit. + * + */ + public FormToolkit(Display display) { + this(new FormColors(display)); + } + + /** + * Creates a toolkit that will use the provided (shared) colors. The toolkit + * will dispose the colors if and only if they are not marked as + * shared via the markShared() method. + *

+ * Clients that call this method must call {@link #dispose()} when they + * are finished using the toolkit. + * + * @param colors + * the shared colors + */ + public FormToolkit(FormColors colors) { + this.colors = colors; + initialize(); + } + + /** + * Creates a button as a part of the form. + * + * @param parent + * the button parent + * @param text + * an optional text for the button (can be null) + * @param style + * the button style (for example, SWT.PUSH) + * @return the button widget + */ + public Button createButton(Composite parent, String text, int style) { + Button button = new Button(parent, style | SWT.FLAT | orientation); + if (text != null) + button.setText(text); + adapt(button, true, true); + return button; + } + + /** + * Creates the composite as a part of the form. + * + * @param parent + * the composite parent + * @return the composite widget + */ + public Composite createComposite(Composite parent) { + return createComposite(parent, SWT.NULL); + } + + /** + * Creates the composite as part of the form using the provided style. + * + * @param parent + * the composite parent + * @param style + * the composite style + * @return the composite widget + */ + public Composite createComposite(Composite parent, int style) { +// Composite composite = new LayoutComposite(parent, style | orientation); + Composite composite = new Composite(parent, style | orientation); + adapt(composite); + return composite; + } + + /** + * Creats the composite that can server as a separator between various parts + * of a form. Separator height should be controlled by setting the height + * hint on the layout data for the composite. + * + * @param parent + * the separator parent + * @return the separator widget + */ +// RAP [rh] createCompositeSeparator: currently no useful implementation possible, delete? + public Composite createCompositeSeparator(Composite parent) { + final Composite composite = new Composite(parent, orientation); +// RAP [rh] GC and paint events missing +// composite.addListener(SWT.Paint, new Listener() { +// public void handleEvent(Event e) { +// if (composite.isDisposed()) +// return; +// Rectangle bounds = composite.getBounds(); +// GC gc = e.gc; +// gc.setForeground(colors.getColor(IFormColors.SEPARATOR)); +// if (colors.getBackground() != null) +// gc.setBackground(colors.getBackground()); +// gc.fillGradientRectangle(0, 0, bounds.width, bounds.height, +// false); +// } +// }); +// if (parent instanceof Section) +// ((Section) parent).setSeparatorControl(composite); + return composite; + } + + /** + * Creates a label as a part of the form. + * + * @param parent + * the label parent + * @param text + * the label text + * @return the label widget + */ + public Label createLabel(Composite parent, String text) { + return createLabel(parent, text, SWT.NONE); + } + + /** + * Creates a label as a part of the form. + * + * @param parent + * the label parent + * @param text + * the label text + * @param style + * the label style + * @return the label widget + */ + public Label createLabel(Composite parent, String text, int style) { + Label label = new Label(parent, style | orientation); + if (text != null) + label.setText(text); + adapt(label, false, false); + return label; + } + + /** + * Creates a hyperlink as a part of the form. The hyperlink will be added to + * the hyperlink group that belongs to this toolkit. + * + * @param parent + * the hyperlink parent + * @param text + * the text of the hyperlink + * @param style + * the hyperlink style + * @return the hyperlink widget + */ +// public Hyperlink createHyperlink(Composite parent, String text, int style) { +// Hyperlink hyperlink = new Hyperlink(parent, style | orientation); +// if (text != null) +// hyperlink.setText(text); +// hyperlink.addFocusListener(visibilityHandler); +// hyperlink.addKeyListener(keyboardHandler); +// hyperlinkGroup.add(hyperlink); +// return hyperlink; +// } + + /** + * Creates an image hyperlink as a part of the form. The hyperlink will be + * added to the hyperlink group that belongs to this toolkit. + * + * @param parent + * the hyperlink parent + * @param style + * the hyperlink style + * @return the image hyperlink widget + */ +// public ImageHyperlink createImageHyperlink(Composite parent, int style) { +// ImageHyperlink hyperlink = new ImageHyperlink(parent, style +// | orientation); +// hyperlink.addFocusListener(visibilityHandler); +// hyperlink.addKeyListener(keyboardHandler); +// hyperlinkGroup.add(hyperlink); +// return hyperlink; +// } + + /** + * Creates a rich text as a part of the form. + * + * @param parent + * the rich text parent + * @param trackFocus + * if true, the toolkit will monitor focus + * transfers to ensure that the hyperlink in focus is visible in + * the form. + * @return the rich text widget + * @since 1.2 + */ +// public FormText createFormText(Composite parent, boolean trackFocus) { +// FormText engine = new FormText(parent, SWT.WRAP | orientation); +// engine.marginWidth = 1; +// engine.marginHeight = 0; +// engine.setHyperlinkSettings(getHyperlinkGroup()); +// adapt(engine, trackFocus, true); +// engine.setMenu(parent.getMenu()); +// return engine; +// } + + /** + * Adapts a control to be used in a form that is associated with this + * toolkit. This involves adjusting colors and optionally adding handlers to + * ensure focus tracking and keyboard management. + * + * @param control + * a control to adapt + * @param trackFocus + * if true, form will be scrolled horizontally + * and/or vertically if needed to ensure that the control is + * visible when it gains focus. Set it to false if + * the control is not capable of gaining focus. + * @param trackKeyboard + * if true, the control that is capable of + * gaining focus will be tracked for certain keys that are + * important to the underlying form (for example, PageUp, + * PageDown, ScrollUp, ScrollDown etc.). Set it to + * false if the control is not capable of gaining + * focus or these particular key event are already used by the + * control. + */ + public void adapt(Control control, boolean trackFocus, boolean trackKeyboard) { + control.setBackground(colors.getBackground()); + control.setForeground(colors.getForeground()); +// if (control instanceof ExpandableComposite) { +// ExpandableComposite ec = (ExpandableComposite) control; +// if (ec.toggle != null) { +// if (trackFocus) +// ec.toggle.addFocusListener(visibilityHandler); +// if (trackKeyboard) +// ec.toggle.addKeyListener(keyboardHandler); +// } +// if (ec.textLabel != null) { +// if (trackFocus) +// ec.textLabel.addFocusListener(visibilityHandler); +// if (trackKeyboard) +// ec.textLabel.addKeyListener(keyboardHandler); +// } +// return; +// } + if (trackFocus) + control.addFocusListener(visibilityHandler); + if (trackKeyboard) + control.addKeyListener(keyboardHandler); + } + + /** + * Adapts a composite to be used in a form associated with this toolkit. + * + * @param composite + * the composite to adapt + */ + public void adapt(Composite composite) { + composite.setBackground(colors.getBackground()); + composite.addMouseListener(new MouseAdapter() { + public void mouseDown(MouseEvent e) { + ((Control) e.widget).setFocus(); + } + }); + if (composite.getParent() != null) + composite.setMenu(composite.getParent().getMenu()); + } + + /** + * A helper method that ensures the provided control is visible when + * ScrolledComposite is somewhere in the parent chain. If scroll bars are + * visible and the control is clipped, the client of the scrolled composite + * will be scrolled to reveal the control. + * + * @param c + * the control to reveal + */ + public static void ensureVisible(Control c) { + FormUtil.ensureVisible(c); + } + + /** + * Creates a section as a part of the form. + * + * @param parent + * the section parent + * @param sectionStyle + * the section style + * @return the section widget + */ +// public Section createSection(Composite parent, int sectionStyle) { +// Section section = new Section(parent, orientation, sectionStyle); +// section.setMenu(parent.getMenu()); +// adapt(section, true, true); +// if (section.toggle != null) { +// section.toggle.setHoverDecorationColor(colors +// .getColor(IFormColors.TB_TOGGLE_HOVER)); +// section.toggle.setDecorationColor(colors +// .getColor(IFormColors.TB_TOGGLE)); +// } +// section.setFont(boldFontHolder.getBoldFont(parent.getFont())); +// if ((sectionStyle & Section.TITLE_BAR) != 0 +// || (sectionStyle & Section.SHORT_TITLE_BAR) != 0) { +// colors.initializeSectionToolBarColors(); +// section.setTitleBarBackground(colors.getColor(IFormColors.TB_BG)); +// section.setTitleBarBorderColor(colors +// .getColor(IFormColors.TB_BORDER)); +// } +// // call setTitleBarForeground regardless as it also sets the label color +// section.setTitleBarForeground(colors +// .getColor(IFormColors.TB_TOGGLE)); +// return section; +// } + + /** + * Creates an expandable composite as a part of the form. + * + * @param parent + * the expandable composite parent + * @param expansionStyle + * the expandable composite style + * @return the expandable composite widget + */ +// public ExpandableComposite createExpandableComposite(Composite parent, +// int expansionStyle) { +// ExpandableComposite ec = new ExpandableComposite(parent, orientation, +// expansionStyle); +// ec.setMenu(parent.getMenu()); +// adapt(ec, true, true); +// ec.setFont(boldFontHolder.getBoldFont(ec.getFont())); +// return ec; +// } + + /** + * Creates a separator label as a part of the form. + * + * @param parent + * the separator parent + * @param style + * the separator style + * @return the separator label + */ + public Label createSeparator(Composite parent, int style) { + Label label = new Label(parent, SWT.SEPARATOR | style | orientation); + label.setBackground(colors.getBackground()); + label.setForeground(colors.getBorderColor()); + return label; + } + + /** + * Creates a table as a part of the form. + * + * @param parent + * the table parent + * @param style + * the table style + * @return the table widget + */ + public Table createTable(Composite parent, int style) { + Table table = new Table(parent, style | borderStyle | orientation); + adapt(table, false, false); + // hookDeleteListener(table); + return table; + } + + /** + * Creates a text as a part of the form. + * + * @param parent + * the text parent + * @param value + * the text initial value + * @return the text widget + */ + public Text createText(Composite parent, String value) { + return createText(parent, value, SWT.SINGLE); + } + + /** + * Creates a text as a part of the form. + * + * @param parent + * the text parent + * @param value + * the text initial value + * @param style + * the text style + * @return the text widget + */ + public Text createText(Composite parent, String value, int style) { + Text text = new Text(parent, borderStyle | style | orientation); + if (value != null) + text.setText(value); + text.setForeground(colors.getForeground()); + text.setBackground(colors.getBackground()); + text.addFocusListener(visibilityHandler); + return text; + } + + /** + * Creates a tree widget as a part of the form. + * + * @param parent + * the tree parent + * @param style + * the tree style + * @return the tree widget + */ + public Tree createTree(Composite parent, int style) { + Tree tree = new Tree(parent, borderStyle | style | orientation); + adapt(tree, false, false); + // hookDeleteListener(tree); + return tree; + } + + /** + * Creates a scrolled form widget in the provided parent. If you do not + * require scrolling because there is already a scrolled composite up the + * parent chain, use 'createForm' instead. + * + * @param parent + * the scrolled form parent + * @return the form that can scroll itself + * @see #createForm + */ + public ScrolledComposite createScrolledForm(Composite parent) { + ScrolledComposite form = new ScrolledComposite(parent, SWT.V_SCROLL + | SWT.H_SCROLL | orientation); + form.setExpandHorizontal(true); + form.setExpandVertical(true); + form.setBackground(colors.getBackground()); + form.setForeground(colors.getColor(IFormColors.TITLE)); + form.setFont(JFaceResources.getHeaderFont()); + return form; + } + + /** + * Creates a form widget in the provided parent. Note that this widget does + * not scroll its content, so make sure there is a scrolled composite up the + * parent chain. If you require scrolling, use 'createScrolledForm' instead. + * + * @param parent + * the form parent + * @return the form that does not scroll + * @see #createScrolledForm + */ +// public Form createForm(Composite parent) { +// Form formContent = new Form(parent, orientation); +// formContent.setBackground(colors.getBackground()); +// formContent.setForeground(colors.getColor(IFormColors.TITLE)); +// formContent.setFont(JFaceResources.getHeaderFont()); +// return formContent; +// } + + /** + * Takes advantage of the gradients and other capabilities to decorate the + * form heading using colors computed based on the current skin and + * operating system. + * + * @param form + * the form to decorate + */ + +// public void decorateFormHeading(Form form) { +// Color top = colors.getColor(IFormColors.H_GRADIENT_END); +// Color bot = colors.getColor(IFormColors.H_GRADIENT_START); +// form.setTextBackground(new Color[] { top, bot }, new int[] { 100 }, +// true); +// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE1, colors +// .getColor(IFormColors.H_BOTTOM_KEYLINE1)); +// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE2, colors +// .getColor(IFormColors.H_BOTTOM_KEYLINE2)); +// form.setHeadColor(IFormColors.H_HOVER_LIGHT, colors +// .getColor(IFormColors.H_HOVER_LIGHT)); +// form.setHeadColor(IFormColors.H_HOVER_FULL, colors +// .getColor(IFormColors.H_HOVER_FULL)); +// form.setHeadColor(IFormColors.TB_TOGGLE, colors +// .getColor(IFormColors.TB_TOGGLE)); +// form.setHeadColor(IFormColors.TB_TOGGLE_HOVER, colors +// .getColor(IFormColors.TB_TOGGLE_HOVER)); +// form.setSeparatorVisible(true); +// } + + /** + * Creates a scrolled page book widget as a part of the form. + * + * @param parent + * the page book parent + * @param style + * the text style + * @return the scrolled page book widget + */ +// public ScrolledPageBook createPageBook(Composite parent, int style) { +// ScrolledPageBook book = new ScrolledPageBook(parent, style +// | orientation); +// adapt(book, true, true); +// book.setMenu(parent.getMenu()); +// return book; +// } + + /** + * Disposes the toolkit. + */ + public void dispose() { + if (isDisposed) { + return; + } + isDisposed = true; + if (colors.isShared() == false) { + colors.dispose(); + colors = null; + } + boldFontHolder.dispose(); + } + + /** + * Returns the hyperlink group that manages hyperlinks for this toolkit. + * + * @return the hyperlink group + */ +// public HyperlinkGroup getHyperlinkGroup() { +// return hyperlinkGroup; +// } + + /** + * Sets the background color for the entire toolkit. The method delegates + * the call to the FormColors object and also updates the hyperlink group so + * that hyperlinks and other objects are in sync. + * + * @param bg + * the new background color + */ + public void setBackground(Color bg) { +// hyperlinkGroup.setBackground(bg); + colors.setBackground(bg); + } + + /** + * Refreshes the hyperlink colors by loading from JFace settings. + */ +// public void refreshHyperlinkColors() { +// hyperlinkGroup.initializeDefaultForegrounds(colors.getDisplay()); +// } + +// RAP [rh] paintBordersFor not useful as no GC to actually paint borders +// /** +// * Paints flat borders for widgets created by this toolkit within the +// * provided parent. Borders will not be painted if the global border style +// * is SWT.BORDER (i.e. if native borders are used). Call this method during +// * creation of a form composite to get the borders of its children painted. +// * Care should be taken when selection layout margins. At least one pixel +// * pargin width and height must be chosen to allow the toolkit to paint the +// * border on the parent around the widgets. +// *

+// * Borders are painted for some controls that are selected by the toolkit by +// * default. If a control needs a border but is not on its list, it is +// * possible to force border in the following way: +// * +// *

+//	 *
+//	 *
+//	 *
+//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
+//	 *
+//	 *             or
+//	 *
+//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
+//	 *
+//	 *
+//	 *
+//	 * 
+// * +// * @param parent +// * the parent that owns the children for which the border needs +// * to be painted. +// */ +// public void paintBordersFor(Composite parent) { +// // if (borderStyle == SWT.BORDER) +// // return; +// if (borderPainter == null) +// borderPainter = new BorderPainter(); +// parent.addPaintListener(borderPainter); +// } + + /** + * Returns the colors used by this toolkit. + * + * @return the color object + */ + public FormColors getColors() { + return colors; + } + + /** + * Returns the border style used for various widgets created by this + * toolkit. The intent of the toolkit is to create controls with styles that + * yield a 'flat' appearance. On systems where the native borders are + * already flat, we set the style to SWT.BORDER and don't paint the borders + * ourselves. Otherwise, the style is set to SWT.NULL, and borders are + * painted by the toolkit. + * + * @return the global border style + */ + public int getBorderStyle() { + return borderStyle; + } + + /** + * Returns the margin required around the children whose border is being + * painted by the toolkit using {@link #paintBordersFor(Composite)}. Since + * the border is painted around the controls on the parent, a number of + * pixels needs to be reserved for this border. For windowing systems where + * the native border is used, this margin is 0. + * + * @return the margin in the parent when children have their border painted + */ + public int getBorderMargin() { + return getBorderStyle() == SWT.BORDER ? 0 : 2; + } + + /** + * Sets the border style to be used when creating widgets. The toolkit + * chooses the correct style based on the platform but this value can be + * changed using this method. + * + * @param style + * SWT.BORDER or SWT.NULL + * @see #getBorderStyle + */ + public void setBorderStyle(int style) { + this.borderStyle = style; + } + + /** + * A utility method that ensures that the control is visible in the scrolled + * composite. The prerequisite for this method is that the control has a + * class that extends ScrolledComposite somewhere in the parent chain. If + * the control is partially or fully clipped, the composite is scrolled to + * set by setting the origin to the control origin. + * + * @param c + * the control to make visible + * @param verticalOnly + * if true, the scrolled composite will be + * scrolled only vertically if needed. Otherwise, the scrolled + * composite origin will be set to the control origin. + */ + public static void setControlVisible(Control c, boolean verticalOnly) { + ScrolledComposite scomp = FormUtil.getScrolledComposite(c); + if (scomp == null) + return; + Point location = FormUtil.getControlLocation(scomp, c); + scomp.setOrigin(location); + } + + private void initialize() { + initializeBorderStyle(); +// hyperlinkGroup = new HyperlinkGroup(colors.getDisplay()); +// hyperlinkGroup.setBackground(colors.getBackground()); + visibilityHandler = new VisibilityHandler(); + keyboardHandler = new KeyboardHandler(); + boldFontHolder = new BoldFontHolder(); + } + +// RAP [rh] revise detection of border style: can't ask OS here + private void initializeBorderStyle() { +// String osname = System.getProperty("os.name"); //$NON-NLS-1$ +// String osversion = System.getProperty("os.version"); //$NON-NLS-1$ +// if (osname.startsWith("Windows") && "5.1".compareTo(osversion) <= 0) { //$NON-NLS-1$ //$NON-NLS-2$ +// // Skinned widgets used on newer Windows (e.g. XP (5.1), Vista +// // (6.0)) +// // Check for Windows Classic. If not used, set the style to BORDER +// RGB rgb = colors.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); +// if (rgb.red != 212 || rgb.green != 208 || rgb.blue != 200) +// borderStyle = SWT.BORDER; +// } else if (osname.startsWith("Mac")) //$NON-NLS-1$ +// borderStyle = SWT.BORDER; + + borderStyle = SWT.BORDER; + } + + /** + * Returns the orientation that all the widgets created by this toolkit will + * inherit, if set. Can be SWT.NULL, + * SWT.LEFT_TO_RIGHT and SWT.RIGHT_TO_LEFT. + * + * @return orientation style for this toolkit, or SWT.NULL if + * not set. The default orientation is inherited from the Window + * default orientation. + * @see org.eclipse.jface.window.Window#getDefaultOrientation() + */ + + public int getOrientation() { + return orientation; + } + + /** + * Sets the orientation that all the widgets created by this toolkit will + * inherit. Can be SWT.NULL, SWT.LEFT_TO_RIGHT + * and SWT.RIGHT_TO_LEFT. + * + * @param orientation + * style for this toolkit. + */ + + public void setOrientation(int orientation) { + this.orientation = orientation; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java new file mode 100644 index 000000000..76e3f1104 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java @@ -0,0 +1,522 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.MouseEvent; +//import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +//import org.eclipse.swt.graphics.Image; +//import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Layout; +//import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Text; +//import org.eclipse.ui.forms.widgets.ColumnLayout; +//import org.eclipse.ui.forms.widgets.Form; +//import org.eclipse.ui.forms.widgets.FormText; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ILayoutExtension; +// +//import com.ibm.icu.text.BreakIterator; + +public class FormUtil { + public static final String PLUGIN_ID = "org.eclipse.ui.forms"; //$NON-NLS-1$ + + static final int H_SCROLL_INCREMENT = 5; + + static final int V_SCROLL_INCREMENT = 64; + + public static final String DEBUG = PLUGIN_ID + "/debug"; //$NON-NLS-1$ + + public static final String DEBUG_TEXT = DEBUG + "/text"; //$NON-NLS-1$ + public static final String DEBUG_TEXTSIZE = DEBUG + "/textsize"; //$NON-NLS-1$ + + public static final String DEBUG_FOCUS = DEBUG + "/focus"; //$NON-NLS-1$ + + public static final String FOCUS_SCROLLING = "focusScrolling"; //$NON-NLS-1$ + + public static final String IGNORE_BODY = "__ignore_body__"; //$NON-NLS-1$ + + public static Text createText(Composite parent, String label, + FormToolkit factory) { + return createText(parent, label, factory, 1); + } + + public static Text createText(Composite parent, String label, + FormToolkit factory, int span) { + factory.createLabel(parent, label); + Text text = factory.createText(parent, ""); //$NON-NLS-1$ + int hfill = span == 1 ? GridData.FILL_HORIZONTAL + : GridData.HORIZONTAL_ALIGN_FILL; + GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); + gd.horizontalSpan = span; + text.setLayoutData(gd); + return text; + } + + public static Text createText(Composite parent, String label, + FormToolkit factory, int span, int style) { + Label l = factory.createLabel(parent, label); + if ((style & SWT.MULTI) != 0) { + GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); + l.setLayoutData(gd); + } + Text text = factory.createText(parent, "", style); //$NON-NLS-1$ + int hfill = span == 1 ? GridData.FILL_HORIZONTAL + : GridData.HORIZONTAL_ALIGN_FILL; + GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); + gd.horizontalSpan = span; + text.setLayoutData(gd); + return text; + } + + public static Text createText(Composite parent, FormToolkit factory, + int span) { + Text text = factory.createText(parent, ""); //$NON-NLS-1$ + int hfill = span == 1 ? GridData.FILL_HORIZONTAL + : GridData.HORIZONTAL_ALIGN_FILL; + GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); + gd.horizontalSpan = span; + text.setLayoutData(gd); + return text; + } + + public static int computeMinimumWidth(GC gc, String text) { +// BreakIterator wb = BreakIterator.getWordInstance(); +// wb.setText(text); +// int last = 0; +// +// int width = 0; +// +// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { +// String word = text.substring(last, loc); +// Point extent = gc.textExtent(word); +// width = Math.max(width, extent.x); +// last = loc; +// } +// String lastWord = text.substring(last); +// Point extent = gc.textExtent(lastWord); +// width = Math.max(width, extent.x); +// return width; + return 0; + } + + public static Point computeWrapSize(GC gc, String text, int wHint) { +// BreakIterator wb = BreakIterator.getWordInstance(); +// wb.setText(text); + FontMetrics fm = gc.getFontMetrics(); + int lineHeight = fm.getHeight(); + + int saved = 0; + int last = 0; + int height = lineHeight; + int maxWidth = 0; +// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { +// String word = text.substring(saved, loc); +// Point extent = gc.textExtent(word); +// if (extent.x > wHint) { +// // overflow +// saved = last; +// height += extent.y; +// // switch to current word so maxWidth will accommodate very long single words +// word = text.substring(last, loc); +// extent = gc.textExtent(word); +// } +// maxWidth = Math.max(maxWidth, extent.x); +// last = loc; +// } + /* + * Correct the height attribute in case it was calculated wrong due to wHint being less than maxWidth. + * The recursive call proved to be the only thing that worked in all cases. Some attempts can be made + * to estimate the height, but the algorithm needs to be run again to be sure. + */ + if (maxWidth > wHint) + return computeWrapSize(gc, text, maxWidth); + return new Point(maxWidth, height); + } + +// RAP [rh] paintWrapText unnecessary +// public static void paintWrapText(GC gc, String text, Rectangle bounds) { +// paintWrapText(gc, text, bounds, false); +// } + +// RAP [rh] paintWrapText unnecessary +// public static void paintWrapText(GC gc, String text, Rectangle bounds, +// boolean underline) { +// BreakIterator wb = BreakIterator.getWordInstance(); +// wb.setText(text); +// FontMetrics fm = gc.getFontMetrics(); +// int lineHeight = fm.getHeight(); +// int descent = fm.getDescent(); +// +// int saved = 0; +// int last = 0; +// int y = bounds.y; +// int width = bounds.width; +// +// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { +// String line = text.substring(saved, loc); +// Point extent = gc.textExtent(line); +// +// if (extent.x > width) { +// // overflow +// String prevLine = text.substring(saved, last); +// gc.drawText(prevLine, bounds.x, y, true); +// if (underline) { +// Point prevExtent = gc.textExtent(prevLine); +// int lineY = y + lineHeight - descent + 1; +// gc +// .drawLine(bounds.x, lineY, bounds.x + prevExtent.x, +// lineY); +// } +// +// saved = last; +// y += lineHeight; +// } +// last = loc; +// } +// // paint the last line +// String lastLine = text.substring(saved, last); +// gc.drawText(lastLine, bounds.x, y, true); +// if (underline) { +// int lineY = y + lineHeight - descent + 1; +// Point lastExtent = gc.textExtent(lastLine); +// gc.drawLine(bounds.x, lineY, bounds.x + lastExtent.x, lineY); +// } +// } + + public static ScrolledComposite getScrolledComposite(Control c) { + Composite parent = c.getParent(); + + while (parent != null) { + if (parent instanceof ScrolledComposite) { + return (ScrolledComposite) parent; + } + parent = parent.getParent(); + } + return null; + } + + public static void ensureVisible(Control c) { + ScrolledComposite scomp = getScrolledComposite(c); + if (scomp != null) { + Object data = scomp.getData(FOCUS_SCROLLING); + if (data == null || !data.equals(Boolean.FALSE)) + FormUtil.ensureVisible(scomp, c); + } + } + + public static void ensureVisible(ScrolledComposite scomp, Control control) { + // if the control is a FormText we do not need to scroll since it will + // ensure visibility of its segments as necessary +// if (control instanceof FormText) +// return; + Point controlSize = control.getSize(); + Point controlOrigin = getControlLocation(scomp, control); + ensureVisible(scomp, controlOrigin, controlSize); + } + + public static void ensureVisible(ScrolledComposite scomp, + Point controlOrigin, Point controlSize) { + Rectangle area = scomp.getClientArea(); + Point scompOrigin = scomp.getOrigin(); + + int x = scompOrigin.x; + int y = scompOrigin.y; + + // horizontal right, but only if the control is smaller + // than the client area + if (controlSize.x < area.width + && (controlOrigin.x + controlSize.x > scompOrigin.x + + area.width)) { + x = controlOrigin.x + controlSize.x - area.width; + } + // horizontal left - make sure the left edge of + // the control is showing + if (controlOrigin.x < x) { + if (controlSize.x < area.width) + x = controlOrigin.x + controlSize.x - area.width; + else + x = controlOrigin.x; + } + // vertical bottom + if (controlSize.y < area.height + && (controlOrigin.y + controlSize.y > scompOrigin.y + + area.height)) { + y = controlOrigin.y + controlSize.y - area.height; + } + // vertical top - make sure the top of + // the control is showing + if (controlOrigin.y < y) { + if (controlSize.y < area.height) + y = controlOrigin.y + controlSize.y - area.height; + else + y = controlOrigin.y; + } + + if (scompOrigin.x != x || scompOrigin.y != y) { + // scroll to reveal + scomp.setOrigin(x, y); + } + } + + public static void ensureVisible(ScrolledComposite scomp, Control control, + MouseEvent e) { + Point controlOrigin = getControlLocation(scomp, control); + int rX = controlOrigin.x + e.x; + int rY = controlOrigin.y + e.y; + Rectangle area = scomp.getClientArea(); + Point scompOrigin = scomp.getOrigin(); + + int x = scompOrigin.x; + int y = scompOrigin.y; + // System.out.println("Ensure: area="+area+", origin="+scompOrigin+", + // cloc="+controlOrigin+", csize="+controlSize+", x="+x+", y="+y); + + // horizontal right + if (rX > scompOrigin.x + area.width) { + x = rX - area.width; + } + // horizontal left + else if (rX < x) { + x = rX; + } + // vertical bottom + if (rY > scompOrigin.y + area.height) { + y = rY - area.height; + } + // vertical top + else if (rY < y) { + y = rY; + } + + if (scompOrigin.x != x || scompOrigin.y != y) { + // scroll to reveal + scomp.setOrigin(x, y); + } + } + + public static Point getControlLocation(ScrolledComposite scomp, + Control control) { + int x = 0; + int y = 0; + Control content = scomp.getContent(); + Control currentControl = control; + for (;;) { + if (currentControl == content) + break; + Point location = currentControl.getLocation(); + // if (location.x > 0) + // x += location.x; + // if (location.y > 0) + // y += location.y; + x += location.x; + y += location.y; + currentControl = currentControl.getParent(); + } + return new Point(x, y); + } + + static void scrollVertical(ScrolledComposite scomp, boolean up) { + scroll(scomp, 0, up ? -V_SCROLL_INCREMENT : V_SCROLL_INCREMENT); + } + + static void scrollHorizontal(ScrolledComposite scomp, boolean left) { + scroll(scomp, left ? -H_SCROLL_INCREMENT : H_SCROLL_INCREMENT, 0); + } + + static void scrollPage(ScrolledComposite scomp, boolean up) { + Rectangle clientArea = scomp.getClientArea(); + int increment = up ? -clientArea.height : clientArea.height; + scroll(scomp, 0, increment); + } + + static void scroll(ScrolledComposite scomp, int xoffset, int yoffset) { + Point origin = scomp.getOrigin(); + Point contentSize = scomp.getContent().getSize(); + int xorigin = origin.x + xoffset; + int yorigin = origin.y + yoffset; + xorigin = Math.max(xorigin, 0); + xorigin = Math.min(xorigin, contentSize.x - 1); + yorigin = Math.max(yorigin, 0); + yorigin = Math.min(yorigin, contentSize.y - 1); + scomp.setOrigin(xorigin, yorigin); + } + +// RAP [rh] FormUtil#updatePageIncrement: empty implementation + public static void updatePageIncrement(ScrolledComposite scomp) { +// ScrollBar vbar = scomp.getVerticalBar(); +// if (vbar != null) { +// Rectangle clientArea = scomp.getClientArea(); +// int increment = clientArea.height - 5; +// vbar.setPageIncrement(increment); +// } +// ScrollBar hbar = scomp.getHorizontalBar(); +// if (hbar != null) { +// Rectangle clientArea = scomp.getClientArea(); +// int increment = clientArea.width - 5; +// hbar.setPageIncrement(increment); +// } + } + + public static void processKey(int keyCode, Control c) { + if (c.isDisposed()) { + return; + } + ScrolledComposite scomp = FormUtil.getScrolledComposite(c); + if (scomp != null) { + if (c instanceof Combo) + return; + switch (keyCode) { + case SWT.ARROW_DOWN: + if (scomp.getData("novarrows") == null) //$NON-NLS-1$ + FormUtil.scrollVertical(scomp, false); + break; + case SWT.ARROW_UP: + if (scomp.getData("novarrows") == null) //$NON-NLS-1$ + FormUtil.scrollVertical(scomp, true); + break; + case SWT.ARROW_LEFT: + FormUtil.scrollHorizontal(scomp, true); + break; + case SWT.ARROW_RIGHT: + FormUtil.scrollHorizontal(scomp, false); + break; + case SWT.PAGE_UP: + FormUtil.scrollPage(scomp, true); + break; + case SWT.PAGE_DOWN: + FormUtil.scrollPage(scomp, false); + break; + } + } + } + + public static boolean isWrapControl(Control c) { + if ((c.getStyle() & SWT.WRAP) != 0) + return true; + if (c instanceof Composite) { + return false; +// return ((Composite) c).getLayout() instanceof ILayoutExtension; + } + return false; + } + + public static int getWidthHint(int wHint, Control c) { + boolean wrap = isWrapControl(c); + return wrap ? wHint : SWT.DEFAULT; + } + + public static int getHeightHint(int hHint, Control c) { + if (c instanceof Composite) { + Layout layout = ((Composite) c).getLayout(); +// if (layout instanceof ColumnLayout) +// return hHint; + } + return SWT.DEFAULT; + } + + public static int computeMinimumWidth(Control c, boolean changed) { + if (c instanceof Composite) { + Layout layout = ((Composite) c).getLayout(); +// if (layout instanceof ILayoutExtension) +// return ((ILayoutExtension) layout).computeMinimumWidth( +// (Composite) c, changed); + } + return c.computeSize(FormUtil.getWidthHint(5, c), SWT.DEFAULT, changed).x; + } + + public static int computeMaximumWidth(Control c, boolean changed) { + if (c instanceof Composite) { + Layout layout = ((Composite) c).getLayout(); +// if (layout instanceof ILayoutExtension) +// return ((ILayoutExtension) layout).computeMaximumWidth( +// (Composite) c, changed); + } + return c.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed).x; + } + +// public static Form getForm(Control c) { +// Composite parent = c.getParent(); +// while (parent != null) { +// if (parent instanceof Form) { +// return (Form) parent; +// } +// parent = parent.getParent(); +// } +// return null; +// } + +// RAP [rh] FormUtil#createAlphaMashImage unnecessary +// public static Image createAlphaMashImage(Device device, Image srcImage) { +// Rectangle bounds = srcImage.getBounds(); +// int alpha = 0; +// int calpha = 0; +// ImageData data = srcImage.getImageData(); +// // Create a new image with alpha values alternating +// // between fully transparent (0) and fully opaque (255). +// // This image will show the background through the +// // transparent pixels. +// for (int i = 0; i < bounds.height; i++) { +// // scan line +// alpha = calpha; +// for (int j = 0; j < bounds.width; j++) { +// // column +// data.setAlpha(j, i, alpha); +// alpha = alpha == 255 ? 0 : 255; +// } +// calpha = calpha == 255 ? 0 : 255; +// } +// return new Image(device, data); +// } + + public static boolean mnemonicMatch(String text, char key) { + char mnemonic = findMnemonic(text); + if (mnemonic == '\0') + return false; + return Character.toUpperCase(key) == Character.toUpperCase(mnemonic); + } + + private static char findMnemonic(String string) { + int index = 0; + int length = string.length(); + do { + while (index < length && string.charAt(index) != '&') + index++; + if (++index >= length) + return '\0'; + if (string.charAt(index) != '&') + return string.charAt(index); + index++; + } while (index < length); + return '\0'; + } + + public static void setFocusScrollingEnabled(Control c, boolean enabled) { + ScrolledComposite scomp = null; + + if (c instanceof ScrolledComposite) + scomp = (ScrolledComposite)c; + else + scomp = getScrolledComposite(c); + if (scomp!=null) + scomp.setData(FormUtil.FOCUS_SCROLLING, enabled?null:Boolean.FALSE); + } + + // RAP [rh] FormUtil#setAntialias unnecessary +// public static void setAntialias(GC gc, int style) { +// if (!gc.getAdvanced()) { +// gc.setAdvanced(true); +// if (!gc.getAdvanced()) +// return; +// } +// gc.setAntialias(style); +// } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java new file mode 100644 index 000000000..cf0e5d35e --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java @@ -0,0 +1,102 @@ +package org.argeo.cms.ui.eclipse.forms; + +/** + * A place to hold all the color constants used in the forms package. + * + * @since 1.0 + */ + +public interface IFormColors { + /** + * A prefix for all the keys. + */ + String PREFIX = "org.eclipse.ui.forms."; //$NON-NLS-1$ + /** + * Key for the form title foreground color. + */ + String TITLE = PREFIX + "TITLE"; //$NON-NLS-1$ + + /** + * A prefix for the header color constants. + */ + String H_PREFIX = PREFIX + "H_"; //$NON-NLS-1$ + /* + * A prefix for the section title bar color constants. + */ + String TB_PREFIX = PREFIX + "TB_"; //$NON-NLS-1$ + /** + * Key for the form header background gradient ending color. + */ + String H_GRADIENT_END = H_PREFIX + "GRADIENT_END"; //$NON-NLS-1$ + + /** + * Key for the form header background gradient starting color. + * + */ + String H_GRADIENT_START = H_PREFIX + "GRADIENT_START"; //$NON-NLS-1$ + /** + * Key for the form header bottom keyline 1 color. + * + */ + String H_BOTTOM_KEYLINE1 = H_PREFIX + "BOTTOM_KEYLINE1"; //$NON-NLS-1$ + /** + * Key for the form header bottom keyline 2 color. + * + */ + String H_BOTTOM_KEYLINE2 = H_PREFIX + "BOTTOM_KEYLINE2"; //$NON-NLS-1$ + /** + * Key for the form header light hover color. + * + */ + String H_HOVER_LIGHT = H_PREFIX + "H_HOVER_LIGHT"; //$NON-NLS-1$ + /** + * Key for the form header full hover color. + * + */ + String H_HOVER_FULL = H_PREFIX + "H_HOVER_FULL"; //$NON-NLS-1$ + + /** + * Key for the tree/table border color. + */ + String BORDER = PREFIX + "BORDER"; //$NON-NLS-1$ + + /** + * Key for the section separator color. + */ + String SEPARATOR = PREFIX + "SEPARATOR"; //$NON-NLS-1$ + + /** + * Key for the section title bar background. + */ + String TB_BG = TB_PREFIX + "BG"; //$NON-NLS-1$ + + /** + * Key for the section title bar foreground. + */ + String TB_FG = TB_PREFIX + "FG"; //$NON-NLS-1$ + + /** + * Key for the section title bar gradient. + * @deprecated Since 3.3, this color is not used any more. The + * tool bar gradient is created starting from {@link #TB_BG} to + * the section background color. + */ + String TB_GBG = TB_BG; + + /** + * Key for the section title bar border. + */ + String TB_BORDER = TB_PREFIX + "BORDER"; //$NON-NLS-1$ + + /** + * Key for the section toggle color. Since 3.1, this color is used for all + * section styles. + */ + String TB_TOGGLE = TB_PREFIX + "TOGGLE"; //$NON-NLS-1$ + + /** + * Key for the section toggle hover color. + * + */ + String TB_TOGGLE_HOVER = TB_PREFIX + "TOGGLE_HOVER"; //$NON-NLS-1$ +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java new file mode 100644 index 000000000..954cc0372 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java @@ -0,0 +1,108 @@ +package org.argeo.cms.ui.eclipse.forms; + +/** + * Classes that implement this interface can be added to the managed form and + * take part in the form life cycle. The part is initialized with the form and + * will be asked to accept focus. The part can receive form input and can elect + * to do something according to it (for example, select an object that matches + * the input). + *

+ * The form part has two 'out of sync' states in respect to the model(s) that + * feed the form: dirty and stale. When a part is dirty, it + * means that the user interacted with it and now its widgets contain state that + * is newer than the model. In order to sync up with the model, 'commit' needs + * to be called. In contrast, the model can change 'under' the form (as a result + * of some actions outside the form), resulting in data in the model being + * 'newer' than the content presented in the form. A 'stale' form part is + * brought in sync with the model by calling 'refresh'. The part is responsible + * for notifying the form when one of these states change in the part. The form + * reserves the right to handle this notification in the most appropriate way + * for the situation (for example, if the form is in a page of the multi-page + * editor, it may do nothing for stale parts if the page is currently not + * showing). + *

+ * When the form is disposed, each registered part is disposed as well. Parts + * are responsible for releasing any system resources they created and for + * removing themselves as listeners from all event providers. + * + * @see IManagedForm + * @since 1.0 + * + */ +public interface IFormPart { + /** + * Initializes the part. + * + * @param form + * the managed form that manages the part + */ + void initialize(IManagedForm form); + + /** + * Disposes the part allowing it to release allocated resources. + */ + void dispose(); + + /** + * Returns true if the part has been modified with respect to the data + * loaded from the model. + * + * @return true if the part has been modified with respect to the data + * loaded from the model + */ + boolean isDirty(); + + /** + * If part is displaying information loaded from a model, this method + * instructs it to commit the new (modified) data back into the model. + * + * @param onSave + * indicates if commit is called during 'save' operation or for + * some other reason (for example, if form is contained in a + * wizard or a multi-page editor and the user is about to leave + * the page). + */ + void commit(boolean onSave); + + /** + * Notifies the part that an object has been set as overall form's input. + * The part can elect to react by revealing or selecting the object, or do + * nothing if not applicable. + * + * @return true if the part has selected and revealed the + * input object, false otherwise. + */ + boolean setFormInput(Object input); + + /** + * Instructs form part to transfer focus to the widget that should has focus + * in that part. The method can do nothing (if it has no widgets capable of + * accepting focus). + */ + void setFocus(); + + /** + * Tests whether the form part is stale and needs refreshing. Parts can + * receive notification from models that will make their content stale, but + * may need to delay refreshing to improve performance (for example, there + * is no need to immediately refresh a part on a form that is current on a + * hidden page). + *

+ * It is important to differentiate 'stale' and 'dirty' states. Part is + * 'dirty' if user interacted with its editable widgets and changed the + * values. In contrast, part is 'stale' when the data it presents in the + * widgets has been changed in the model without direct user interaction. + * + * @return true if the part needs refreshing, + * false otherwise. + */ + boolean isStale(); + + /** + * Refreshes the part completely from the information freshly obtained from + * the model. The method will not be called if the part is not stale. + * Otherwise, the part is responsible for clearing the 'stale' flag after + * refreshing itself. + */ + void refresh(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java new file mode 100644 index 000000000..490d3a303 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java @@ -0,0 +1,175 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.custom.ScrolledComposite; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ScrolledForm; + +/** + * Managed form wraps a form widget and adds life cycle methods for form parts. + * A form part is a portion of the form that participates in form life cycle + * events. + *

+ * There is no 1/1 mapping between widgets and form parts. A widget like Section + * can be a part by itself, but a number of widgets can gather around one form + * part. + *

+ * This interface should not be extended or implemented. New form instances + * should be created using ManagedForm. + * + * @see ManagedForm + * @since 1.0 + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface IManagedForm { + /** + * Initializes the form by looping through the managed parts and + * initializing them. Has no effect if already called once. + */ + public void initialize(); + + /** + * Returns the toolkit used by this form. + * + * @return the toolkit + */ + public FormToolkit getToolkit(); + + /** + * Returns the form widget managed by this form. + * + * @return the form widget + */ + public ScrolledComposite getForm(); + + /** + * Reflows the form as a result of the layout change. + * + * @param changed + * if true, discard cached layout information + */ + public void reflow(boolean changed); + + /** + * A part can use this method to notify other parts that implement + * IPartSelectionListener about selection changes. + * + * @param part + * the part that broadcasts the selection + * @param selection + * the selection in the part + */ + public void fireSelectionChanged(IFormPart part, ISelection selection); + + /** + * Returns all the parts currently managed by this form. + * + * @return the managed parts + */ + IFormPart[] getParts(); + + /** + * Adds the new part to the form. + * + * @param part + * the part to add + */ + void addPart(IFormPart part); + + /** + * Removes the part from the form. + * + * @param part + * the part to remove + */ + void removePart(IFormPart part); + + /** + * Sets the input of this page to the provided object. + * + * @param input + * the new page input + * @return true if the form contains this object, + * false otherwise. + */ + boolean setInput(Object input); + + /** + * Returns the current page input. + * + * @return page input object or null if not applicable. + */ + Object getInput(); + + /** + * Tests if form is dirty. A managed form is dirty if at least one managed + * part is dirty. + * + * @return true if at least one managed part is dirty, + * false otherwise. + */ + boolean isDirty(); + + /** + * Notifies the form that the dirty state of one of its parts has changed. + * The global dirty state of the form can be obtained by calling 'isDirty'. + * + * @see #isDirty + */ + void dirtyStateChanged(); + + /** + * Commits the dirty form. All pending changes in the widgets are flushed + * into the model. + * + * @param onSave + */ + void commit(boolean onSave); + + /** + * Tests if form is stale. A managed form is stale if at least one managed + * part is stale. This can happen when the underlying model changes, + * resulting in the presentation of the part being out of sync with the + * model and needing refreshing. + * + * @return true if the form is stale, false + * otherwise. + */ + boolean isStale(); + + /** + * Notifies the form that the stale state of one of its parts has changed. + * The global stale state of the form can be obtained by calling 'isStale'. + */ + void staleStateChanged(); + + /** + * Refreshes the form by refreshing every part that is stale. + */ + void refresh(); + + /** + * Sets the container that owns this form. Depending on the context, the + * container may be wizard, editor page, editor etc. + * + * @param container + * the container of this form + */ + void setContainer(Object container); + + /** + * Returns the container of this form. + * + * @return the form container + */ + Object getContainer(); + + /** + * Returns the message manager that will keep track of messages in this + * form. + * + * @return the message manager instance + */ +// IMessageManager getMessageManager(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java new file mode 100644 index 000000000..0f557d41f --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java @@ -0,0 +1,23 @@ +package org.argeo.cms.ui.eclipse.forms; + +import org.eclipse.jface.viewers.ISelection; + +/** + * Form parts can implement this interface if they want to be + * notified when another part on the same form changes selection + * state. + * + * @see IFormPart + * @since 1.0 + */ +public interface IPartSelectionListener { + /** + * Called when the provided part has changed selection state. + * + * @param part + * the selection source + * @param selection + * the new selection + */ + public void selectionChanged(IFormPart part, ISelection selection); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java new file mode 100644 index 000000000..4140465a1 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java @@ -0,0 +1,323 @@ +package org.argeo.cms.ui.eclipse.forms; + +import java.util.Vector; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.widgets.Composite; +//import org.eclipse.ui.forms.widgets.FormToolkit; +//import org.eclipse.ui.forms.widgets.ScrolledForm; + +/** + * Managed form wraps a form widget and adds life cycle methods for form parts. + * A form part is a portion of the form that participates in form life cycle + * events. + *

+ * There is requirement for 1/1 mapping between widgets and form parts. A widget + * like Section can be a part by itself, but a number of widgets can join around + * one form part. + *

+ * Note to developers: this class is left public to allow its use beyond the + * original intention (inside a multi-page editor's page). You should limit the + * use of this class to make new instances inside a form container (wizard page, + * dialog etc.). Clients that need access to the class should not do it + * directly. Instead, they should do it through IManagedForm interface as much + * as possible. + * + * @since 1.0 + */ +public class ManagedForm implements IManagedForm { + private Object input; + + private ScrolledComposite form; + + private FormToolkit toolkit; + + private Object container; + + private boolean ownsToolkit; + + private boolean initialized; + + private Vector parts = new Vector(); + + /** + * Creates a managed form in the provided parent. Form toolkit and widget + * will be created and owned by this object. + * + * @param parent + * the parent widget + */ + public ManagedForm(Composite parent) { + toolkit = new FormToolkit(parent.getDisplay()); + ownsToolkit = true; + form = toolkit.createScrolledForm(parent); + + } + + /** + * Creates a managed form that will use the provided toolkit and + * + * @param toolkit + * @param form + */ + public ManagedForm(FormToolkit toolkit, ScrolledComposite form) { + this.form = form; + this.toolkit = toolkit; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#addPart(org.eclipse.ui.forms.IFormPart) + */ + public void addPart(IFormPart part) { + parts.add(part); + part.initialize(this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#removePart(org.eclipse.ui.forms.IFormPart) + */ + public void removePart(IFormPart part) { + parts.remove(part); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getParts() + */ + public IFormPart[] getParts() { + return (IFormPart[]) parts.toArray(new IFormPart[parts.size()]); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getToolkit() + */ + public FormToolkit getToolkit() { + return toolkit; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getForm() + */ + public ScrolledComposite getForm() { + return form; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#reflow(boolean) + */ + public void reflow(boolean changed) { +// form.reflow(changed); + } + + /** + * A part can use this method to notify other parts that implement + * IPartSelectionListener about selection changes. + * + * @param part + * the part that broadcasts the selection + * @param selection + * the selection in the part + * @see IPartSelectionListener + */ + public void fireSelectionChanged(IFormPart part, ISelection selection) { + for (int i = 0; i < parts.size(); i++) { + IFormPart cpart = (IFormPart) parts.get(i); + if (part.equals(cpart)) + continue; +// if (cpart instanceof IPartSelectionListener) { +// ((IPartSelectionListener) cpart).selectionChanged(part, +// selection); +// } + } + } + + /** + * Initializes the form by looping through the managed parts and + * initializing them. Has no effect if already called once. + */ + public void initialize() { + if (initialized) + return; + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + part.initialize(this); + } + initialized = true; + } + + /** + * Disposes all the parts in this form. + */ + public void dispose() { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + part.dispose(); + } + if (ownsToolkit) { + toolkit.dispose(); + } + } + + /** + * Refreshes the form by refreshes all the stale parts. Since 3.1, this + * method is performed on a UI thread when called from another thread so it + * is not needed to wrap the call in Display.syncExec or + * asyncExec. + */ + public void refresh() { + Thread t = Thread.currentThread(); + Thread dt = toolkit.getColors().getDisplay().getThread(); + if (t.equals(dt)) + doRefresh(); + else { + toolkit.getColors().getDisplay().asyncExec(new Runnable() { + public void run() { + doRefresh(); + } + }); + } + } + + private void doRefresh() { + int nrefreshed = 0; + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isStale()) { + part.refresh(); + nrefreshed++; + } + } +// if (nrefreshed > 0) +// form.reflow(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#commit(boolean) + */ + public void commit(boolean onSave) { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isDirty()) + part.commit(onSave); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#setInput(java.lang.Object) + */ + public boolean setInput(Object input) { + boolean pageResult = false; + + this.input = input; + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + boolean result = part.setFormInput(input); + if (result) + pageResult = true; + } + return pageResult; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getInput() + */ + public Object getInput() { + return input; + } + + /** + * Transfers the focus to the first form part. + */ + public void setFocus() { + if (parts.size() > 0) { + IFormPart part = (IFormPart) parts.get(0); + part.setFocus(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#isDirty() + */ + public boolean isDirty() { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isDirty()) + return true; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#isStale() + */ + public boolean isStale() { + for (int i = 0; i < parts.size(); i++) { + IFormPart part = (IFormPart) parts.get(i); + if (part.isStale()) + return true; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#dirtyStateChanged() + */ + public void dirtyStateChanged() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#staleStateChanged() + */ + public void staleStateChanged() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#getContainer() + */ + public Object getContainer() { + return container; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.forms.IManagedForm#setContainer(java.lang.Object) + */ + public void setContainer(Object container) { + this.container = container; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.forms.IManagedForm#getMessageManager() + */ +// public IMessageManager getMessageManager() { +// return form.getMessageManager(); +// } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java new file mode 100644 index 000000000..484dae842 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java @@ -0,0 +1,85 @@ +package org.argeo.cms.ui.eclipse.forms.editor; + +import org.argeo.cms.ui.eclipse.forms.FormToolkit; +import org.eclipse.jface.dialogs.IPageChangeProvider; + +/** + * This class forms a base of multi-page form editors that typically use one or + * more pages with forms and one page for raw source of the editor input. + *

+ * Pages are added 'lazily' i.e. adding a page reserves a tab for it but does + * not cause the page control to be created. Page control is created when an + * attempt is made to select the page in question. This allows editors with + * several tabs and complex pages to open quickly. + *

+ * Subclasses should extend this class and implement addPages + * method. One of the two addPage methods should be called to + * contribute pages to the editor. One adds complete (standalone) editors as + * nested tabs. These editors will be created right away and will be hooked so + * that key bindings, selection service etc. is compatible with the one for the + * standalone case. The other method adds classes that implement + * IFormPage interface. These pages will be created lazily and + * they will share the common key binding and selection service. Since 3.1, + * FormEditor is a page change provider. It allows listeners to attach to it and + * get notified when pages are changed. This new API in JFace allows dynamic + * help to update on page changes. + * + * @since 1.0 + */ +// RAP [if] As RAP is still using workbench 3.4, the implementation of +// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code +// with the adoption of workbench > 3.5 +//public abstract class FormEditor extends MultiPageEditorPart { +public abstract class FormEditor implements + IPageChangeProvider { + private FormToolkit formToolkit; + + +public FormToolkit getToolkit() { + return formToolkit; + } + +public void editorDirtyStateChanged() { + +} + +public FormPage getActivePageInstance() { + return null; +} + + // RAP [if] As RAP is still using workbench 3.4, the implementation of +// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code +// with the adoption of workbench > 3.5 +// private ListenerList pageListeners = new ListenerList(); +// +// /* +// * (non-Javadoc) +// * +// * @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) +// */ +// public void addPageChangedListener(IPageChangedListener listener) { +// pageListeners.add(listener); +// } +// +// /* +// * (non-Javadoc) +// * +// * @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) +// */ +// public void removePageChangedListener(IPageChangedListener listener) { +// pageListeners.remove(listener); +// } +// +// private void firePageChanged(final PageChangedEvent event) { +// Object[] listeners = pageListeners.getListeners(); +// for (int i = 0; i < listeners.length; ++i) { +// final IPageChangedListener l = (IPageChangedListener) listeners[i]; +// SafeRunnable.run(new SafeRunnable() { +// public void run() { +// l.pageChanged(event); +// } +// }); +// } +// } +// RAPEND [if] +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java new file mode 100644 index 000000000..a788412db --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java @@ -0,0 +1,276 @@ +package org.argeo.cms.ui.eclipse.forms.editor; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.argeo.cms.ui.eclipse.forms.ManagedForm; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +/** + * A base class that all pages that should be added to FormEditor must subclass. + * Form page has an instance of PageForm that extends managed form. Subclasses + * should override method 'createFormContent(ManagedForm)' to fill the form with + * content. Note that page itself can be loaded lazily (on first open). + * Consequently, the call to create the form content can come after the editor + * has been opened for a while (in fact, it is possible to open and close the + * editor and never create the form because no attempt has been made to show the + * page). + * + * @since 1.0 + */ +public class FormPage implements IFormPage { + private FormEditor editor; + private PageForm mform; + private int index; + private String id; + + private String partName; + + + + public void setPartName(String partName) { + this.partName = partName; + } + private static class PageForm extends ManagedForm { + public PageForm(FormPage page, ScrolledComposite form) { + super(page.getEditor().getToolkit(), form); + setContainer(page); + } + + public FormPage getPage() { + return (FormPage)getContainer(); + } + public void dirtyStateChanged() { + getPage().getEditor().editorDirtyStateChanged(); + } + public void staleStateChanged() { + if (getPage().isActive()) + refresh(); + } + } + /** + * A constructor that creates the page and initializes it with the editor. + * + * @param editor + * the parent editor + * @param id + * the unique identifier + * @param title + * the page title + */ + public FormPage(FormEditor editor, String id, String title) { + this(id, title); + initialize(editor); + } + /** + * The constructor. The parent editor need to be passed in the + * initialize method if this constructor is used. + * + * @param id + * a unique page identifier + * @param title + * a user-friendly page title + */ + public FormPage(String id, String title) { + this.id = id; + setPartName(title); + } + /** + * Initializes the form page. + * + * @see IEditorPart#init + */ +// public void init(IEditorSite site, IEditorInput input) { +// setSite(site); +// setInput(input); +// } + /** + * Primes the form page with the parent editor instance. + * + * @param editor + * the parent editor + */ + public void initialize(FormEditor editor) { + this.editor = editor; + } + /** + * Returns the parent editor. + * + * @return parent editor instance + */ + public FormEditor getEditor() { + return editor; + } + /** + * Returns the managed form owned by this page. + * + * @return the managed form + */ + public IManagedForm getManagedForm() { + return mform; + } + /** + * Implements the required method by refreshing the form when set active. + * Subclasses must call super when overriding this method. + */ + public void setActive(boolean active) { + if (active) { + // We are switching to this page - refresh it + // if needed. + if (mform != null) + mform.refresh(); + } + } + /** + * Tests if the page is active by asking the parent editor if this page is + * the currently active page. + * + * @return true if the page is currently active, + * false otherwise. + */ + public boolean isActive() { + return this.equals(editor.getActivePageInstance()); + } + /** + * Creates the part control by creating the managed form using the parent + * editor's toolkit. Subclasses should override + * createFormContent(IManagedForm) to populate the form with + * content. + * + * @param parent + * the page parent composite + */ + public void createPartControl(Composite parent) { + ScrolledComposite form = editor.getToolkit().createScrolledForm(parent); + mform = new PageForm(this, form); + BusyIndicator.showWhile(parent.getDisplay(), new Runnable() { + public void run() { + createFormContent(mform); + } + }); + } + /** + * Subclasses should override this method to create content in the form + * hosted in this page. + * + * @param managedForm + * the form hosted in this page. + */ + protected void createFormContent(IManagedForm managedForm) { + } + /** + * Returns the form page control. + * + * @return managed form's control + */ + public Control getPartControl() { + return mform != null ? mform.getForm() : null; + } + /** + * Disposes the managed form. + */ + public void dispose() { + if (mform != null) + mform.dispose(); + } + /** + * Returns the unique identifier that can be used to reference this page. + * + * @return the unique page identifier + */ + public String getId() { + return id; + } + /** + * Returns null- form page has no title image. Subclasses + * may override. + * + * @return null + */ + public Image getTitleImage() { + return null; + } + /** + * Sets the focus by delegating to the managed form. + */ + public void setFocus() { + if (mform != null) + mform.setFocus(); + } + /** + * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) + */ +// public void doSave(IProgressMonitor monitor) { +// if (mform != null) +// mform.commit(true); +// } + /** + * @see org.eclipse.ui.ISaveablePart#doSaveAs() + */ + public void doSaveAs() { + } + /** + * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() + */ + public boolean isSaveAsAllowed() { + return false; + } + /** + * Implemented by testing if the managed form is dirty. + * + * @return true if the managed form is dirty, + * false otherwise. + * + * @see org.eclipse.ui.ISaveablePart#isDirty() + */ + public boolean isDirty() { + return mform != null ? mform.isDirty() : false; + } + /** + * Preserves the page index. + * + * @param index + * the assigned page index + */ + public void setIndex(int index) { + this.index = index; + } + /** + * Returns the saved page index. + * + * @return the page index + */ + public int getIndex() { + return index; + } + /** + * Form pages are not editors. + * + * @return false + */ + public boolean isEditor() { + return false; + } + /** + * Attempts to select and reveal the given object by passing the request to + * the managed form. + * + * @param object + * the object to select and reveal in the page if possible. + * @return true if the page has been successfully selected + * and revealed by one of the managed form parts, false + * otherwise. + */ + public boolean selectReveal(Object object) { + if (mform != null) + return mform.setInput(object); + return false; + } + /** + * By default, editor will be allowed to flip the page. + * @return true + */ + public boolean canLeaveThePage() { + return true; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java new file mode 100644 index 000000000..eb08cb59d --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java @@ -0,0 +1,119 @@ +package org.argeo.cms.ui.eclipse.forms.editor; +import org.argeo.cms.ui.eclipse.forms.IManagedForm; +import org.eclipse.swt.widgets.Control; +/** + * Interface that all GUI pages need to implement in order + * to be added to FormEditor part. The interface makes + * several assumptions: + *

+ *

Existing editors can be wrapped by implementing + * this interface. In this case, 'isEditor' should return true. + * A common editor to wrap in TextEditor that is + * often added to show the raw source code of the file open into + * the multi-page editor. + * + * @since 1.0 + */ +public interface IFormPage { + /** + * @param editor + * the form editor that this page belongs to + */ + void initialize(FormEditor editor); + /** + * Returns the editor this page belongs to. + * + * @return the form editor + */ + FormEditor getEditor(); + /** + * Returns the managed form of this page, unless this is a source page. + * + * @return the managed form or null if this is a source page. + */ + IManagedForm getManagedForm(); + /** + * Indicates whether the page has become the active in the editor. Classes + * that implement this interface may use this method to commit the page (on + * false) or lazily create and/or populate the content on + * true. + * + * @param active + * true if page should be visible, false + * otherwise. + */ + void setActive(boolean active); + /** + * Returns true if page is currently active, false if not. + * + * @return true for active page. + */ + boolean isActive(); + /** + * Tests if the content of the page is in a state that allows the + * editor to flip to another page. Typically, pages that contain + * raw source with syntax errors should not allow editors to + * leave them until errors are corrected. + * @return true if the editor can flip to another page, + * false otherwise. + */ + boolean canLeaveThePage(); + /** + * Returns the control associated with this page. + * + * @return the control of this page if created or null if the + * page has not been shown yet. + */ + Control getPartControl(); + /** + * Page must have a unique id that can be used to show it without knowing + * its relative position in the editor. + * + * @return the unique page identifier + */ + String getId(); + /** + * Returns the position of the page in the editor. + * + * @return the zero-based index of the page in the editor. + */ + int getIndex(); + /** + * Sets the position of the page in the editor. + * + * @param index + * the zero-based index of the page in the editor. + */ + void setIndex(int index); + /** + * Tests whether this page wraps a complete editor that + * can be registered on its own, or represents a page + * that cannot exist outside the multi-page editor context. + * + * @return true if the page wraps an editor, + * false if this is a form page. + */ + boolean isEditor(); + /** + * A hint to bring the provided object into focus. If the object is in a + * tree or table control, select it. If it is shown on a scrollable page, + * ensure that it is visible. If the object is not presented in + * the page, false should be returned to allow another + * page to try. + * + * @param object + * object to select and reveal + * @return true if the request was successful, false + * otherwise. + */ + boolean selectReveal(Object object); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java new file mode 100644 index 000000000..3c1e8cda5 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableLink.java @@ -0,0 +1,75 @@ +package org.argeo.cms.ui.forms; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable String that displays a browsable link when read-only */ +public class EditableLink extends EditablePropertyString implements + SwtEditablePart { + private static final long serialVersionUID = 5055000749992803591L; + + private String type; + private String message; + private boolean readOnly; + + public EditableLink(Composite parent, int style, Node node, + String propertyName, String type, String message) + throws RepositoryException { + super(parent, style, node, propertyName, message); + this.message = message; + this.type = type; + + readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + if (node.hasProperty(propertyName)) { + this.setStyle(FormStyle.propertyText.style()); + this.setText(node.getProperty(propertyName).getString()); + } else { + this.setStyle(FormStyle.propertyMessage.style()); + this.setText(""); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (EclipseUiUtils.isEmpty(text)) + lbl.setText(message); + else if (readOnly) + setLinkValue(lbl, text); + else + // if canEdit() we put only the value with no link + // to avoid glitches of the edition life cycle + lbl.setText(text); + } else if (child instanceof Text) { + Text txt = (Text) child; + if (EclipseUiUtils.isEmpty(text)) { + txt.setText(""); + txt.setMessage(message); + } else + txt.setText(text); + } + } + + private void setLinkValue(Label lbl, String text) { + if (FormStyle.email.style().equals(type)) + lbl.setText(FormUtils.getMailLink(text)); + else if (FormStyle.phone.style().equals(type)) + lbl.setText(FormUtils.getPhoneLink(text)); + else if (FormStyle.website.style().equals(type)) + lbl.setText(FormUtils.getUrlLink(text)); + else if (FormStyle.facebook.style().equals(type) + || FormStyle.instagram.style().equals(type) + || FormStyle.linkedIn.style().equals(type) + || FormStyle.twitter.style().equals(type)) + lbl.setText(FormUtils.getUrlLink(text)); + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java new file mode 100644 index 000000000..ff8270046 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java @@ -0,0 +1,261 @@ +package org.argeo.cms.ui.forms; + +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +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; + +/** Display, add or remove values from a list in a CMS context */ +public class EditableMultiStringProperty extends StyledControl implements SwtEditablePart { + private static final long serialVersionUID = -7044614381252178595L; + + private String propertyName; + private String message; + // TODO implement the ability to provide a list of possible values +// private String[] possibleValues; + private boolean canEdit; + private SelectionListener removeValueSL; + private List values; + + // TODO manage within the CSS + private int rowSpacing = 5; + private int rowMarging = 0; + private int oneValueMargingRight = 5; + private int btnWidth = 16; + private int btnHeight = 16; + private int btnHorizontalIndent = 3; + + public EditableMultiStringProperty(Composite parent, int style, Node node, String propertyName, List values, + String[] possibleValues, String addValueMsg, SelectionListener removeValueSelectionListener) + throws RepositoryException { + super(parent, style, node, true); + + this.propertyName = propertyName; + this.values = values; +// this.possibleValues = new String[] { "Un", "Deux", "Trois" }; + this.message = addValueMsg; + this.canEdit = removeValueSelectionListener != null; + this.removeValueSL = removeValueSelectionListener; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + // Row layout items do not need explicit layout data + protected void setControlLayoutData(Control control) { + } + + /** To be overridden */ + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.fillWidth()); + } + + @Override + public Control getControl() { + return super.getControl(); + } + + @Override + protected Control createControl(Composite box, String style) { + Composite row = new Composite(box, SWT.NO_FOCUS); + row.setLayoutData(EclipseUiUtils.fillAll()); + + RowLayout rl = new RowLayout(SWT.HORIZONTAL); + rl.wrap = true; + rl.spacing = rowSpacing; + rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging; + row.setLayout(rl); + + if (values != null) { + for (final String value : values) { + if (canEdit) + createRemovableValue(row, SWT.SINGLE, value); + else + createValueLabel(row, SWT.SINGLE, value); + } + } + + if (!canEdit) + return row; + else if (isEditing()) + return createText(row, style); + else + return createLabel(row, style); + } + + /** + * Override to provide specific layout for the existing values, typically adding + * a pound (#) char for tags or anchor info for browsable links. We assume the + * parent composite already has a layout and it is the caller responsibility to + * apply corresponding layout data + */ + protected Label createValueLabel(Composite parent, int style, String value) { + Label label = new Label(parent, style); + label.setText("#" + value); + CmsSwtUtils.markup(label); + CmsSwtUtils.style(label, FormStyle.propertyText.style()); + return label; + } + + private Composite createRemovableValue(Composite parent, int style, String value) { + Composite valCmp = new Composite(parent, SWT.NO_FOCUS); + GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); + gl.marginRight = oneValueMargingRight; + valCmp.setLayout(gl); + + createValueLabel(valCmp, SWT.WRAP, value); + + Button deleteBtn = new Button(valCmp, SWT.FLAT); + deleteBtn.setData(FormConstants.LINKED_VALUE, value); + deleteBtn.addSelectionListener(removeValueSL); + CmsSwtUtils.style(deleteBtn, FormStyle.delete.style() + FormStyle.BUTTON_SUFFIX); + GridData gd = new GridData(); + gd.heightHint = btnHeight; + gd.widthHint = btnWidth; + gd.horizontalIndent = btnHorizontalIndent; + deleteBtn.setLayoutData(gd); + + return valCmp; + } + + protected Text createText(Composite box, String style) { + final Text text = new Text(box, getStyle()); + // The "add new value" text is not meant to change, so we can set it on + // creation + text.setMessage(message); + CmsSwtUtils.style(text, style); + text.setFocus(); + + text.addTraverseListener(new TraverseListener() { + private static final long serialVersionUID = 1L; + + public void keyTraversed(TraverseEvent e) { + if (e.keyCode == SWT.CR) { + addValue(text); + e.doit = false; + } + } + }); + + // The OK button does not work with the focusOut listener + // because focus out is called before the OK button is pressed + + // // we must call layout() now so that the row data can compute the + // height + // // of the other controls. + // text.getParent().layout(); + // int height = text.getSize().y; + // + // Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM); + // okBtn.setText("OK"); + // RowData rd = new RowData(SWT.DEFAULT, height - 2); + // okBtn.setLayoutData(rd); + // + // okBtn.addSelectionListener(new SelectionAdapter() { + // private static final long serialVersionUID = 2780819012423622369L; + // + // @Override + // public void widgetSelected(SelectionEvent e) { + // addValue(text); + // } + // }); + + return text; + } + + /** Performs the real addition, overwrite to make further sanity checks */ + protected void addValue(Text text) { + String value = text.getText(); + String errMsg = null; + + if (EclipseUiUtils.isEmpty(value)) + return; + + if (values.contains(value)) + errMsg = "Dupplicated value: " + value + ", please correct and try again"; + if (errMsg != null) + MessageDialog.openError(this.getShell(), "Addition not allowed", errMsg); + else { + values.add(value); + Composite newCmp = createRemovableValue(text.getParent(), SWT.SINGLE, value); + newCmp.moveAbove(text); + text.setText(""); + newCmp.getParent().layout(); + } + } + + protected Label createLabel(Composite box, String style) { + if (canEdit) { + Label lbl = new Label(box, getStyle()); + lbl.setText(message); + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + return null; + } + + protected void clear(boolean deep) { + Control child = getControl(); + if (deep) + super.clear(deep); + else { + child.getParent().dispose(); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (canEdit) + lbl.setText(text); + else + lbl.setText(""); + } else if (child instanceof Text) { + Text txt = (Text) child; + txt.setText(text); + } + } + + public synchronized void startEditing() { + CmsSwtUtils.style(getControl(), FormStyle.propertyText.style()); +// getControl().setData(STYLE, FormStyle.propertyText.style()); + super.startEditing(); + } + + public synchronized void stopEditing() { + CmsSwtUtils.style(getControl(), FormStyle.propertyMessage.style()); +// getControl().setData(STYLE, FormStyle.propertyMessage.style()); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java new file mode 100644 index 000000000..641f916f2 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java @@ -0,0 +1,298 @@ +package org.argeo.cms.ui.forms; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +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.Control; +import org.eclipse.swt.widgets.DateTime; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** CMS form part to display and edit a date */ +public class EditablePropertyDate extends StyledControl implements SwtEditablePart { + private static final long serialVersionUID = 2500215515778162468L; + + // Context + private String propertyName; + private String message; + private DateFormat dateFormat; + + // UI Objects + private Text dateTxt; + private Button openCalBtn; + + // TODO manage within the CSS + private int fieldBtnSpacing = 5; + + /** + * + * @param parent + * @param style + * @param node + * @param propertyName + * @param message + * @param dateFormat provide a {@link DateFormat} as contract to be able to + * read/write dates as strings + * @throws RepositoryException + */ + public EditablePropertyDate(Composite parent, int style, Node node, String propertyName, String message, + DateFormat dateFormat) throws RepositoryException { + super(parent, style, node, false); + + this.propertyName = propertyName; + this.message = message; + this.dateFormat = dateFormat; + + if (node.hasProperty(propertyName)) { + this.setStyle(FormStyle.propertyText.style()); + this.setText(dateFormat.format(node.getProperty(propertyName).getDate().getTime())); + } else { + this.setStyle(FormStyle.propertyMessage.style()); + this.setText(message); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (EclipseUiUtils.isEmpty(text)) + lbl.setText(message); + else + lbl.setText(text); + } else if (child instanceof Text) { + Text txt = (Text) child; + if (EclipseUiUtils.isEmpty(text)) { + txt.setText(""); + } else + txt.setText(text); + } + } + + public synchronized void startEditing() { + // if (dateTxt != null && !dateTxt.isDisposed()) + CmsSwtUtils.style(getControl(), FormStyle.propertyText); +// getControl().setData(STYLE, FormStyle.propertyText.style()); + super.startEditing(); + } + + public synchronized void stopEditing() { + if (EclipseUiUtils.isEmpty(dateTxt.getText())) + CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); +// getControl().setData(STYLE, FormStyle.propertyMessage.style()); + else + CmsSwtUtils.style(getControl(), FormStyle.propertyText); +// getControl().setData(STYLE, FormStyle.propertyText.style()); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) { + return createCustomEditableControl(box, style); + } else + return createLabel(box, style); + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle() | SWT.WRAP); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + private Control createCustomEditableControl(Composite box, String style) { + box.setLayoutData(CmsSwtUtils.fillWidth()); + Composite dateComposite = new Composite(box, SWT.NONE); + GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); + gl.horizontalSpacing = fieldBtnSpacing; + dateComposite.setLayout(gl); + dateTxt = new Text(dateComposite, SWT.BORDER); + CmsSwtUtils.style(dateTxt, style); + dateTxt.setLayoutData(new GridData(120, SWT.DEFAULT)); + dateTxt.setToolTipText( + "Enter a date with form \"" + FormUtils.DEFAULT_SHORT_DATE_FORMAT + "\" or use the calendar"); + openCalBtn = new Button(dateComposite, SWT.FLAT); + CmsSwtUtils.style(openCalBtn, FormStyle.calendar.style() + FormStyle.BUTTON_SUFFIX); + GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false); + gd.heightHint = 17; + openCalBtn.setLayoutData(gd); + // openCalBtn.setImage(PeopleRapImages.CALENDAR_BTN); + + openCalBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 1L; + + public void widgetSelected(SelectionEvent event) { + CalendarPopup popup = new CalendarPopup(dateTxt); + popup.open(); + } + }); + + // dateTxt.addFocusListener(new FocusListener() { + // private static final long serialVersionUID = 1L; + // + // @Override + // public void focusLost(FocusEvent event) { + // String newVal = dateTxt.getText(); + // // Enable reset of the field + // if (FormUtils.notNull(newVal)) + // calendar = null; + // else { + // try { + // Calendar newCal = parseDate(newVal); + // // DateText.this.setText(newCal); + // calendar = newCal; + // } catch (ParseException pe) { + // // Silent. Manage error popup? + // if (calendar != null) + // EditablePropertyDate.this.setText(calendar); + // } + // } + // } + // + // @Override + // public void focusGained(FocusEvent event) { + // } + // }); + return dateTxt; + } + + protected void clear(boolean deep) { + Control child = getControl(); + if (deep || child instanceof Label) + super.clear(deep); + else { + child.getParent().dispose(); + } + } + + /** Enable setting a custom tooltip on the underlying text */ + @Deprecated + public void setToolTipText(String toolTipText) { + dateTxt.setToolTipText(toolTipText); + } + + @Deprecated + /** Enable setting a custom message on the underlying text */ + public void setMessage(String message) { + dateTxt.setMessage(message); + } + + @Deprecated + public void setText(Calendar cal) { + String newValueStr = ""; + if (cal != null) + newValueStr = dateFormat.format(cal.getTime()); + if (!newValueStr.equals(dateTxt.getText())) + dateTxt.setText(newValueStr); + } + + // UTILITIES TO MANAGE THE CALENDAR POPUP + // TODO manage the popup shell in a cleaner way + private class CalendarPopup extends Shell { + private static final long serialVersionUID = 1L; + private DateTime dateTimeCtl; + + public CalendarPopup(Control source) { + super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + populate(); + // Add border and shadow style + CmsSwtUtils.markup(CalendarPopup.this); + CmsSwtUtils.style(CalendarPopup.this, FormStyle.popupCalendar.style()); + pack(); + layout(); + setLocation(source.toDisplay((source.getLocation().x - 2), (source.getSize().y) + 3)); + + addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + close(); + dispose(); + } + }); + open(); + } + + private void setProperty() { + // Direct set does not seems to work. investigate + // cal.set(dateTimeCtl.getYear(), dateTimeCtl.getMonth(), + // dateTimeCtl.getDay(), 12, 0); + Calendar cal = new GregorianCalendar(); + cal.set(Calendar.YEAR, dateTimeCtl.getYear()); + cal.set(Calendar.MONTH, dateTimeCtl.getMonth()); + cal.set(Calendar.DAY_OF_MONTH, dateTimeCtl.getDay()); + String dateStr = dateFormat.format(cal.getTime()); + dateTxt.setText(dateStr); + } + + protected void populate() { + setLayout(EclipseUiUtils.noSpaceGridLayout()); + + dateTimeCtl = new DateTime(this, SWT.CALENDAR); + dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll()); + + Calendar calendar = FormUtils.parseDate(dateFormat, dateTxt.getText()); + + if (calendar != null) + dateTimeCtl.setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH)); + + dateTimeCtl.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -8414377364434281112L; + + @Override + public void widgetSelected(SelectionEvent e) { + setProperty(); + } + }); + + dateTimeCtl.addMouseListener(new MouseListener() { + private static final long serialVersionUID = 1L; + + @Override + public void mouseUp(MouseEvent e) { + } + + @Override + public void mouseDown(MouseEvent e) { + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + setProperty(); + close(); + dispose(); + } + }); + } + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java new file mode 100644 index 000000000..f2575e1f9 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java @@ -0,0 +1,80 @@ +package org.argeo.cms.ui.forms; + +import static org.argeo.cms.ui.forms.FormStyle.propertyMessage; +import static org.argeo.cms.ui.forms.FormStyle.propertyText; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.EditableText; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable String in a CMS context */ +public class EditablePropertyString extends EditableText implements SwtEditablePart { + private static final long serialVersionUID = 5055000749992803591L; + + private String propertyName; + private String message; + + // encode the '&' character in rap + private final static String AMPERSAND = "&"; + private final static String AMPERSAND_REGEX = "&(?![#a-zA-Z0-9]+;)"; + + public EditablePropertyString(Composite parent, int style, Node node, String propertyName, String message) + throws RepositoryException { + super(parent, style, node, true); + //setUseTextAsLabel(true); + this.propertyName = propertyName; + this.message = message; + + if (node.hasProperty(propertyName)) { + this.setStyle(propertyText.style()); + this.setText(node.getProperty(propertyName).getString()); + } else { + this.setStyle(propertyMessage.style()); + this.setText(message + " "); + } + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) { + Label lbl = (Label) child; + if (EclipseUiUtils.isEmpty(text)) + lbl.setText(message + " "); + else + // TODO enhance this + lbl.setText(text.replaceAll(AMPERSAND_REGEX, AMPERSAND)); + } else if (child instanceof Text) { + Text txt = (Text) child; + if (EclipseUiUtils.isEmpty(text)) { + txt.setText(""); + txt.setMessage(message + " "); + } else + txt.setText(text.replaceAll("
", "\n")); + } + } + + public synchronized void startEditing() { + CmsSwtUtils.style(getControl(), FormStyle.propertyText); + super.startEditing(); + } + + public synchronized void stopEditing() { + if (EclipseUiUtils.isEmpty(((Text) getControl()).getText())) + CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); + else + CmsSwtUtils.style(getControl(), FormStyle.propertyText); + super.stopEditing(); + } + + public String getPropertyName() { + return propertyName; + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java new file mode 100644 index 000000000..fe9f7e7d7 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormConstants.java @@ -0,0 +1,7 @@ +package org.argeo.cms.ui.forms; + +/** Constants used in the various CMS Forms */ +public interface FormConstants { + // DATAKEYS + public final static String LINKED_VALUE = "LinkedValue"; +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java new file mode 100644 index 000000000..a75c19150 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java @@ -0,0 +1,114 @@ +package org.argeo.cms.ui.forms; + +import java.util.Observable; +import java.util.Observer; + +import javax.jcr.Node; + +import org.argeo.api.cms.ux.CmsEditable; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; + +/** Add life cycle management abilities to an editable form page */ +public class FormEditorHeader implements SelectionListener, Observer { + private static final long serialVersionUID = 7392898696542484282L; + + // private final Node context; + private final CmsEditable cmsEditable; + private Button publishBtn; + + // Should we provide here the ability to switch from read only to edition + // mode? + // private Button editBtn; + // private boolean readOnly; + + // TODO add information about the current node status, typically if it is + // dirty or not + + private Composite parent; + private Composite display; + private Object layoutData; + + public FormEditorHeader(Composite parent, int style, Node context, + CmsEditable cmsEditable) { + this.cmsEditable = cmsEditable; + this.parent = parent; + // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + // this.context = context; + if (this.cmsEditable instanceof Observable) + ((Observable) this.cmsEditable).addObserver(this); + refresh(); + } + + public void setLayoutData(Object layoutData) { + this.layoutData = layoutData; + if (display != null && !display.isDisposed()) + display.setLayoutData(layoutData); + } + + protected void refresh() { + if (display != null && !display.isDisposed()) + display.dispose(); + + display = new Composite(parent, SWT.NONE); + display.setLayoutData(layoutData); + + CmsSwtUtils.style(display, FormStyle.header.style()); + display.setBackgroundMode(SWT.INHERIT_FORCE); + + display.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + publishBtn = createSimpleBtn(display, getPublishButtonLabel()); + display.moveAbove(null); + parent.layout(); + } + + private Button createSimpleBtn(Composite parent, String label) { + Button button = new Button(parent, SWT.FLAT | SWT.PUSH); + button.setText(label); + CmsSwtUtils.style(button, FormStyle.header.style()); + button.addSelectionListener(this); + return button; + } + + private String getPublishButtonLabel() { + // Rather check if the current node differs from what has been + // previously committed + // For the time being, we always reach here, the underlying CmsEditable + // is always editing. + if (cmsEditable.isEditing()) + return " Publish "; + else + return " Edit "; + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (e.getSource() == publishBtn) { + // For the time being, the underlying CmsEditable + // is always editing when we reach this point + if (cmsEditable.isEditing()) { + // we always leave the node in a check outed state + cmsEditable.stopEditing(); + cmsEditable.startEditing(); + } else { + cmsEditable.startEditing(); + } + } + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + @Override + public void update(Observable o, Object arg) { + if (o == cmsEditable) { + refresh(); + } + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java new file mode 100644 index 000000000..1888055fc --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java @@ -0,0 +1,608 @@ +package org.argeo.cms.ui.forms; + +import java.io.IOException; +import java.io.InputStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsEditable; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.viewers.AbstractPageViewer; +import org.argeo.cms.ui.viewers.Section; +import org.argeo.cms.ui.viewers.SectionPart; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.rap.fileupload.FileDetails; +import org.eclipse.rap.fileupload.FileUploadEvent; +import org.eclipse.rap.fileupload.FileUploadHandler; +import org.eclipse.rap.fileupload.FileUploadListener; +import org.eclipse.rap.fileupload.FileUploadReceiver; +import org.eclipse.rap.rwt.service.ServerPushSession; +import org.eclipse.rap.rwt.widgets.FileUpload; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.layout.FormLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +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; + +/** Manage life cycle of a form page that is linked to a given node */ +public class FormPageViewer extends AbstractPageViewer { + private final static CmsLog log = CmsLog.getLog(FormPageViewer.class); + private static final long serialVersionUID = 5277789504209413500L; + + private final Section mainSection; + + // TODO manage within the CSS + private Integer labelColWidth = null; + private int rowLayoutHSpacing = 8; + + // Context cached in the viewer + // The reference to translate from text to calendar and reverse + private DateFormat dateFormat = new SimpleDateFormat(FormUtils.DEFAULT_SHORT_DATE_FORMAT); + private CmsImageManager imageManager; + private FileUploadListener fileUploadListener; + + public FormPageViewer(Section mainSection, int style, CmsEditable cmsEditable) throws RepositoryException { + super(mainSection, style, cmsEditable); + this.mainSection = mainSection; + + if (getCmsEditable().canEdit()) { + fileUploadListener = new FUL(); + } + } + + @Override + protected void prepare(SwtEditablePart part, Object caretPosition) { + if (part instanceof Img) { + ((Img) part).setFileUploadListener(fileUploadListener); + } + } + + /** To be overridden.Save the edited part. */ + protected void save(SwtEditablePart part) throws RepositoryException { + Node node = null; + if (part instanceof EditableMultiStringProperty) { + EditableMultiStringProperty ept = (EditableMultiStringProperty) part; + // SWT : View + List values = ept.getValues(); + // JCR : Model + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (values.isEmpty()) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + node.setProperty(propName, values.toArray(new String[0])); + } + // => Viewer : Controller + } else if (part instanceof EditablePropertyString) { + EditablePropertyString ept = (EditablePropertyString) part; + // SWT : View + String txt = ((Text) ept.getControl()).getText(); + // JCR : Model + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (EclipseUiUtils.isEmpty(txt)) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + setPropertySilently(node, propName, txt); + // node.setProperty(propName, txt); + } + // node.getSession().save(); + // => Viewer : Controller + } else if (part instanceof EditablePropertyDate) { + EditablePropertyDate ept = (EditablePropertyDate) part; + Calendar cal = FormUtils.parseDate(dateFormat, ((Text) ept.getControl()).getText()); + node = ept.getNode(); + String propName = ept.getPropertyName(); + if (cal == null) { + if (node.hasProperty(propName)) + node.getProperty(propName).remove(); + } else { + node.setProperty(propName, cal); + } + // node.getSession().save(); + // => Viewer : Controller + } + // TODO: make this configurable, sometimes we do not want to save the + // current session at this stage + if (node != null && node.getSession().hasPendingChanges()) { + JcrUtils.updateLastModified(node, true); + node.getSession().save(); + } + } + + @Override + protected void updateContent(SwtEditablePart part) throws RepositoryException { + if (part instanceof EditableMultiStringProperty) { + EditableMultiStringProperty ept = (EditableMultiStringProperty) part; + // SWT : View + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + List valStrings = new ArrayList(); + if (node.hasProperty(propName)) { + Value[] values = node.getProperty(propName).getValues(); + for (Value val : values) + valStrings.add(val.getString()); + } + ept.setValues(valStrings); + } else if (part instanceof EditablePropertyString) { + // || part instanceof EditableLink + EditablePropertyString ept = (EditablePropertyString) part; + // JCR : Model + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + if (node.hasProperty(propName)) { + String value = node.getProperty(propName).getString(); + ept.setText(value); + } else + ept.setText(""); + // => Viewer : Controller + } else if (part instanceof EditablePropertyDate) { + EditablePropertyDate ept = (EditablePropertyDate) part; + // JCR : Model + Node node = ept.getNode(); + String propName = ept.getPropertyName(); + if (node.hasProperty(propName)) + ept.setText(dateFormat.format(node.getProperty(propName).getDate().getTime())); + else + ept.setText(""); + } else if (part instanceof SectionPart) { + SectionPart sectionPart = (SectionPart) part; + Node partNode = sectionPart.getNode(); + // use control AFTER setting style, since it may have been reset + if (part instanceof EditableImage) { + EditableImage editableImage = (EditableImage) part; + imageManager().load(partNode, part.getControl(), editableImage.getPreferredImageSize()); + } + } + } + + // FILE UPLOAD LISTENER + protected class FUL implements FileUploadListener { + + public FUL() { + } + + public void uploadProgress(FileUploadEvent event) { + // TODO Monitor upload progress + } + + public void uploadFailed(FileUploadEvent event) { + throw new IllegalStateException("Upload failed " + event, event.getException()); + } + + public void uploadFinished(FileUploadEvent event) { + for (FileDetails file : event.getFileDetails()) { + if (log.isDebugEnabled()) + log.debug("Received: " + file.getFileName()); + } + mainSection.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + saveEdit(); + } + }); + FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource(); + uploadHandler.dispose(); + } + } + + // FOCUS OUT LISTENER + protected FocusListener createFocusListener() { + return new FocusOutListener(); + } + + private class FocusOutListener implements FocusListener { + private static final long serialVersionUID = -6069205786732354186L; + + @Override + public void focusLost(FocusEvent event) { + saveEdit(); + } + + @Override + public void focusGained(FocusEvent event) { + // does nothing; + } + } + + // MOUSE LISTENER + @Override + protected MouseListener createMouseListener() { + return new ML(); + } + + private class ML extends MouseAdapter { + private static final long serialVersionUID = 8526890859876770905L; + + @Override + public void mouseDoubleClick(MouseEvent e) { + if (e.button == 1) { + Control source = (Control) e.getSource(); + if (getCmsEditable().canEdit()) { + if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) { + if (source == mainSection) + return; + SwtEditablePart part = findDataParent(source); + upload(part); + } else { + getCmsEditable().startEditing(); + } + } + } + } + + @Override + public void mouseDown(MouseEvent e) { + if (getCmsEditable().isEditing()) { + if (e.button == 1) { + Control source = (Control) e.getSource(); + SwtEditablePart composite = findDataParent(source); + Point point = new Point(e.x, e.y); + if (!(composite instanceof Img)) + edit(composite, source.toDisplay(point)); + } else if (e.button == 3) { + // EditablePart composite = findDataParent((Control) e + // .getSource()); + // if (styledTools != null) + // styledTools.show(composite, new Point(e.x, e.y)); + } + } + } + + protected synchronized void upload(SwtEditablePart part) { + if (part instanceof SectionPart) { + if (part instanceof Img) { + if (getEdited() == part) + return; + edit(part, null); + layout(part.getControl()); + } + } + } + } + + @Override + public Control getControl() { + return mainSection; + } + + protected CmsImageManager imageManager() { + if (imageManager == null) + imageManager = (CmsImageManager) CmsSwtUtils.getCmsView(mainSection).getImageManager(); + return imageManager; + } + + // LOCAL UI HELPERS + protected Section createSectionIfNeeded(Composite body, Node node) throws RepositoryException { + Section section = null; + if (node != null) { + section = new Section(body, SWT.NO_FOCUS, node); + section.setLayoutData(CmsSwtUtils.fillWidth()); + section.setLayout(CmsSwtUtils.noSpaceGridLayout()); + } + return section; + } + + protected void createSimpleLT(Composite bodyRow, Node node, String propName, String label, String msg) + throws RepositoryException { + if (getCmsEditable().canEdit() || node.hasProperty(propName)) { + createPropertyLbl(bodyRow, label); + EditablePropertyString eps = new EditablePropertyString(bodyRow, SWT.WRAP | SWT.LEFT, node, propName, msg); + eps.setMouseListener(getMouseListener()); + eps.setFocusListener(getFocusListener()); + eps.setLayoutData(CmsSwtUtils.fillWidth()); + } + } + + protected void createMultiStringLT(Composite bodyRow, Node node, String propName, String label, String msg) + throws RepositoryException { + boolean canEdit = getCmsEditable().canEdit(); + if (canEdit || node.hasProperty(propName)) { + createPropertyLbl(bodyRow, label); + + List valueStrings = new ArrayList(); + + if (node.hasProperty(propName)) { + Value[] values = node.getProperty(propName).getValues(); + for (Value value : values) + valueStrings.add(value.getString()); + } + + // TODO use a drop down to display possible values to the end user + EditableMultiStringProperty emsp = new EditableMultiStringProperty(bodyRow, SWT.SINGLE | SWT.LEAD, node, + propName, valueStrings, new String[] { "Implement this" }, msg, + canEdit ? getRemoveValueSelListener() : null); + addListeners(emsp); + // emsp.setMouseListener(getMouseListener()); + emsp.setStyle(FormStyle.propertyMessage.style()); + emsp.setLayoutData(CmsSwtUtils.fillWidth()); + } + } + + protected Label createPropertyLbl(Composite parent, String value) { + return createPropertyLbl(parent, value, SWT.NONE); + } + + protected Label createPropertyLbl(Composite parent, String value, int vAlign) { + // boolean isSmall = CmsView.getCmsView(parent).getUxContext().isSmall(); + Label label = new Label(parent, SWT.LEAD | SWT.WRAP); + label.setText(value + " "); + CmsSwtUtils.style(label, FormStyle.propertyLabel.style()); + GridData gd = new GridData(SWT.LEAD, vAlign, false, false); + if (labelColWidth != null) + gd.widthHint = labelColWidth; + label.setLayoutData(gd); + return label; + } + + protected Label newStyledLabel(Composite parent, String style, String value) { + Label label = new Label(parent, SWT.NONE); + label.setText(value); + CmsSwtUtils.style(label, style); + return label; + } + + protected Composite createRowLayoutComposite(Composite parent) throws RepositoryException { + Composite bodyRow = new Composite(parent, SWT.NO_FOCUS); + bodyRow.setLayoutData(CmsSwtUtils.fillWidth()); + RowLayout rl = new RowLayout(SWT.WRAP); + rl.type = SWT.HORIZONTAL; + rl.spacing = rowLayoutHSpacing; + rl.marginHeight = rl.marginWidth = 0; + rl.marginTop = rl.marginBottom = rl.marginLeft = rl.marginRight = 0; + bodyRow.setLayout(rl); + return bodyRow; + } + + protected Composite createAddImgComposite(final Section section, Composite parent, final Node parentNode) + throws RepositoryException { + + Composite body = new Composite(parent, SWT.NO_FOCUS); + body.setLayout(new GridLayout()); + + FormFileUploadReceiver receiver = new FormFileUploadReceiver(section, parentNode, null); + final FileUploadHandler currentUploadHandler = new FileUploadHandler(receiver); + if (fileUploadListener != null) + currentUploadHandler.addUploadListener(fileUploadListener); + + // Button creation + final FileUpload fileUpload = new FileUpload(body, SWT.BORDER); + fileUpload.setText("Import an image"); + fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + fileUpload.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 4869523412991968759L; + + @Override + public void widgetSelected(SelectionEvent e) { + ServerPushSession pushSession = new ServerPushSession(); + pushSession.start(); + String uploadURL = currentUploadHandler.getUploadUrl(); + fileUpload.submit(uploadURL); + } + }); + + return body; + } + + protected class FormFileUploadReceiver extends FileUploadReceiver { + + private Node context; + private Section section; + private String name; + + public FormFileUploadReceiver(Section section, Node context, String name) { + this.context = context; + this.section = section; + this.name = name; + } + + @Override + public void receive(InputStream stream, FileDetails details) throws IOException { + + if (name == null) + name = details.getFileName(); + + // TODO clean image name more carefully + String cleanedName = name.replaceAll("[^a-zA-Z0-9-.]", ""); + // We add a unique prefix to workaround the cache issue: when + // deleting and re-adding a new image with same name, the end user + // browser will use the cache and the image will remain unchanged + // for a while + cleanedName = System.currentTimeMillis() % 100000 + "_" + cleanedName; + + imageManager().uploadImage(context, context, cleanedName, stream, details.getContentType()); + // TODO clean refresh strategy + section.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + try { + FormPageViewer.this.refresh(section); + section.layout(); + section.getParent().layout(); + } catch (RepositoryException re) { + throw new JcrException("Unable to refresh " + "image section for " + context, re); + } + } + }); + } + } + + protected void addListeners(StyledControl control) { + control.setMouseListener(getMouseListener()); + control.setFocusListener(getFocusListener()); + } + + protected Img createImgComposite(Composite parent, Node node, Point preferredSize) throws RepositoryException { + Img img = new Img(parent, SWT.NONE, node, new Cms2DSize(preferredSize.x, preferredSize.y)) { + private static final long serialVersionUID = 1297900641952417540L; + + @Override + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + + @Override + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + } + }; + img.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); + updateContent(img); + addListeners(img); + return img; + } + + protected Composite addDeleteAbility(final Section section, final Node sessionNode, int topWeight, + int rightWeight) { + Composite comp = new Composite(section, SWT.NONE); + comp.setLayoutData(CmsSwtUtils.fillAll()); + comp.setLayout(new FormLayout()); + + // The body to be populated + Composite body = new Composite(comp, SWT.NO_FOCUS); + body.setLayoutData(EclipseUiUtils.fillFormData()); + + if (getCmsEditable().canEdit()) { + // the delete button + Button deleteBtn = new Button(comp, SWT.FLAT); + CmsSwtUtils.style(deleteBtn, FormStyle.deleteOverlay.style()); + FormData formData = new FormData(); + formData.right = new FormAttachment(rightWeight, 0); + formData.top = new FormAttachment(topWeight, 0); + deleteBtn.setLayoutData(formData); + deleteBtn.moveAbove(body); + + deleteBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = 4304223543657238462L; + + @Override + public void widgetSelected(SelectionEvent e) { + super.widgetSelected(e); + if (MessageDialog.openConfirm(section.getShell(), "Confirm deletion", + "Are you really you want to remove this?")) { + Session session; + try { + session = sessionNode.getSession(); + Section parSection = section.getParentSection(); + sessionNode.remove(); + session.save(); + refresh(parSection); + layout(parSection); + } catch (RepositoryException re) { + throw new JcrException("Unable to delete " + sessionNode, re); + } + + } + + } + }); + } + return body; + } + +// // LOCAL HELPERS FOR NODE MANAGEMENT +// private Node getOrCreateNode(Node parent, String nodeName, String nodeType) throws RepositoryException { +// Node node = null; +// if (getCmsEditable().canEdit() && !parent.hasNode(nodeName)) { +// node = JcrUtils.mkdirs(parent, nodeName, nodeType); +// parent.getSession().save(); +// } +// +// if (getCmsEditable().canEdit() || parent.hasNode(nodeName)) +// node = parent.getNode(nodeName); +// +// return node; +// } + + private SelectionListener getRemoveValueSelListener() { + return new SelectionAdapter() { + private static final long serialVersionUID = 9022259089907445195L; + + @Override + public void widgetSelected(SelectionEvent e) { + Object source = e.getSource(); + if (source instanceof Button) { + Button btn = (Button) source; + Object obj = btn.getData(FormConstants.LINKED_VALUE); + SwtEditablePart ep = findDataParent(btn); + if (ep != null && ep instanceof EditableMultiStringProperty) { + EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep; + List values = emsp.getValues(); + if (values.contains(obj)) { + values.remove(values.indexOf(obj)); + emsp.setValues(values); + try { + save(emsp); + // TODO workaround to force refresh + edit(emsp, 0); + cancelEdit(); + } catch (RepositoryException e1) { + throw new JcrException("Unable to remove value " + obj, e1); + } + layout(emsp); + } + } + } + } + }; + } + + protected void setPropertySilently(Node node, String propName, String value) throws RepositoryException { + try { + // TODO Clean this: + // Format strings to replace \n + value = value.replaceAll("\n", "
"); + // Do not make the update if validation fails + try { + MarkupValidatorCopy.getInstance().validate(value); + } catch (Exception e) { + log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + + ", String cannot be validated - " + e.getMessage()); + return; + } + // TODO check if the newly created property is of the correct type, + // otherwise the property will be silently created with a STRING + // property type. + node.setProperty(propName, value); + } catch (ValueFormatException vfe) { + log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + " - " + vfe.getMessage()); + } + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java new file mode 100644 index 000000000..709ecd024 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormStyle.java @@ -0,0 +1,29 @@ +package org.argeo.cms.ui.forms; + +import org.argeo.api.cms.ux.CmsStyle; + +/** Syles used */ +public enum FormStyle implements CmsStyle { + // Main + form, title, + // main part + header, headerBtn, headerCombo, section, sectionHeader, + // Property fields + propertyLabel, propertyText, propertyMessage, errorMessage, + // Date + popupCalendar, + // Buttons + starred, unstarred, starOverlay, editOverlay, deleteOverlay, updateOverlay, deleteOverlaySmall, calendar, delete, + // Contacts + email, address, phone, website, + // Social Media + facebook, twitter, linkedIn, instagram; + + @Override + public String getClassPrefix() { + return "argeo-form"; + } + + // TODO clean button style management + public final static String BUTTON_SUFFIX = "_btn"; +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java new file mode 100644 index 000000000..ef49c8ae5 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/FormUtils.java @@ -0,0 +1,196 @@ +package org.argeo.cms.ui.forms; + +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Utilitary methods to ease implementation of CMS forms */ +public class FormUtils { + private final static CmsLog log = CmsLog.getLog(FormUtils.class); + + public final static String DEFAULT_SHORT_DATE_FORMAT = "dd/MM/yyyy"; + + /** Best effort to convert a String to a calendar. Fails silently */ + public static Calendar parseDate(DateFormat dateFormat, String calStr) { + Calendar cal = null; + if (EclipseUiUtils.notEmpty(calStr)) { + try { + Date date = dateFormat.parse(calStr); + cal = new GregorianCalendar(); + cal.setTime(date); + } catch (ParseException pe) { + // Silent + log.warn("Unable to parse date: " + calStr + " - msg: " + + pe.getMessage()); + } + } + return cal; + } + + /** Add a double click listener on tables that display a JCR node list */ + public static void addCanonicalDoubleClickListener(final TableViewer v) { + v.addDoubleClickListener(new IDoubleClickListener() { + + @Override + public void doubleClick(DoubleClickEvent event) { + CmsView cmsView = CmsUiUtils.getCmsView(); + Node node = (Node) ((IStructuredSelection) event.getSelection()) + .getFirstElement(); + try { + cmsView.navigateTo(node.getPath()); + } catch (RepositoryException e) { + throw new CmsException("Unable to get path for node " + + node + " before calling navigateTo(path)", e); + } + } + }); + } + + // MANAGE ERROR DECORATION + + public static ControlDecoration addDecoration(final Text text) { + final ControlDecoration dynDecoration = new ControlDecoration(text, + SWT.LEFT); + Image icon = getDecorationImage(FieldDecorationRegistry.DEC_ERROR); + dynDecoration.setImage(icon); + dynDecoration.setMarginWidth(3); + dynDecoration.hide(); + return dynDecoration; + } + + public static void refreshDecoration(Text text, ControlDecoration deco, + boolean isValid, boolean clean) { + if (isValid || clean) { + text.setBackground(null); + deco.hide(); + } else { + text.setBackground(new Color(text.getDisplay(), 250, 200, 150)); + deco.show(); + } + } + + public static Image getDecorationImage(String image) { + FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault(); + return registry.getFieldDecoration(image).getImage(); + } + + public static void addCompulsoryDecoration(Label label) { + final ControlDecoration dynDecoration = new ControlDecoration(label, + SWT.RIGHT | SWT.TOP); + Image icon = getDecorationImage(FieldDecorationRegistry.DEC_REQUIRED); + dynDecoration.setImage(icon); + dynDecoration.setMarginWidth(3); + } + + // TODO the read only generation of read only links for various contact type + // should be factorised in the cms Utils. + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able phone number + */ + public static String getPhoneLink(String value) { + return getPhoneLink(value, value); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able phone number + * + * @param value + * @param label + * a potentially distinct label + * @return + */ + public static String getPhoneLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + builder.append("").append(label) + .append(""); + return builder.toString(); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able mail + */ + public static String getMailLink(String value) { + return getMailLink(value, value); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able mail + * + * @param value + * @param label + * a potentially distinct label + * @return + */ + public static String getMailLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + value = replaceAmpersand(value); + builder.append("").append(label).append(""); + return builder.toString(); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able link + */ + public static String getUrlLink(String value) { + return getUrlLink(value, value); + } + + /** + * Creates the read-only HTML snippet to display in a label with styling + * enabled in order to provide a click-able link + */ + public static String getUrlLink(String value, String label) { + StringBuilder builder = new StringBuilder(); + value = replaceAmpersand(value); + label = replaceAmpersand(label); + if (!(value.startsWith("http://") || value.startsWith("https://"))) + value = "http://" + value; + builder.append("" + label + ""); + return builder.toString(); + } + + private static String AMPERSAND = "&"; + + /** + * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to + * avoid SAXParseException while rendering HTML with RWT + */ + public static String replaceAmpersand(String value) { + value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND); + return value; + } + + // Prevents instantiation + private FormUtils() { + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java new file mode 100644 index 000000000..3f588d1ea --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java @@ -0,0 +1,169 @@ +package org.argeo.cms.ui.forms; + +import java.io.StringReader; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.eclipse.rap.rwt.SingletonUtil; +import org.eclipse.swt.widgets.Widget; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Copy of RAP v2.3 since it is in an internal package. + */ +class MarkupValidatorCopy { + + // Used by Eclipse Scout project + public static final String MARKUP_VALIDATION_DISABLED = "org.eclipse.rap.rwt.markupValidationDisabled"; + + private static final String DTD = createDTD(); + private static final Map SUPPORTED_ELEMENTS = createSupportedElementsMap(); + private final SAXParser saxParser; + + public static MarkupValidatorCopy getInstance() { + return SingletonUtil.getSessionInstance(MarkupValidatorCopy.class); + } + + public MarkupValidatorCopy() { + saxParser = createSAXParser(); + } + + public void validate(String text) { + StringBuilder markup = new StringBuilder(); + markup.append(DTD); + markup.append(""); + markup.append(text); + markup.append(""); + InputSource inputSource = new InputSource(new StringReader(markup.toString())); + try { + saxParser.parse(inputSource, new MarkupHandler()); + } catch (RuntimeException exception) { + throw exception; + } catch (Exception exception) { + throw new IllegalArgumentException("Failed to parse markup text", exception); + } + } + + public static boolean isValidationDisabledFor(Widget widget) { + return Boolean.TRUE.equals(widget.getData(MARKUP_VALIDATION_DISABLED)); + } + + private static SAXParser createSAXParser() { + SAXParser result = null; + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + try { + result = parserFactory.newSAXParser(); + } catch (Exception exception) { + throw new RuntimeException("Failed to create SAX parser", exception); + } + return result; + } + + private static String createDTD() { + StringBuilder result = new StringBuilder(); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append(""); + result.append("]>"); + return result.toString(); + } + + private static Map createSupportedElementsMap() { + Map result = new HashMap(); + result.put("html", new String[0]); + result.put("br", new String[0]); + result.put("b", new String[] { "style" }); + result.put("strong", new String[] { "style" }); + result.put("i", new String[] { "style" }); + result.put("em", new String[] { "style" }); + result.put("sub", new String[] { "style" }); + result.put("sup", new String[] { "style" }); + result.put("big", new String[] { "style" }); + result.put("small", new String[] { "style" }); + result.put("del", new String[] { "style" }); + result.put("ins", new String[] { "style" }); + result.put("code", new String[] { "style" }); + result.put("samp", new String[] { "style" }); + result.put("kbd", new String[] { "style" }); + result.put("var", new String[] { "style" }); + result.put("cite", new String[] { "style" }); + result.put("dfn", new String[] { "style" }); + result.put("q", new String[] { "style" }); + result.put("abbr", new String[] { "style", "title" }); + result.put("span", new String[] { "style" }); + result.put("img", new String[] { "style", "src", "width", "height", "title", "alt" }); + result.put("a", new String[] { "style", "href", "target", "title" }); + return result; + } + + private static class MarkupHandler extends DefaultHandler { + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) { + checkSupportedElements(name, attributes); + checkSupportedAttributes(name, attributes); + checkMandatoryAttributes(name, attributes); + } + + private static void checkSupportedElements(String elementName, Attributes attributes) { + if (!SUPPORTED_ELEMENTS.containsKey(elementName)) { + throw new IllegalArgumentException("Unsupported element in markup text: " + elementName); + } + } + + private static void checkSupportedAttributes(String elementName, Attributes attributes) { + if (attributes.getLength() > 0) { + List supportedAttributes = Arrays.asList(SUPPORTED_ELEMENTS.get(elementName)); + int index = 0; + String attributeName = attributes.getQName(index); + while (attributeName != null) { + if (!supportedAttributes.contains(attributeName)) { + String message = "Unsupported attribute \"{0}\" for element \"{1}\" in markup text"; + message = MessageFormat.format(message, new Object[] { attributeName, elementName }); + throw new IllegalArgumentException(message); + } + index++; + attributeName = attributes.getQName(index); + } + } + } + + private static void checkMandatoryAttributes(String elementName, Attributes attributes) { + checkIntAttribute(elementName, attributes, "img", "width"); + checkIntAttribute(elementName, attributes, "img", "height"); + } + + private static void checkIntAttribute(String elementName, Attributes attributes, String checkedElementName, + String checkedAttributeName) { + if (checkedElementName.equals(elementName)) { + String attribute = attributes.getValue(checkedAttributeName); + try { + Integer.parseInt(attribute); + } catch (NumberFormatException exception) { + String message = "Mandatory attribute \"{0}\" for element \"{1}\" is missing or not a valid integer"; + Object[] arguments = new Object[] { checkedAttributeName, checkedElementName }; + message = MessageFormat.format(message, arguments); + throw new IllegalArgumentException(message); + } + } + } + + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java new file mode 100644 index 000000000..5f954c1c4 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/forms/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS forms, based on SWT/JFace. */ +package org.argeo.cms.ui.forms; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java new file mode 100644 index 000000000..d9c1c1221 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java @@ -0,0 +1,524 @@ +package org.argeo.cms.ui.fs; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.spi.FileSystemProvider; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.Session; + +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider; +import org.argeo.eclipse.ui.fs.FsTableViewer; +import org.argeo.eclipse.ui.fs.FsUiConstants; +import org.argeo.eclipse.ui.fs.FsUiUtils; +import org.argeo.eclipse.ui.fs.NioFileLabelProvider; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.layout.RowLayout; +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.Table; +import org.eclipse.swt.widgets.Text; + +/** + * Default CMS browser composite: a sashForm layout with bookmarks at the left + * hand side, a simple table in the middle and an overview at right hand side. + */ +public class CmsFsBrowser extends Composite { + // private final static Log log = LogFactory.getLog(CmsFsBrowser.class); + private static final long serialVersionUID = -40347919096946585L; + + private final FileSystemProvider nodeFileSystemProvider; + private final Node currentBaseContext; + + // UI Parts for the browser + private Composite leftPannelCmp; + private Composite filterCmp; + private Text filterTxt; + private FsTableViewer directoryDisplayViewer; + private Composite rightPannelCmp; + + private FsContextMenu contextMenu; + + // Local context (this composite is state full) + private Path initialPath; + private Path currDisplayedFolder; + private Path currSelected; + + // local variables (to be cleaned) + private int bookmarkColWith = 500; + + /* + * WARNING: unfinalised implementation of the mechanism to retrieve base + * paths + */ + + private final static String NODE_PREFIX = "node://"; + + private String getCurrentHomePath() { + Session session = null; + try { + Repository repo = currentBaseContext.getSession().getRepository(); + session = CurrentUser.tryAs(() -> repo.login()); + String homepath = CmsJcrUtils.getUserHome(session).getPath(); + return homepath; + } catch (Exception e) { + throw new CmsException("Cannot retrieve Current User Home Path", e); + } finally { + JcrUtils.logoutQuietly(session); + } + } + + protected Path[] getMyFilesPath() { + // return Paths.get(System.getProperty("user.dir")); + String currHomeUriStr = NODE_PREFIX + getCurrentHomePath(); + try { + URI uri = new URI(currHomeUriStr); + FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); + if (fileSystem == null) { + PrivilegedExceptionAction pea = new PrivilegedExceptionAction() { + @Override + public FileSystem run() throws Exception { + return nodeFileSystemProvider.newFileSystem(uri, null); + } + + }; + fileSystem = CurrentUser.tryAs(pea); + } + Path[] paths = { fileSystem.getPath(getCurrentHomePath()), fileSystem.getPath("/") }; + return paths; + } catch (URISyntaxException | PrivilegedActionException e) { + throw new RuntimeException("unable to initialise home file system for " + currHomeUriStr, e); + } + } + + private Path[] getMyGroupsFilesPath() { + // TODO + Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp") }; + return paths; + } + + private Path[] getMyBookmarks() { + // TODO + Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp"), Paths.get("/opt") }; + return paths; + } + + /* End of warning */ + + public CmsFsBrowser(Composite parent, int style, Node context, FileSystemProvider fileSystemProvider) { + super(parent, style); + this.nodeFileSystemProvider = fileSystemProvider; + this.currentBaseContext = context; + + this.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + SashForm form = new SashForm(this, SWT.HORIZONTAL); + + leftPannelCmp = new Composite(form, SWT.NO_FOCUS); + // Bookmarks are still static + populateBookmarks(leftPannelCmp); + + Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS); + createDisplay(centerCmp); + + rightPannelCmp = new Composite(form, SWT.NO_FOCUS); + + form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + form.setWeights(new int[] { 15, 40, 20 }); + } + + void refresh() { + modifyFilter(false); + // also refresh bookmarks and groups + } + + private void createDisplay(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // top filter + filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); + addFilterPanel(filterCmp); + + // Main display + directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); + List colDefs = new ArrayList<>(); + colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 250)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), + "Last modified", 400)); + final Table table = directoryDisplayViewer.configureDefaultTable(colDefs); + table.setLayoutData(EclipseUiUtils.fillAll()); + + // table.addKeyListener(new KeyListener() { + // private static final long serialVersionUID = -8083424284436715709L; + // + // @Override + // public void keyReleased(KeyEvent e) { + // } + // + // @Override + // public void keyPressed(KeyEvent e) { + // if (log.isDebugEnabled()) + // log.debug("Key event received: " + e.keyCode); + // IStructuredSelection selection = (IStructuredSelection) + // directoryDisplayViewer.getSelection(); + // Path selected = null; + // if (!selection.isEmpty()) + // selected = ((Path) selection.getFirstElement()); + // if (e.keyCode == SWT.CR) { + // if (!Files.isDirectory(selected)) + // return; + // if (selected != null) { + // currDisplayedFolder = selected; + // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); + // } + // } else if (e.keyCode == SWT.BS) { + // currDisplayedFolder = currDisplayedFolder.getParent(); + // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); + // directoryDisplayViewer.getTable().setFocus(); + // } + // } + // }); + + directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (selection.isEmpty()) + setSelected(null); + else + selected = ((Path) selection.getFirstElement()); + if (selected != null) { + // TODO manage multiple selection + setSelected(selected); + } + } + }); + + directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { + @Override + public void doubleClick(DoubleClickEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (selected != null) { + if (!Files.isDirectory(selected)) + return; + setInput(selected); + } + } + }); + + // The context menu + contextMenu = new FsContextMenu(this); + + table.addMouseListener(new MouseAdapter() { + private static final long serialVersionUID = 6737579410648595940L; + + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 3) { + // contextMenu.setCurrFolderPath(currDisplayedFolder); + contextMenu.show(table, new Point(e.x, e.y), currDisplayedFolder); + } + } + }); + } + + private void addPathElementBtn(Path path) { + Button elemBtn = new Button(filterCmp, SWT.PUSH); + String nameStr; + if (path.toString().equals("/")) + nameStr = "[jcr:root]"; + else + nameStr = path.getFileName().toString(); + elemBtn.setText(nameStr + " >> "); + CmsSwtUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); + elemBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -4103695476023480651L; + + @Override + public void widgetSelected(SelectionEvent e) { + setInput(path); + } + }); + } + + public void setInput(Path path) { + if (path.equals(currDisplayedFolder)) + return; + currDisplayedFolder = path; + + Path diff = initialPath.relativize(currDisplayedFolder); + + for (Control child : filterCmp.getChildren()) + if (!child.equals(filterTxt)) + child.dispose(); + + addPathElementBtn(initialPath); + Path currTarget = initialPath; + if (!diff.toString().equals("")) + for (Path pathElem : diff) { + currTarget = currTarget.resolve(pathElem); + addPathElementBtn(currTarget); + } + + filterTxt.setText(""); + filterTxt.moveBelow(null); + setSelected(null); + filterCmp.getParent().layout(true, true); + } + + private void setSelected(Path path) { + currSelected = path; + setOverviewInput(path); + } + + public Viewer getViewer() { + return directoryDisplayViewer; + } + + private void populateBookmarks(Composite parent) { + CmsSwtUtils.clear(parent); + parent.setLayout(new GridLayout()); + ISelectionChangedListener selList = new BookmarksSelChangeListener(); + + FsTableViewer homeViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + Table table = homeViewer.configureDefaultSingleColumnTable(bookmarkColWith); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + homeViewer.addSelectionChangedListener(selList); + homeViewer.setPathsInput(getMyFilesPath()); + + appendTitle(parent, "Shared files"); + FsTableViewer groupsViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + table = groupsViewer.configureDefaultSingleColumnTable(bookmarkColWith); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + groupsViewer.addSelectionChangedListener(selList); + groupsViewer.setPathsInput(getMyGroupsFilesPath()); + + appendTitle(parent, "My bookmarks"); + FsTableViewer bookmarksViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + table = bookmarksViewer.configureDefaultSingleColumnTable(bookmarkColWith); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + bookmarksViewer.addSelectionChangedListener(selList); + bookmarksViewer.setPathsInput(getMyBookmarks()); + } + + /** + * Recreates the content of the box that displays information about the + * current selected Path. + */ + private void setOverviewInput(Path path) { + try { + EclipseUiUtils.clear(rightPannelCmp); + rightPannelCmp.setLayout(new GridLayout()); + if (path != null) { + // if (isImg(context)) { + // EditableImage image = new Img(parent, RIGHT, context, + // imageWidth); + // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, + // true, false, + // 2, 1)); + // } + + Label contextL = new Label(rightPannelCmp, SWT.NONE); + contextL.setText(path.getFileName().toString()); + contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp)); + addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString()); + // addProperty(rightPannelCmp, "Owner", + // Files.getOwner(path).getName()); + if (Files.isDirectory(path)) { + addProperty(rightPannelCmp, "Type", "Folder"); + } else { + String mimeType = Files.probeContentType(path); + if (EclipseUiUtils.isEmpty(mimeType)) + mimeType = "Unknown"; + addProperty(rightPannelCmp, "Type", mimeType); + addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); + } + } + rightPannelCmp.layout(true, true); + } catch (IOException e) { + throw new CmsException("Cannot display details for " + path.toString(), e); + } + } + + private void addFilterPanel(Composite parent) { + RowLayout rl = new RowLayout(SWT.HORIZONTAL); + rl.wrap = true; + parent.setLayout(rl); + // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, + // false))); + + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Search current folder"); + filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT)); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 1L; + + public void modifyText(ModifyEvent event) { + modifyFilter(false); + } + }); + filterTxt.addKeyListener(new KeyListener() { + private static final long serialVersionUID = 2533535233583035527L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + // boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; + // // boolean altPressed = (e.stateMask & SWT.ALT) != 0; + // FilterEntitiesVirtualTable currTable = null; + // if (currEdited != null) { + // FilterEntitiesVirtualTable table = + // browserCols.get(currEdited); + // if (table != null && !table.isDisposed()) + // currTable = table; + // } + // + // if (e.keyCode == SWT.ARROW_DOWN) + // currTable.setFocus(); + // else if (e.keyCode == SWT.BS) { + // if (filterTxt.getText().equals("") + // && !(currEdited.getNameCount() == 1 || + // currEdited.equals(initialPath))) { + // Path oldEdited = currEdited; + // Path parentPath = currEdited.getParent(); + // setEdited(parentPath); + // if (browserCols.containsKey(parentPath)) + // browserCols.get(parentPath).setSelected(oldEdited); + // filterTxt.setFocus(); + // e.doit = false; + // } + // } else if (e.keyCode == SWT.TAB && !shiftPressed) { + // Path uniqueChild = getOnlyChild(currEdited, + // filterTxt.getText()); + // if (uniqueChild != null) { + // // Highlight the unique chosen child + // currTable.setSelected(uniqueChild); + // setEdited(uniqueChild); + // } + // filterTxt.setFocus(); + // e.doit = false; + // } + } + }); + } + + private Path getOnlyChild(Path parent, String filter) { + try (DirectoryStream stream = Files.newDirectoryStream(currDisplayedFolder, filter + "*")) { + Path uniqueChild = null; + boolean moreThanOne = false; + loop: for (Path entry : stream) { + if (uniqueChild == null) { + uniqueChild = entry; + } else { + moreThanOne = true; + break loop; + } + } + if (!moreThanOne) + return uniqueChild; + return null; + } catch (IOException ioe) { + throw new CmsException( + "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, + ioe); + } + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currDisplayedFolder != null) { + String filter = filterTxt.getText() + "*"; + directoryDisplayViewer.setInput(currDisplayedFolder, filter); + } + } + + private class BookmarksSelChangeListener implements ISelectionChangedListener { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currDisplayedFolder) && newSelected.equals(initialPath)) + return; + initialPath = newSelected; + setInput(newSelected); + } + } + } + + // Simplify UI implementation + private void addProperty(Composite parent, String propName, String value) { + Label contextL = new Label(parent, SWT.NONE); + contextL.setText(propName + ": " + value); + } + + private Label appendTitle(Composite parent, String value) { + Label titleLbl = new Label(parent, SWT.NONE); + titleLbl.setText(value); + titleLbl.setFont(EclipseUiUtils.getBoldFont(parent)); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 5; + gd.verticalIndent = 5; + titleLbl.setLayoutData(gd); + return titleLbl; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java new file mode 100644 index 000000000..e875b5a3d --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FileDrop.java @@ -0,0 +1,37 @@ +package org.argeo.cms.ui.fs; + +import java.io.IOException; +import java.io.InputStream; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.specific.FileDropAdapter; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.widgets.Control; + +/** Allows a control to receive file drops. */ +public class FileDrop { + private final static CmsLog log = CmsLog.getLog(FileDrop.class); + + public void createDropTarget(Control control) { + FileDropAdapter fileDropAdapter = new FileDropAdapter() { + @Override + protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { + if (log.isDebugEnabled()) + log.debug("Process upload of " + fileName + " (" + contentType + ")"); + processFileUpload(in, fileName, contentType); + } + }; + DropTarget dropTarget = new DropTarget(control, DND.DROP_MOVE | DND.DROP_COPY); + fileDropAdapter.prepareDropTarget(control, dropTarget); + } + + public void handleFileDrop(Control control, DropTargetEvent event) { + } + + /** Executed in UI thread */ + protected void processFileUpload(InputStream in, String fileName, String contentType) throws IOException { + + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java new file mode 100644 index 000000000..1fb3c2a05 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java @@ -0,0 +1,383 @@ +package org.argeo.cms.ui.fs; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.SingleValue; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** Generic popup context menu to manage NIO Path in a Viewer. */ +public class FsContextMenu extends Shell { + private static final long serialVersionUID = -9120261153509855795L; + + private final static CmsLog log = CmsLog.getLog(FsContextMenu.class); + + // Default known actions + public final static String ACTION_ID_CREATE_FOLDER = "createFolder"; + public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder"; + public final static String ACTION_ID_SHARE_FOLDER = "shareFolder"; + public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder"; + public final static String ACTION_ID_DELETE = "delete"; + public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles"; + public final static String ACTION_ID_OPEN = "open"; + + // Local context + private final CmsFsBrowser browser; + // private final Viewer viewer; + private final static String KEY_ACTION_ID = "actionId"; + private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER, + ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_UPLOAD_FILE, + ACTION_ID_OPEN }; + private Map actionButtons = new HashMap(); + + private Path currFolderPath; + + public FsContextMenu(CmsFsBrowser browser) { // Viewer viewer, Display + // display) { + super(browser.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + this.browser = browser; + setLayout(EclipseUiUtils.noSpaceGridLayout()); + + Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER); + boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + CmsSwtUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX); + createContextMenu(boxCmp); + + addShellListener(new ActionsShellListener()); + } + + protected void createContextMenu(Composite boxCmp) { + ActionsSelListener asl = new ActionsSelListener(); + for (String actionId : DEFAULT_ACTIONS) { + Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD); + btn.setText(getLabel(actionId)); + btn.setLayoutData(EclipseUiUtils.fillWidth()); + CmsSwtUtils.markup(btn); + CmsSwtUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX); + btn.setData(KEY_ACTION_ID, actionId); + btn.addSelectionListener(asl); + actionButtons.put(actionId, btn); + } + } + + protected String getLabel(String actionId) { + switch (actionId) { + case ACTION_ID_CREATE_FOLDER: + return "Create Folder"; + case ACTION_ID_BOOKMARK_FOLDER: + return "Bookmark Folder"; + case ACTION_ID_SHARE_FOLDER: + return "Share Folder"; + case ACTION_ID_DOWNLOAD_FOLDER: + return "Download as zip archive"; + case ACTION_ID_DELETE: + return "Delete"; + case ACTION_ID_UPLOAD_FILE: + return "Upload Files"; + case ACTION_ID_OPEN: + return "Open"; + default: + throw new IllegalArgumentException("Unknown action ID " + actionId); + } + } + + protected void aboutToShow(Control source, Point location) { + IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); + boolean emptySel = true; + boolean multiSel = false; + boolean isFolder = true; + if (selection != null && !selection.isEmpty()) { + emptySel = false; + multiSel = selection.size() > 1; + if (!multiSel && selection.getFirstElement() instanceof Path) { + isFolder = Files.isDirectory((Path) selection.getFirstElement()); + } + } + if (emptySel) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } else if (multiSel) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } else if (isFolder) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER); + } else { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_OPEN, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } + } + + private void setVisible(boolean visible, String... buttonIds) { + for (String id : buttonIds) { + Button button = actionButtons.get(id); + button.setVisible(visible); + GridData gd = (GridData) button.getLayoutData(); + gd.heightHint = visible ? SWT.DEFAULT : 0; + } + } + + public void show(Control source, Point location, Path currFolderPath) { + if (isVisible()) + setVisible(false); + // TODO find a better way to retrieve the parent path (cannot be deduced + // from table content because it will fail on an empty folder) + this.currFolderPath = currFolderPath; + aboutToShow(source, location); + pack(); + layout(); + if (source instanceof Control) + setLocation(((Control) source).toDisplay(location.x, location.y)); + open(); + } + + class StyleButton extends Label { + private static final long serialVersionUID = 7731102609123946115L; + + public StyleButton(Composite parent, int swtStyle) { + super(parent, swtStyle); + } + + } + + // class ActionsMouseListener extends MouseAdapter { + // private static final long serialVersionUID = -1041871937815812149L; + // + // @Override + // public void mouseDown(MouseEvent e) { + // Object eventSource = e.getSource(); + // if (e.button == 1) { + // if (eventSource instanceof Button) { + // Button pressedBtn = (Button) eventSource; + // String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); + // switch (actionId) { + // case ACTION_ID_CREATE_FOLDER: + // createFolder(); + // break; + // case ACTION_ID_DELETE: + // deleteItems(); + // break; + // default: + // throw new IllegalArgumentException("Unimplemented action " + actionId); + // // case ACTION_ID_SHARE_FOLDER: + // // return "Share Folder"; + // // case ACTION_ID_DOWNLOAD_FOLDER: + // // return "Download as zip archive"; + // // case ACTION_ID_UPLOAD_FILE: + // // return "Upload Files"; + // // case ACTION_ID_OPEN: + // // return "Open"; + // } + // } + // } + // viewer.getControl().setFocus(); + // // setVisible(false); + // } + // } + + class ActionsSelListener extends SelectionAdapter { + private static final long serialVersionUID = -1041871937815812149L; + + @Override + public void widgetSelected(SelectionEvent e) { + Object eventSource = e.getSource(); + if (eventSource instanceof Button) { + Button pressedBtn = (Button) eventSource; + String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); + switch (actionId) { + case ACTION_ID_CREATE_FOLDER: + createFolder(); + break; + case ACTION_ID_DELETE: + deleteItems(); + break; + case ACTION_ID_OPEN: + openFile(); + break; + case ACTION_ID_UPLOAD_FILE: + uploadFiles(); + break; + default: + throw new IllegalArgumentException("Unimplemented action " + actionId); + // case ACTION_ID_SHARE_FOLDER: + // return "Share Folder"; + // case ACTION_ID_DOWNLOAD_FOLDER: + // return "Download as zip archive"; + // case ACTION_ID_OPEN: + // return "Open"; + } + } + browser.setFocus(); + // viewer.getControl().setFocus(); + // setVisible(false); + + } + } + + class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter { + private static final long serialVersionUID = -5092341449523150827L; + + @Override + public void shellDeactivated(ShellEvent e) { + setVisible(false); + } + } + + private void openFile() { + log.warn("Implement single sourced, workbench independant \"Open File\" action"); + } + + private void deleteItems() { + IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); + if (selection.isEmpty()) + return; + + StringBuilder builder = new StringBuilder(); + @SuppressWarnings("unchecked") + Iterator iterator = selection.iterator(); + List paths = new ArrayList<>(); + + while (iterator.hasNext()) { + Path path = (Path) iterator.next(); + builder.append(path.getFileName() + ", "); + paths.add(path); + } + String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) + + ". Are you sure?"; + if (MessageDialog.openConfirm(this, "Confirm deletion", msg)) { + for (Path path : paths) { + try { + // Might have already been deleted if we are in a tree + Files.deleteIfExists(path); + } catch (IOException e) { + throw new CmsException("Cannot delete path " + path, e); + } + } + browser.refresh(); + } + } + + private void createFolder() { + String msg = "Please provide a name."; + String name = SingleValue.ask("Create folder", msg); + // TODO enhance check of name validity + if (EclipseUiUtils.notEmpty(name)) { + try { + Path child = currFolderPath.resolve(name); + if (Files.exists(child)) + throw new CmsException("An item with name " + name + " already exists at " + + currFolderPath.toString() + ", cannot create"); + else + Files.createDirectories(child); + browser.refresh(); + } catch (IOException e) { + throw new CmsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e); + } + } + } + + private void uploadFiles() { + try { + FileDialog dialog = new FileDialog(browser.getShell(), SWT.MULTI); + dialog.setText("Choose one or more files to upload"); + + if (EclipseUiUtils.notEmpty(dialog.open())) { + String[] names = dialog.getFileNames(); + // Workaround small differences between RAP and RCP + // 1. returned names are absolute path on RAP and + // relative in RCP + // 2. in RCP we must use getFilterPath that does not + // exists on RAP + Method filterMethod = null; + Path parPath = null; + try { + filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath"); + String filterPath = (String) filterMethod.invoke(dialog); + parPath = Paths.get(filterPath); + } catch (NoSuchMethodException nsme) { // RAP + } + if (names.length == 0) + return; + else { + loop: for (String name : names) { + Path tmpPath = Paths.get(name); + if (parPath != null) + tmpPath = parPath.resolve(tmpPath); + if (Files.exists(tmpPath)) { + URI uri = tmpPath.toUri(); + String uriStr = uri.toString(); + + if (Files.isDirectory(tmpPath)) { + MessageDialog.openError(browser.getShell(), "Unimplemented directory import", + "Upload of directories in the system is not yet implemented"); + continue loop; + } + Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString()); + InputStream in = null; + try { + in = new ByteArrayInputStream(Files.readAllBytes(tmpPath)); + Files.copy(in, targetPath); + Files.delete(tmpPath); + } finally { + IOUtils.closeQuietly(in); + } + if (log.isDebugEnabled()) + log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString()); + } else { + String msg = "Cannot copy tmp file from " + tmpPath.toString(); + if (parPath != null) + msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS"; + MessageDialog.openError(browser.getShell(), "Missing file", msg); + continue loop; + } + } + browser.refresh(); + } + } + } catch (Exception e) { + e.printStackTrace(); + MessageDialog.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath); + } + } + + public void setCurrFolderPath(Path currFolderPath) { + this.currFolderPath = currFolderPath; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java new file mode 100644 index 000000000..9ae319282 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/FsStyles.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.fs; + +/** FS Ui specific CSS styles */ +public interface FsStyles { + String BREAD_CRUMB_BTN = "breadCrumb_btn"; + String CONTEXT_MENU_BOX = "contextMenu_box"; + String BUTTON_SUFFIX = "_btn"; +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java new file mode 100644 index 000000000..6a6c27286 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/fs/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace file system components. */ +package org.argeo.cms.ui.fs; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java new file mode 100644 index 000000000..e10da3aed --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/Activator.java @@ -0,0 +1,37 @@ +package org.argeo.cms.ui.internal; + +import org.argeo.api.cms.CmsState; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +public class Activator implements BundleActivator { + + // avoid dependency to RWT OSGi + private final static String CONTEXT_NAME_PROP = "contextName"; + + private static ServiceTracker nodeState; + + // @Override + public void start(BundleContext bc) throws Exception { + // UI +// bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(), +// LangUtils.dico(CONTEXT_NAME_PROP, "system")); +// bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.dico(CONTEXT_NAME_PROP, "user")); + + nodeState = new ServiceTracker<>(bc, CmsState.class, null); + nodeState.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + if (nodeState != null) { + nodeState.close(); + nodeState = null; + } + } + + public static CmsState getNodeState() { + return nodeState.getService(); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java new file mode 100644 index 000000000..ea0abdf5d --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java @@ -0,0 +1,81 @@ +package org.argeo.cms.ui.internal; + +import java.util.ArrayList; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsException; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +@Deprecated +class JcrContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -1333678161322488674L; + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput == null) + return; + if (!(newInput instanceof Node)) + throw new CmsException("Input " + newInput + " must be a node"); + } + + @Override + public Object[] getElements(Object inputElement) { + try { + Node node = (Node) inputElement; + ArrayList arr = new ArrayList(); + NodeIterator nit = node.getNodes(); + while (nit.hasNext()) { + arr.add(nit.nextNode()); + } + return arr.toArray(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } + + @Override + public Object[] getChildren(Object parentElement) { + try { + Node node = (Node) parentElement; + ArrayList arr = new ArrayList(); + NodeIterator nit = node.getNodes(); + while (nit.hasNext()) { + arr.add(nit.nextNode()); + } + return arr.toArray(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } + + @Override + public Object getParent(Object element) { + try { + Node node = (Node) element; + if (node.getName().equals("")) + return null; + else + return node.getParent(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } + + @Override + public boolean hasChildren(Object element) { + try { + Node node = (Node) element; + return node.hasNodes(); + } catch (RepositoryException e) { + throw new CmsException("Cannot get elements", e); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java new file mode 100644 index 000000000..60bb42b46 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java @@ -0,0 +1,74 @@ +package org.argeo.cms.ui.internal; + +import static javax.jcr.nodetype.NodeType.NT_FILE; + +import java.io.IOException; +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.FilenameUtils; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.cms.ui.widgets.Img; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.fileupload.FileDetails; +import org.eclipse.rap.fileupload.FileUploadReceiver; + +public class JcrFileUploadReceiver extends FileUploadReceiver { + private Img img; + private final Node parentNode; + private final String nodeName; + private final CmsImageManager imageManager; + + /** If nodeName is null, use the uploaded file name */ + public JcrFileUploadReceiver(Img img, Node parentNode, String nodeName, CmsImageManager imageManager) { + super(); + this.img = img; + this.parentNode = parentNode; + this.nodeName = nodeName; + this.imageManager = imageManager; + } + + @Override + public void receive(InputStream stream, FileDetails details) throws IOException { + try { + String fileName = nodeName != null ? nodeName : details.getFileName(); + String contentType = details.getContentType(); + if (isImage(details.getFileName(), contentType)) { + imageManager.uploadImage(img.getNode(),parentNode, fileName, stream, contentType); + return; + } + + Node fileNode; + if (parentNode.hasNode(fileName)) { + fileNode = parentNode.getNode(fileName); + if (!fileNode.isNodeType(NT_FILE)) + fileNode.remove(); + } + fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream); + + if (contentType != null) { + fileNode.addMixin(NodeType.MIX_MIMETYPE); + fileNode.setProperty(Property.JCR_MIMETYPE, contentType); + } + processNewFile(fileNode); + fileNode.getSession().save(); + } catch (RepositoryException e) { + throw new JcrException("Cannot receive " + details, e); + } + } + + protected Boolean isImage(String fileName, String contentType) { + String ext = FilenameUtils.getExtension(fileName); + return ext != null && (ext.equals("png") || ext.equalsIgnoreCase("jpg")); + } + + protected void processNewFile(Node node) { + + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java new file mode 100644 index 000000000..6162a74bf --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java @@ -0,0 +1,74 @@ +package org.argeo.cms.ui.internal; + +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.argeo.cms.ui.widgets.EditableImage; +import org.argeo.cms.ux.AbstractImageManager; +import org.argeo.cms.ux.CmsUxUtils; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Text; + +/** NOT working yet. */ +public class SimpleEditableImage extends EditableImage { + private static final long serialVersionUID = -5689145523114022890L; + + private String src; + private Cms2DSize imageSize; + + public SimpleEditableImage(Composite parent, int swtStyle) { + super(parent, swtStyle); + // load(getControl()); + getParent().layout(); + } + + public SimpleEditableImage(Composite parent, int swtStyle, String src, Cms2DSize imageSize) { + super(parent, swtStyle); + this.src = src; + this.imageSize = imageSize; + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) { + return createText(box, style); + } else { + return createLabel(box, style); + } + } + + protected String createImgTag() throws RepositoryException { + String imgTag; + if (src != null) + imgTag = CmsUxUtils.img(src, imageSize); + else + imgTag = CmsUiUtils.noImg(imageSize != null ? imageSize : AbstractImageManager.NO_IMAGE_SIZE); + return imgTag; + } + + protected Text createText(Composite box, String style) { + Text text = new Text(box, getStyle()); + CmsSwtUtils.style(text, style); + return text; + } + + public String getSrc() { + return src; + } + + public void setSrc(String src) { + this.src = src; + } + + public Cms2DSize getImageSize() { + return imageSize; + } + + public void setImageSize(Cms2DSize imageSize) { + this.imageSize = imageSize; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java new file mode 100644 index 000000000..380634118 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java @@ -0,0 +1,75 @@ +package org.argeo.cms.ui.jcr; + +import java.util.Collections; +import java.util.Map; +import java.util.Observable; +import java.util.TreeMap; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; + +public class DefaultRepositoryRegister extends Observable implements RepositoryRegister { + /** Key for a JCR repository alias */ + private final static String CN = CmsConstants.CN; + /** Key for a JCR repository URI */ + // public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri"; + private final static CmsLog log = CmsLog.getLog(DefaultRepositoryRegister.class); + + /** Read only map which will be directly exposed. */ + private Map repositories = Collections.unmodifiableMap(new TreeMap()); + + @SuppressWarnings("rawtypes") + public synchronized Repository getRepository(Map parameters) throws RepositoryException { + if (!parameters.containsKey(CN)) + throw new RepositoryException("Parameter " + CN + " has to be defined."); + String alias = parameters.get(CN).toString(); + if (!repositories.containsKey(alias)) + throw new RepositoryException("No repository registered with alias " + alias); + + return repositories.get(alias); + } + + /** Access to the read-only map */ + public synchronized Map getRepositories() { + return repositories; + } + + /** Registers a service, typically called when OSGi services are bound. */ + @SuppressWarnings("rawtypes") + public synchronized void register(Repository repository, Map properties) { + String alias; + if (properties == null || !properties.containsKey(CN)) { + log.warn("Cannot register a repository if no " + CN + " property is specified."); + return; + } + alias = properties.get(CN).toString(); + Map map = new TreeMap(repositories); + map.put(alias, repository); + repositories = Collections.unmodifiableMap(map); + setChanged(); + notifyObservers(alias); + } + + /** Unregisters a service, typically called when OSGi services are unbound. */ + @SuppressWarnings("rawtypes") + public synchronized void unregister(Repository repository, Map properties) { + // TODO: also check bean name? + if (properties == null || !properties.containsKey(CN)) { + log.warn("Cannot unregister a repository without property " + CN); + return; + } + + String alias = properties.get(CN).toString(); + Map map = new TreeMap(repositories); + if (map.remove(alias) == null) { + log.warn("No repository was registered with alias " + alias); + return; + } + repositories = Collections.unmodifiableMap(map); + setChanged(); + notifyObservers(alias); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java new file mode 100644 index 000000000..0f7ee7735 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java @@ -0,0 +1,98 @@ +package org.argeo.cms.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.Version; +import javax.jcr.version.VersionHistory; +import javax.jcr.version.VersionIterator; +import javax.jcr.version.VersionManager; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Display some version information of a JCR full versionable node in a tree + * like structure + */ +public class FullVersioningTreeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = 8691772509491211112L; + + /** + * Sends back the first level of the Tree. input element must be a single + * node object + */ + public Object[] getElements(Object inputElement) { + try { + Node rootNode = (Node) inputElement; + String curPath = rootNode.getPath(); + VersionManager vm = rootNode.getSession().getWorkspace() + .getVersionManager(); + + VersionHistory vh = vm.getVersionHistory(curPath); + List result = new ArrayList(); + VersionIterator vi = vh.getAllLinearVersions(); + + while (vi.hasNext()) { + result.add(vi.nextVersion()); + } + return result.toArray(); + } catch (RepositoryException re) { + throw new EclipseUiException( + "Unexpected error while getting version elements", re); + } + } + + public Object[] getChildren(Object parentElement) { + try { + if (parentElement instanceof Version) { + List tmp = new ArrayList(); + tmp.add(((Version) parentElement).getFrozenNode()); + return tmp.toArray(); + } + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while getting child " + + "node for version element", re); + } + return null; + } + + public Object getParent(Object element) { + try { + // this will not work in a simpleVersionning environment, parent is + // not a node. + if (element instanceof Node + && ((Node) element).isNodeType(NodeType.NT_FROZEN_NODE)) { + Node node = (Node) element; + return node.getParent(); + } else + return null; + } catch (RepositoryException e) { + return null; + } + } + + public boolean hasChildren(Object element) { + try { + if (element instanceof Version) + return true; + else if (element instanceof Node) + return ((Node) element).hasNodes(); + else + return false; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check children of " + element, e); + } + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java new file mode 100644 index 000000000..b36acc368 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java @@ -0,0 +1,68 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.argeo.cms.ui.jcr.model.RepositoriesElem; +import org.argeo.cms.ui.jcr.model.RepositoryElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; + +/** Useful methods to manage the JCR Browser */ +public class JcrBrowserUtils { + + public static String getPropertyTypeAsString(Property prop) { + try { + return PropertyType.nameFromValue(prop.getType()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check type for " + prop, e); + } + } + + /** Insure that the UI component is not stale, refresh if needed */ + public static void forceRefreshIfNeeded(TreeParent element) { + Node curNode = null; + + boolean doRefresh = false; + + try { + if (element instanceof SingleJcrNodeElem) { + curNode = ((SingleJcrNodeElem) element).getNode(); + } else if (element instanceof WorkspaceElem) { + curNode = ((WorkspaceElem) element).getRootNode(); + } + + if (curNode != null && element.getChildren().length != curNode.getNodes().getSize()) + doRefresh = true; + else if (element instanceof RepositoryElem) { + RepositoryElem rn = (RepositoryElem) element; + if (rn.isConnected()) { + String[] wkpNames = rn.getAccessibleWorkspaceNames(); + if (element.getChildren().length != wkpNames.length) + doRefresh = true; + } + } else if (element instanceof RepositoriesElem) { + doRefresh = true; + // Always force refresh for RepositoriesElem : the condition + // below does not take remote repository into account and it is + // not trivial to do so. + + // RepositoriesElem rn = (RepositoriesElem) element; + // if (element.getChildren().length != + // rn.getRepositoryRegister() + // .getRepositories().size()) + // doRefresh = true; + } + if (doRefresh) { + element.clearChildren(); + element.getChildren(); + } + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while synchronising the UI with the JCR repository", re); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java new file mode 100644 index 000000000..1707681b4 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java @@ -0,0 +1,60 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.Node; + +import org.argeo.cms.ui.jcr.model.RepositoryElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; + +/** Centralizes the management of double click on a NodeTreeViewer */ +public class JcrDClickListener implements IDoubleClickListener { + // private final static Log log = LogFactory + // .getLog(GenericNodeDoubleClickListener.class); + + private TreeViewer nodeViewer; + + // private JcrFileProvider jfp; + // private FileHandler fileHandler; + + public JcrDClickListener(TreeViewer nodeViewer) { + this.nodeViewer = nodeViewer; + // jfp = new JcrFileProvider(); + // Commented out. see https://www.argeo.org/bugzilla/show_bug.cgi?id=188 + // fileHandler = null; + // fileHandler = new FileHandler(jfp); + } + + public void doubleClick(DoubleClickEvent event) { + if (event.getSelection() == null || event.getSelection().isEmpty()) + return; + Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (obj instanceof RepositoryElem) { + RepositoryElem rpNode = (RepositoryElem) obj; + if (rpNode.isConnected()) { + rpNode.logout(); + } else { + rpNode.login(); + } + nodeViewer.refresh(obj); + } else if (obj instanceof WorkspaceElem) { + WorkspaceElem wn = (WorkspaceElem) obj; + if (wn.isConnected()) + wn.logout(); + else + wn.login(); + nodeViewer.refresh(obj); + } else if (obj instanceof SingleJcrNodeElem) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; + Node node = sjn.getNode(); + openNode(node); + } + } + + protected void openNode(Node node) { + // TODO implement generic behaviour + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java new file mode 100644 index 000000000..d1d1e31ef --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrImages.java @@ -0,0 +1,24 @@ +package org.argeo.cms.ui.jcr; + +import org.argeo.cms.ui.theme.CmsImages; +import org.eclipse.swt.graphics.Image; + +/** Shared icons. */ +public class JcrImages { + public final static Image NODE = CmsImages.createIcon("node.gif"); + public final static Image FOLDER = CmsImages.createIcon("folder.gif"); + public final static Image FILE = CmsImages.createIcon("file.gif"); + public final static Image BINARY = CmsImages.createIcon("binary.png"); + public final static Image HOME = CmsImages.createIcon("person-logged-in.png"); + public final static Image SORT = CmsImages.createIcon("sort.gif"); + public final static Image REMOVE = CmsImages.createIcon("remove.gif"); + + public final static Image REPOSITORIES = CmsImages.createIcon("repositories.gif"); + public final static Image REPOSITORY_DISCONNECTED = CmsImages.createIcon("repository_disconnected.gif"); + public final static Image REPOSITORY_CONNECTED = CmsImages.createIcon("repository_connected.gif"); + public final static Image REMOTE_DISCONNECTED = CmsImages.createIcon("remote_disconnected.gif"); + public final static Image REMOTE_CONNECTED = CmsImages.createIcon("remote_connected.gif"); + public final static Image WORKSPACE_DISCONNECTED = CmsImages.createIcon("workspace_disconnected.png"); + public final static Image WORKSPACE_CONNECTED = CmsImages.createIcon("workspace_connected.png"); + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java new file mode 100644 index 000000000..cc8479f78 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java @@ -0,0 +1,82 @@ +package org.argeo.cms.ui.jcr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Implementation of the {@code ITreeContentProvider} in order to display a + * single JCR node and its children in a tree like structure + */ +public class JcrTreeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -2128326504754297297L; + // private Node rootNode; + private JcrItemsComparator itemComparator = new JcrItemsComparator(); + + /** + * Sends back the first level of the Tree. input element must be a single node + * object + */ + public Object[] getElements(Object inputElement) { + Node rootNode = (Node) inputElement; + return childrenNodes(rootNode); + } + + public Object[] getChildren(Object parentElement) { + return childrenNodes((Node) parentElement); + } + + public Object getParent(Object element) { + try { + Node node = (Node) element; + if (!node.getPath().equals("/")) + return node.getParent(); + else + return null; + } catch (RepositoryException e) { + return null; + } + } + + public boolean hasChildren(Object element) { + try { + return ((Node) element).hasNodes(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check children existence on " + element, e); + } + } + + protected Object[] childrenNodes(Node parentNode) { + try { + List children = new ArrayList(); + NodeIterator nit = parentNode.getNodes(); + while (nit.hasNext()) { + Node node = nit.nextNode(); +// if (node.getName().startsWith("rep:") || node.getName().startsWith("jcr:") +// || node.getName().startsWith("nt:")) +// continue nodes; + children.add(node); + } + Node[] arr = children.toArray(new Node[0]); + Arrays.sort(arr, itemComparator); + return arr; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot list children of " + parentNode, e); + } + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java new file mode 100644 index 000000000..0625cc872 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java @@ -0,0 +1,175 @@ +package org.argeo.cms.ui.jcr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.ui.jcr.model.RepositoriesElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ux.widgets.TreeParent; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Implementation of the {@code ITreeContentProvider} to display multiple + * repository environment in a tree like structure + */ +public class NodeContentProvider implements ITreeContentProvider { + private static final long serialVersionUID = -4083809398848374403L; + final private RepositoryRegister repositoryRegister; + final private RepositoryFactory repositoryFactory; + + // Current user session on the default workspace of the argeo Node + final private Session userSession; + final private Keyring keyring; + private boolean sortChildren; + + // Reference for cleaning + private SingleJcrNodeElem homeNode = null; + private RepositoriesElem repositoriesNode = null; + + // Utils + private TreeBrowserComparator itemComparator = new TreeBrowserComparator(); + + public NodeContentProvider(Session userSession, Keyring keyring, + RepositoryRegister repositoryRegister, + RepositoryFactory repositoryFactory, Boolean sortChildren) { + this.userSession = userSession; + this.keyring = keyring; + this.repositoryRegister = repositoryRegister; + this.repositoryFactory = repositoryFactory; + this.sortChildren = sortChildren; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput == null)// dispose + return; + + if (userSession != null) { + Node userHome = CmsJcrUtils.getUserHome(userSession); + if (userHome != null) { + // TODO : find a way to dynamically get alias for the node + if (homeNode != null) + homeNode.dispose(); + homeNode = new SingleJcrNodeElem(null, userHome, + userSession.getUserID(), CmsConstants.EGO_REPOSITORY); + } + } + if (repositoryRegister != null) { + if (repositoriesNode != null) + repositoriesNode.dispose(); + repositoriesNode = new RepositoriesElem("Repositories", + repositoryRegister, repositoryFactory, null, userSession, + keyring); + } + } + + /** + * Sends back the first level of the Tree. Independent from inputElement + * that can be null + */ + public Object[] getElements(Object inputElement) { + List objs = new ArrayList(); + if (homeNode != null) + objs.add(homeNode); + if (repositoriesNode != null) + objs.add(repositoriesNode); + return objs.toArray(); + } + + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof TreeParent) { + if (sortChildren) { + Object[] tmpArr = ((TreeParent) parentElement).getChildren(); + if (tmpArr == null) + return new Object[0]; + TreeParent[] arr = new TreeParent[tmpArr.length]; + for (int i = 0; i < tmpArr.length; i++) + arr[i] = (TreeParent) tmpArr[i]; + Arrays.sort(arr, itemComparator); + return arr; + } else + return ((TreeParent) parentElement).getChildren(); + } else + return new Object[0]; + } + + /** + * Sets whether the content provider should order the children nodes or not. + * It is user duty to call a full refresh of the tree after changing this + * parameter. + */ + public void setSortChildren(boolean sortChildren) { + this.sortChildren = sortChildren; + } + + public Object getParent(Object element) { + if (element instanceof TreeParent) { + return ((TreeParent) element).getParent(); + } else + return null; + } + + public boolean hasChildren(Object element) { + if (element instanceof RepositoriesElem) { + RepositoryRegister rr = ((RepositoriesElem) element) + .getRepositoryRegister(); + return rr.getRepositories().size() > 0; + } else if (element instanceof TreeParent) { + TreeParent tp = (TreeParent) element; + return tp.hasChildren(); + } + return false; + } + + public void dispose() { + if (homeNode != null) + homeNode.dispose(); + if (repositoriesNode != null) { + // logs out open sessions + // see https://bugzilla.argeo.org/show_bug.cgi?id=23 + repositoriesNode.dispose(); + } + } + + /** + * Specific comparator for this view. See specification here: + * https://www.argeo.org/bugzilla/show_bug.cgi?id=139 + */ + private class TreeBrowserComparator implements Comparator { + + public int category(TreeParent element) { + if (element instanceof SingleJcrNodeElem) { + Node node = ((SingleJcrNodeElem) element).getNode(); + try { + if (node.isNodeType(NodeType.NT_FOLDER)) + return 5; + } catch (RepositoryException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return 10; + } + + public int compare(TreeParent o1, TreeParent o2) { + int cat1 = category(o1); + int cat2 = category(o2); + + if (cat1 != cat2) { + return cat1 - cat2; + } + return o1.getName().compareTo(o2.getName()); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java new file mode 100644 index 000000000..a5751c083 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java @@ -0,0 +1,113 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.NamespaceException; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.ui.jcr.model.RemoteRepositoryElem; +import org.argeo.cms.ui.jcr.model.RepositoriesElem; +import org.argeo.cms.ui.jcr.model.RepositoryElem; +import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; +import org.argeo.cms.ui.jcr.model.WorkspaceElem; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +/** Provides reasonable defaults for know JCR types. */ +public class NodeLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -3662051696443321843L; + + private final static CmsLog log = CmsLog.getLog(NodeLabelProvider.class); + + public String getText(Object element) { + try { + if (element instanceof SingleJcrNodeElem) { + SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; + return getText(sjn.getNode()); + } else if (element instanceof Node) { + return getText((Node) element); + } else + return super.getText(element); + } catch (RepositoryException e) { + throw new EclipseUiException("Unexpected JCR error while getting node name."); + } + } + + protected String getText(Node node) throws RepositoryException { + String label = node.getName(); + StringBuffer mixins = new StringBuffer(""); + for (NodeType type : node.getMixinNodeTypes()) + mixins.append(' ').append(type.getName()); + + return label + " [" + node.getPrimaryNodeType().getName() + mixins + "]"; + } + + @Override + public Image getImage(Object element) { + if (element instanceof RemoteRepositoryElem) { + if (((RemoteRepositoryElem) element).isConnected()) + return JcrImages.REMOTE_CONNECTED; + else + return JcrImages.REMOTE_DISCONNECTED; + } else if (element instanceof RepositoryElem) { + if (((RepositoryElem) element).isConnected()) + return JcrImages.REPOSITORY_CONNECTED; + else + return JcrImages.REPOSITORY_DISCONNECTED; + } else if (element instanceof WorkspaceElem) { + if (((WorkspaceElem) element).isConnected()) + return JcrImages.WORKSPACE_CONNECTED; + else + return JcrImages.WORKSPACE_DISCONNECTED; + } else if (element instanceof RepositoriesElem) { + return JcrImages.REPOSITORIES; + } else if (element instanceof SingleJcrNodeElem) { + Node nodeElem = ((SingleJcrNodeElem) element).getNode(); + return getImage(nodeElem); + + // if (element instanceof Node) { + // return getImage((Node) element); + // } else if (element instanceof WrappedNode) { + // return getImage(((WrappedNode) element).getNode()); + // } else if (element instanceof NodesWrapper) { + // return getImage(((NodesWrapper) element).getNode()); + // } + } + // try { + // return super.getImage(); + // } catch (RepositoryException e) { + // return null; + // } + return super.getImage(element); + } + + protected Image getImage(Node node) { + try { + if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FILE)) + return JcrImages.FILE; + else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) + return JcrImages.FOLDER; + else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_RESOURCE)) + return JcrImages.BINARY; + try { + // TODO check workspace type? + if (node.getDepth() == 1 && node.hasProperty(Property.JCR_ID)) + return JcrImages.HOME; + + // optimizes +// if (node.hasProperty(LdapAttrs.uid.property()) && node.isNodeType(NodeTypes.NODE_USER_HOME)) +// return JcrImages.HOME; + } catch (NamespaceException e) { + // node namespace is not registered in this repo + } + return JcrImages.NODE; + } catch (RepositoryException e) { + log.warn("Error while retrieving type for " + node + " in order to display corresponding image"); + e.printStackTrace(); + return null; + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java new file mode 100644 index 000000000..444350aeb --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java @@ -0,0 +1,52 @@ +package org.argeo.cms.ui.jcr; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Repository; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +public class OsgiRepositoryRegister extends DefaultRepositoryRegister { + private final static BundleContext bc = FrameworkUtil.getBundle(OsgiRepositoryRegister.class).getBundleContext(); + private final ServiceTracker repositoryTracker; + + public OsgiRepositoryRegister() { + repositoryTracker = new ServiceTracker(bc, Repository.class, null) { + + @Override + public Repository addingService(ServiceReference reference) { + + Repository repository = super.addingService(reference); + Map props = new HashMap<>(); + for (String key : reference.getPropertyKeys()) { + props.put(key, reference.getProperty(key)); + } + register(repository, props); + return repository; + } + + @Override + public void removedService(ServiceReference reference, Repository service) { + Map props = new HashMap<>(); + for (String key : reference.getPropertyKeys()) { + props.put(key, reference.getProperty(key)); + } + unregister(service, props); + super.removedService(reference, service); + } + + }; + } + + public void init() { + repositoryTracker.open(); + } + + public void destroy() { + repositoryTracker.close(); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java new file mode 100644 index 000000000..fd544bbd8 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java @@ -0,0 +1,42 @@ +package org.argeo.cms.ui.jcr; + +import java.util.Set; +import java.util.TreeSet; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** Simple content provider that displays all properties of a given Node */ +public class PropertiesContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = 5227554668841613078L; + private JcrItemsComparator itemComparator = new JcrItemsComparator(); + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public Object[] getElements(Object inputElement) { + try { + if (inputElement instanceof Node) { + Set props = new TreeSet(itemComparator); + PropertyIterator pit = ((Node) inputElement).getProperties(); + while (pit.hasNext()) + props.add(pit.nextProperty()); + return props.toArray(); + } + return new Object[] {}; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get element for " + + inputElement, e); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java new file mode 100644 index 000000000..37b90f7ee --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java @@ -0,0 +1,101 @@ +package org.argeo.cms.ui.jcr; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ViewerCell; + +/** Default basic label provider for a given JCR Node's properties */ +public class PropertyLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -5405794508731390147L; + + // To be able to change column order easily + public static final int COLUMN_PROPERTY = 0; + public static final int COLUMN_VALUE = 1; + public static final int COLUMN_TYPE = 2; + public static final int COLUMN_ATTRIBUTES = 3; + + // Utils + protected DateFormat timeFormatter = new SimpleDateFormat(CmsUiConstants.DATE_TIME_FORMAT); + + public void update(ViewerCell cell) { + Object element = cell.getElement(); + cell.setText(getColumnText(element, cell.getColumnIndex())); + } + + public String getColumnText(Object element, int columnIndex) { + try { + if (element instanceof Property) { + Property prop = (Property) element; + if (prop.isMultiple()) { + switch (columnIndex) { + case COLUMN_PROPERTY: + return prop.getName(); + case COLUMN_VALUE: + // Corresponding values are listed on children + return ""; + case COLUMN_TYPE: + return JcrBrowserUtils.getPropertyTypeAsString(prop); + case COLUMN_ATTRIBUTES: + return JcrUtils.getPropertyDefinitionAsString(prop); + } + } else { + switch (columnIndex) { + case COLUMN_PROPERTY: + return prop.getName(); + case COLUMN_VALUE: + return formatValueAsString(prop.getValue()); + case COLUMN_TYPE: + return JcrBrowserUtils.getPropertyTypeAsString(prop); + case COLUMN_ATTRIBUTES: + return JcrUtils.getPropertyDefinitionAsString(prop); + } + } + } else if (element instanceof Value) { + Value val = (Value) element; + switch (columnIndex) { + case COLUMN_PROPERTY: + // Nothing to show + return ""; + case COLUMN_VALUE: + return formatValueAsString(val); + case COLUMN_TYPE: + // listed on the parent + return ""; + case COLUMN_ATTRIBUTES: + // Corresponding attributes are listed on the parent + return ""; + } + } + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot retrieve prop value on " + element, re); + } + return null; + } + + private String formatValueAsString(Value value) { + // TODO enhance this method + try { + String strValue; + + if (value.getType() == PropertyType.BINARY) + strValue = ""; + else if (value.getType() == PropertyType.DATE) + strValue = timeFormatter.format(value.getDate().getTime()); + else + strValue = value.getString(); + return strValue; + } catch (RepositoryException e) { + throw new EclipseUiException("unexpected error while formatting value", e); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java new file mode 100644 index 000000000..802c75619 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java @@ -0,0 +1,16 @@ +package org.argeo.cms.ui.jcr; + +import java.util.Map; + +import javax.jcr.Repository; +import javax.jcr.RepositoryFactory; + +/** Allows to register repositories by name. */ +public interface RepositoryRegister extends RepositoryFactory { + /** + * The registered {@link Repository} as a read-only map. Note that this + * method should be called for each access in order to be sure to be up to + * date in case repositories have registered/unregistered + */ + public Map getRepositories(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java new file mode 100644 index 000000000..37dfe2b8f --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java @@ -0,0 +1,33 @@ +package org.argeo.cms.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.version.Version; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** + * Simple wrapping of the ColumnLabelProvider class to provide text display in + * order to build a tree for version. The getText() method does not assume that + * {@link Version} extends {@link Node} class to respect JCR 2.0 specification + * + */ +public class VersionLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = 5270739851193688238L; + + public String getText(Object element) { + try { + if (element instanceof Version) { + Version version = (Version) element; + return version.getName(); + } else if (element instanceof Node) { + return ((Node) element).getName(); + } + } catch (RepositoryException re) { + throw new EclipseUiException( + "Unexpected error while getting element name", re); + } + return super.getText(element); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java new file mode 100644 index 000000000..d33b33f63 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java @@ -0,0 +1,21 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.Repository; + +import org.argeo.cms.ux.widgets.TreeParent; + +/** Wrap a MaintainedRepository */ +public class MaintainedRepositoryElem extends RepositoryElem { + + public MaintainedRepositoryElem(String alias, Repository repository, TreeParent parent) { + super(alias, repository, parent); + // if (!(repository instanceof MaintainedRepository)) { + // throw new ArgeoException("Repository " + alias + // + " is not a maintained repository"); + // } + } + + // protected MaintainedRepository getMaintainedRepository() { + // return (MaintainedRepository) getRepository(); + // } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java new file mode 100644 index 000000000..908d1b135 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java @@ -0,0 +1,76 @@ +package org.argeo.cms.ui.jcr.model; + +import java.util.Arrays; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; + +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; + +/** Root of a remote repository */ +public class RemoteRepositoryElem extends RepositoryElem { + private final Keyring keyring; + /** + * A session of the logged in user on the default workspace of the node + * repository. + */ + private final Session userSession; + private final String remoteNodePath; + + private final RepositoryFactory repositoryFactory; + private final String uri; + + public RemoteRepositoryElem(String alias, RepositoryFactory repositoryFactory, String uri, TreeParent parent, + Session userSession, Keyring keyring, String remoteNodePath) { + super(alias, null, parent); + this.repositoryFactory = repositoryFactory; + this.uri = uri; + this.keyring = keyring; + this.userSession = userSession; + this.remoteNodePath = remoteNodePath; + } + + @Override + protected Session repositoryLogin(String workspaceName) throws RepositoryException { + Node remoteRepository = userSession.getNode(remoteNodePath); + String userID = remoteRepository.getProperty(ArgeoNames.ARGEO_USER_ID).getString(); + if (userID.trim().equals("")) { + return getRepository().login(workspaceName); + } else { + String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; + char[] password = keyring.getAsChars(pwdPath); + try { + SimpleCredentials credentials = new SimpleCredentials(userID, password); + return getRepository().login(credentials, workspaceName); + } finally { + Arrays.fill(password, 0, password.length, ' '); + } + } + } + + @Override + public Repository getRepository() { + if (repository == null) + repository = CmsJcrUtils.getRepositoryByUri(repositoryFactory, uri); + return super.getRepository(); + } + + public void remove() { + try { + Node remoteNode = userSession.getNode(remoteNodePath); + remoteNode.remove(); + remoteNode.getSession().save(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot remove " + remoteNodePath, e); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java new file mode 100644 index 000000000..742800b0b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java @@ -0,0 +1,112 @@ +package org.argeo.cms.ui.jcr.model; + +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.RepositoryFactory; +import javax.jcr.Session; + +import org.argeo.cms.ArgeoNames; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.security.Keyring; +import org.argeo.cms.ui.jcr.RepositoryRegister; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.dialogs.ErrorFeedback; + +/** + * UI Tree component that implements the Argeo abstraction of a + * {@link RepositoryFactory} that enable a user to "mount" various repositories + * in a single Tree like View. It is usually meant to be at the root of the UI + * Tree and thus {@link getParent()} method will return null. + * + * The {@link RepositoryFactory} is injected at instantiation time and must be + * use get or register new {@link Repository} objects upon which a reference is + * kept here. + */ + +public class RepositoriesElem extends TreeParent implements ArgeoNames { + private final RepositoryRegister repositoryRegister; + private final RepositoryFactory repositoryFactory; + + /** + * A session of the logged in user on the default workspace of the node + * repository. + */ + private final Session userSession; + private final Keyring keyring; + + public RepositoriesElem(String name, RepositoryRegister repositoryRegister, RepositoryFactory repositoryFactory, + TreeParent parent, Session userSession, Keyring keyring) { + super(name); + this.repositoryRegister = repositoryRegister; + this.repositoryFactory = repositoryFactory; + this.userSession = userSession; + this.keyring = keyring; + } + + /** + * Override normal behavior to initialize the various repositories only at + * request time + */ + @Override + public synchronized Object[] getChildren() { + if (isLoaded()) { + return super.getChildren(); + } else { + // initialize current object + Map refRepos = repositoryRegister.getRepositories(); + for (String name : refRepos.keySet()) { + Repository repository = refRepos.get(name); + // if (repository instanceof MaintainedRepository) + // super.addChild(new MaintainedRepositoryElem(name, + // repository, this)); + // else + super.addChild(new RepositoryElem(name, repository, this)); + } + + // remote + if (keyring != null) { + try { + addRemoteRepositories(keyring); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot browse remote repositories", e); + } + } + return super.getChildren(); + } + } + + protected void addRemoteRepositories(Keyring jcrKeyring) throws RepositoryException { + Node userHome = CmsJcrUtils.getUserHome(userSession); + if (userHome != null && userHome.hasNode(ARGEO_REMOTE)) { + NodeIterator it = userHome.getNode(ARGEO_REMOTE).getNodes(); + while (it.hasNext()) { + Node remoteNode = it.nextNode(); + String uri = remoteNode.getProperty(ARGEO_URI).getString(); + try { + RemoteRepositoryElem remoteRepositoryNode = new RemoteRepositoryElem(remoteNode.getName(), + repositoryFactory, uri, this, userSession, jcrKeyring, remoteNode.getPath()); + super.addChild(remoteRepositoryNode); + } catch (Exception e) { + ErrorFeedback.show("Cannot add remote repository " + remoteNode, e); + } + } + } + } + + public void registerNewRepository(String alias, Repository repository) { + // TODO: implement this + // Create a new RepositoryNode Object + // add it + // super.addChild(new RepositoriesNode(...)); + } + + /** Returns the {@link RepositoryRegister} wrapped by this object. */ + public RepositoryRegister getRepositoryRegister() { + return repositoryRegister; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java new file mode 100644 index 000000000..296c36922 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java @@ -0,0 +1,98 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; + +/** + * UI Tree component that wraps a JCR {@link Repository}. It also keeps a + * reference to its parent Tree Ui component; typically the unique + * {@link RepositoriesElem} object of the current view to enable bi-directionnal + * browsing in the tree. + */ + +public class RepositoryElem extends TreeParent { + private String alias; + protected Repository repository; + private Session defaultSession = null; + + /** Create a new repository with distinct name and alias */ + public RepositoryElem(String alias, Repository repository, TreeParent parent) { + super(alias); + this.repository = repository; + setParent(parent); + this.alias = alias; + } + + public void login() { + try { + defaultSession = repositoryLogin(CmsConstants.SYS_WORKSPACE); + String[] wkpNames = defaultSession.getWorkspace().getAccessibleWorkspaceNames(); + for (String wkpName : wkpNames) { + if (wkpName.equals(defaultSession.getWorkspace().getName())) + addChild(new WorkspaceElem(this, wkpName, defaultSession)); + else + addChild(new WorkspaceElem(this, wkpName)); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot connect to repository " + alias, e); + } + } + + public synchronized void logout() { + for (Object child : getChildren()) { + if (child instanceof WorkspaceElem) + ((WorkspaceElem) child).logout(); + } + clearChildren(); + JcrUtils.logoutQuietly(defaultSession); + defaultSession = null; + } + + /** + * Actual call to the {@link Repository#login(javax.jcr.Credentials, String)} + * method. To be overridden. + */ + protected Session repositoryLogin(String workspaceName) throws RepositoryException { + return repository.login(workspaceName); + } + + public String[] getAccessibleWorkspaceNames() { + try { + return defaultSession.getWorkspace().getAccessibleWorkspaceNames(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot retrieve workspace names", e); + } + } + + public void createWorkspace(String workspaceName) { + if (!isConnected()) + login(); + try { + defaultSession.getWorkspace().createWorkspace(workspaceName); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot create workspace", e); + } + } + + /** returns the {@link Repository} referenced by the current UI Node */ + public Repository getRepository() { + return repository; + } + + public String getAlias() { + return alias; + } + + public Boolean isConnected() { + if (defaultSession != null && defaultSession.isLive()) + return true; + else + return false; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java new file mode 100644 index 000000000..a2584a5e8 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java @@ -0,0 +1,84 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Workspace; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; + +/** + * UI Tree component. Wraps a node of a JCR {@link Workspace}. It also keeps a + * reference to its parent node that can either be a {@link WorkspaceElem}, a + * {@link SingleJcrNodeElem} or null if the node is "mounted" as the root of the + * UI tree. + */ +public class SingleJcrNodeElem extends TreeParent { + + private final Node node; + private String alias = null; + + /** Creates a new UiNode in the UI Tree */ + public SingleJcrNodeElem(TreeParent parent, Node node, String name) { + super(name); + setParent(parent); + this.node = node; + } + + /** + * Creates a new UiNode in the UI Tree, keeping a reference to the alias of + * the corresponding repository in the current UI environment. It is useful + * to be able to mount nodes as roots of the UI tree. + */ + public SingleJcrNodeElem(TreeParent parent, Node node, String name, String alias) { + super(name); + setParent(parent); + this.node = node; + this.alias = alias; + } + + /** Returns the node wrapped by the current UI object */ + public Node getNode() { + return node; + } + + protected String getRepositoryAlias() { + return alias; + } + + /** + * Overrides normal behaviour to initialise children only when first + * requested + */ + @Override + public synchronized Object[] getChildren() { + if (isLoaded()) { + return super.getChildren(); + } else { + // initialize current object + try { + NodeIterator ni = node.getNodes(); + while (ni.hasNext()) { + Node curNode = ni.nextNode(); + addChild(new SingleJcrNodeElem(this, curNode, curNode.getName())); + } + return super.getChildren(); + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot initialize SingleJcrNode children", re); + } + } + } + + @Override + public boolean hasChildren() { + try { + if (node.getSession().isLive()) + return node.hasNodes(); + else + return false; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot check children node existence", re); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java new file mode 100644 index 000000000..2d786669f --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java @@ -0,0 +1,117 @@ +package org.argeo.cms.ui.jcr.model; + +import javax.jcr.AccessDeniedException; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +// import javax.jcr.Workspace; +import javax.jcr.Workspace; + +import org.argeo.cms.ux.widgets.TreeParent; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; + +/** + * UI Tree component. Wraps the root node of a JCR {@link Workspace}. It also + * keeps a reference to its parent {@link RepositoryElem}, to be able to + * retrieve alias of the current used repository + */ +public class WorkspaceElem extends TreeParent { + private Session session = null; + + public WorkspaceElem(RepositoryElem parent, String name) { + this(parent, name, null); + } + + public WorkspaceElem(RepositoryElem parent, String name, Session session) { + super(name); + this.session = session; + setParent(parent); + } + + public synchronized Session getSession() { + return session; + } + + public synchronized Node getRootNode() { + try { + if (session != null) + return session.getRootNode(); + else + return null; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get root node of workspace " + getName(), e); + } + } + + public synchronized void login() { + try { + session = ((RepositoryElem) getParent()).repositoryLogin(getName()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot connect to repository " + getName(), e); + } + } + + public Boolean isConnected() { + if (session != null && session.isLive()) + return true; + else + return false; + } + + @Override + public synchronized void dispose() { + logout(); + super.dispose(); + } + + /** Logouts the session, does not nothing if there is no live session. */ + public synchronized void logout() { + clearChildren(); + JcrUtils.logoutQuietly(session); + session = null; + } + + @Override + public synchronized boolean hasChildren() { + try { + if (isConnected()) + try { + return session.getRootNode().hasNodes(); + } catch (AccessDeniedException e) { + // current user may not have access to the root node + return false; + } + else + return false; + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while checking children node existence", re); + } + } + + /** Override normal behaviour to initialize display of the workspace */ + @Override + public synchronized Object[] getChildren() { + if (isLoaded()) { + return super.getChildren(); + } else { + // initialize current object + try { + Node rootNode; + if (session == null) + return null; + else + rootNode = session.getRootNode(); + NodeIterator ni = rootNode.getNodes(); + while (ni.hasNext()) { + Node node = ni.nextNode(); + addChild(new SingleJcrNodeElem(this, node, node.getName())); + } + return super.getChildren(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot initialize WorkspaceNode UI object." + getName(), e); + } + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java new file mode 100644 index 000000000..8f5474449 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/model/package-info.java @@ -0,0 +1,2 @@ +/** Model for SWT/JFace JCR components. */ +package org.argeo.cms.ui.jcr.model; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java new file mode 100644 index 000000000..26ae330b5 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/jcr/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace JCR components. */ +package org.argeo.cms.ui.jcr; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java new file mode 100644 index 000000000..82fdee796 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/package-info.java @@ -0,0 +1,2 @@ +/** SWT/JFace components for Argeo CMS. */ +package org.argeo.cms.ui; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java new file mode 100644 index 000000000..e91f9ba48 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsLink.java @@ -0,0 +1,282 @@ +package org.argeo.cms.ui.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsStyle; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.jcr.JcrException; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.osgi.framework.BundleContext; + +/** A link to an internal or external location. */ +public class CmsLink implements CmsUiProvider { + private final static CmsLog log = CmsLog.getLog(CmsLink.class); + private BundleContext bundleContext; + + private String label; + private String style; + private String target; + private String image; + private boolean openNew = false; + private MouseListener mouseListener; + + private int horizontalAlignment = SWT.CENTER; + private int verticalAlignment = SWT.CENTER; + + private String loggedInLabel = null; + private String loggedInTarget = null; + + // internal + // private Boolean isUrl = false; + private Integer imageWidth, imageHeight; + + public CmsLink() { + super(); + } + + public CmsLink(String label, String target) { + this(label, target, (String) null); + } + + public CmsLink(String label, String target, CmsStyle style) { + this(label, target, style != null ? style.style() : null); + } + + public CmsLink(String label, String target, String style) { + super(); + this.label = label; + this.target = target; + this.style = style; + init(); + } + + public void init() { + if (image != null) { + ImageData image = loadImage(); + if (imageHeight == null && imageWidth == null) { + imageWidth = image.width; + imageHeight = image.height; + } else if (imageHeight == null) { + imageHeight = (imageWidth * image.height) / image.width; + } else if (imageWidth == null) { + imageWidth = (imageHeight * image.width) / image.height; + } + } + } + + /** @return {@link Composite} with a single {@link Label} child. */ + @Override + public Control createUi(final Composite parent, Node context) { +// if (image != null && (imageWidth == null || imageHeight == null)) { +// throw new CmsException("Image is not properly configured." +// + " Make sure bundleContext property is set and init() method has been called."); +// } + + Composite comp = new Composite(parent, SWT.NONE); + comp.setLayout(CmsSwtUtils.noSpaceGridLayout()); + + Label link = new Label(comp, SWT.NONE); + CmsSwtUtils.markup(link); + GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false); + if (image != null) { + if (imageHeight != null) + layoutData.heightHint = imageHeight; + if (label == null) + if (imageWidth != null) + layoutData.widthHint = imageWidth; + } + + link.setLayoutData(layoutData); + CmsSwtUtils.style(comp, style != null ? style : getDefaultStyle()); + CmsSwtUtils.style(link, style != null ? style : getDefaultStyle()); + + // label + StringBuilder labelText = new StringBuilder(); + if (loggedInTarget != null && isLoggedIn()) { + labelText.append(""); + } else if (target != null) { + labelText.append(""); + } + if (image != null) { + registerImageIfNeeded(); + String imageLocation = RWT.getResourceManager().getLocation(image); + labelText.append(""); + + } + + if (loggedInLabel != null && isLoggedIn()) { + labelText.append(' ').append(loggedInLabel); + } else if (label != null) { + labelText.append(' ').append(label); + } + + if ((loggedInTarget != null && isLoggedIn()) || target != null) + labelText.append(""); + + link.setText(labelText.toString()); + + if (mouseListener != null) + link.addMouseListener(mouseListener); + + return comp; + } + + private void registerImageIfNeeded() { + ResourceManager resourceManager = RWT.getResourceManager(); + if (!resourceManager.isRegistered(image)) { + URL res = getImageUrl(); + try (InputStream inputStream = res.openStream()) { + resourceManager.register(image, inputStream); + if (log.isTraceEnabled()) + log.trace("Registered image " + image); + } catch (IOException e) { + throw new RuntimeException("Cannot load image " + image, e); + } + } + } + + private ImageData loadImage() { + URL url = getImageUrl(); + ImageData result = null; + try (InputStream inputStream = url.openStream()) { + result = new ImageData(inputStream); + if (log.isTraceEnabled()) + log.trace("Loaded image " + image); + } catch (IOException e) { + throw new RuntimeException("Cannot load image " + image, e); + } + return result; + } + + private URL getImageUrl() { + URL url; + try { + // pure URL + url = new URL(image); + } catch (MalformedURLException e1) { + url = bundleContext.getBundle().getResource(image); + } + + if (url == null) + throw new IllegalStateException("No image " + image + " available."); + + return url; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setLabel(String label) { + this.label = label; + } + + public void setStyle(String style) { + this.style = style; + } + + /** @deprecated Use {@link #setStyle(String)} instead. */ + @Deprecated + public void setCustom(String custom) { + this.style = custom; + } + + public void setTarget(String target) { + this.target = target; + // try { + // new URL(target); + // isUrl = true; + // } catch (MalformedURLException e1) { + // isUrl = false; + // } + } + + public void setImage(String image) { + this.image = image; + } + + public void setLoggedInLabel(String loggedInLabel) { + this.loggedInLabel = loggedInLabel; + } + + public void setLoggedInTarget(String loggedInTarget) { + this.loggedInTarget = loggedInTarget; + } + + public void setMouseListener(MouseListener mouseListener) { + this.mouseListener = mouseListener; + } + + public void setvAlign(String vAlign) { + if ("bottom".equals(vAlign)) { + verticalAlignment = SWT.BOTTOM; + } else if ("top".equals(vAlign)) { + verticalAlignment = SWT.TOP; + } else if ("center".equals(vAlign)) { + verticalAlignment = SWT.CENTER; + } else { + throw new IllegalArgumentException( + "Unsupported vertical alignment " + vAlign + " (must be: top, bottom or center)"); + } + } + + protected boolean isLoggedIn() { + return !CurrentUser.isAnonymous(); + } + + public void setImageWidth(Integer imageWidth) { + this.imageWidth = imageWidth; + } + + public void setImageHeight(Integer imageHeight) { + this.imageHeight = imageHeight; + } + + public void setOpenNew(boolean openNew) { + this.openNew = openNew; + } + + protected String getDefaultStyle() { + return SimpleStyle.link.name(); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java new file mode 100644 index 000000000..fc0c82146 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsPane.java @@ -0,0 +1,49 @@ +package org.argeo.cms.ui.util; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Composite; + +/** The main pane of a CMS display, with QA and support areas. */ +public class CmsPane { + + private Composite mainArea; + private Composite qaArea; + private Composite supportArea; + + public CmsPane(Composite parent, int style) { + parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); + +// qaArea = new Composite(parent, SWT.NONE); +// qaArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); +// RowLayout qaLayout = new RowLayout(); +// qaLayout.spacing = 0; +// qaArea.setLayout(qaLayout); + + mainArea = new Composite(parent, SWT.NONE); + mainArea.setLayout(new GridLayout()); + mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + +// supportArea = new Composite(parent, SWT.NONE); +// supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); +// RowLayout supportLayout = new RowLayout(); +// supportLayout.spacing = 0; +// supportArea.setLayout(supportLayout); + } + + public Composite getMainArea() { + return mainArea; + } + + public Composite getQaArea() { + return qaArea; + } + + public Composite getSupportArea() { + return supportArea; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java new file mode 100644 index 000000000..7cc611c9b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java @@ -0,0 +1,192 @@ +package org.argeo.cms.ui.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.servlet.http.HttpServletRequest; + +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsView; +import org.argeo.cms.jcr.CmsJcrUtils; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.cms.ux.AbstractImageManager; +import org.argeo.cms.ux.CmsUxUtils; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Table; + +/** Static utilities for the CMS framework. */ +public class CmsUiUtils { + // private final static Log log = LogFactory.getLog(CmsUiUtils.class); + + /* + * CMS VIEW + */ + + /** + * The CMS view related to this display, or null if none is available from this + * call. + * + * @deprecated Use {@link CmsSwtUtils#getCmsView(Composite)} instead. + */ + @Deprecated + public static CmsView getCmsView() { +// return UiContext.getData(CmsView.class.getName()); + return CmsSwtUtils.getCmsView(Display.getCurrent().getActiveShell()); + } + + public static StringBuilder getServerBaseUrl(HttpServletRequest request) { + try { + URL url = new URL(request.getRequestURL().toString()); + StringBuilder buf = new StringBuilder(); + buf.append(url.getProtocol()).append("://").append(url.getHost()); + if (url.getPort() != -1) + buf.append(':').append(url.getPort()); + return buf; + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Cannot extract server base URL from " + request.getRequestURL(), e); + } + } + + // + public static String getDataUrl(Node node, HttpServletRequest request) { + try { + StringBuilder buf = getServerBaseUrl(request); + buf.append(getDataPath(node)); + return new URL(buf.toString()).toString(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Cannot build data URL for " + node, e); + } + } + + /** A path in the node repository */ + public static String getDataPath(Node node) { + return getDataPath(CmsConstants.EGO_REPOSITORY, node); + } + + public static String getDataPath(String cn, Node node) { + return CmsJcrUtils.getDataPath(cn, node); + } + + /** Clean reserved URL characters for use in HTTP links. */ + public static String getDataPathForUrl(Node node) { + return CmsSwtUtils.cleanPathForUrl(getDataPath(node)); + } + + /** @deprecated Use rowData16px() instead. GridData should not be reused. */ + @Deprecated + public static RowData ROW_DATA_16px = new RowData(16, 16); + + + + /* + * FORM LAYOUT + */ + + + + @Deprecated + public static void setItemHeight(Table table, int height) { + table.setData(CmsUiConstants.ITEM_HEIGHT, height); + } + + // + // JCR + // + public static Node getOrAddEmptyFile(Node parent, Enum child) throws RepositoryException { + if (has(parent, child)) + return child(parent, child); + return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]); + } + + public static Node child(Node parent, Enum en) throws RepositoryException { + return parent.getNode(en.name()); + } + + public static Boolean has(Node parent, Enum en) throws RepositoryException { + return parent.hasNode(en.name()); + } + + public static Node getOrAdd(Node parent, Enum en) throws RepositoryException { + return getOrAdd(parent, en, null); + } + + public static Node getOrAdd(Node parent, Enum en, String primaryType) throws RepositoryException { + if (has(parent, en)) + return child(parent, en); + else if (primaryType == null) + return parent.addNode(en.name()); + else + return parent.addNode(en.name(), primaryType); + } + + // IMAGES + + public static String img(Node fileNode, String width, String height) { + return img(null, fileNode, width, height); + } + + public static String img(String serverBase, Node fileNode, String width, String height) { +// String src = (serverBase != null ? serverBase : "") + NodeUtils.getDataPath(fileNode); + String src; + src = (serverBase != null ? serverBase : "") + getDataPathForUrl(fileNode); + return CmsUxUtils.imgBuilder(src, width, height).append("/>").toString(); + } + + public static String noImg(Cms2DSize size) { + ResourceManager rm = RWT.getResourceManager(); + return CmsUxUtils.img(rm.getLocation(AbstractImageManager.NO_IMAGE), size); + } + + public static String noImg() { + return noImg(AbstractImageManager.NO_IMAGE_SIZE); + } + + public static Image noImage(Cms2DSize size) { + ResourceManager rm = RWT.getResourceManager(); + InputStream in = null; + try { + in = rm.getRegisteredContent(AbstractImageManager.NO_IMAGE); + ImageData id = new ImageData(in); + ImageData scaled = id.scaledTo(size.getWidth(), size.getHeight()); + Image image = new Image(Display.getCurrent(), scaled); + return image; + } finally { + try { + in.close(); + } catch (IOException e) { + // silent + } + } + } + + /** Lorem ipsum text to be used during development. */ + public final static String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." + + " Etiam eleifend hendrerit sem, ac ultricies massa ornare ac." + + " Cras aliquam sodales risus, vitae varius lacus molestie quis." + + " Vivamus consequat, leo id lacinia volutpat, eros diam efficitur urna, finibus interdum risus turpis at nisi." + + " Curabitur vulputate nulla quis scelerisque fringilla. Integer consectetur turpis id lobortis accumsan." + + " Pellentesque commodo turpis ac diam ultricies dignissim." + + " Curabitur sit amet dolor volutpat lacus aliquam ornare quis sed velit." + + " Integer varius quis est et tristique." + + " Suspendisse pharetra porttitor purus, eget condimentum magna." + + " Duis vitae turpis eros. Sed tincidunt lacinia rutrum." + + " Aliquam velit velit, rutrum ut augue sed, condimentum lacinia augue."; + + /** Singleton. */ + private CmsUiUtils() { + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java new file mode 100644 index 000000000..b431fc3c9 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/DefaultImageManager.java @@ -0,0 +1,133 @@ +package org.argeo.cms.ui.util; + +import static javax.jcr.Node.JCR_CONTENT; +import static javax.jcr.Property.JCR_DATA; +import static javax.jcr.nodetype.NodeType.NT_FILE; +import static javax.jcr.nodetype.NodeType.NT_RESOURCE; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; + +import org.apache.commons.io.IOUtils; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.AbstractSwtImageManager; +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.rap.rwt.service.ResourceManager; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.widgets.Display; + +/** Manages only public images so far. */ +public class DefaultImageManager extends AbstractSwtImageManager { + private final static CmsLog log = CmsLog.getLog(DefaultImageManager.class); + + /** @return null if not available */ + @Override + public String getImageUrl(Node node) { + return CmsUiUtils.getDataPathForUrl(node); + } + + protected String getResourceName(Node node) { + try { + String workspace = node.getSession().getWorkspace().getName(); + if (node.hasNode(JCR_CONTENT)) + return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier(); + else + return workspace + '_' + node.getIdentifier(); + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + + public Binary getImageBinary(Node node) { + try { + if (node.isNodeType(NT_FILE)) { + return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary(); + } else { + return null; + } + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + + public Image getSwtImage(Node node) { + InputStream inputStream = null; + Binary binary = getImageBinary(node); + if (binary == null) + return null; + try { + inputStream = binary.getStream(); + return new Image(Display.getCurrent(), inputStream); + } catch (RepositoryException e) { + throw new JcrException(e); + } finally { + IOUtils.closeQuietly(inputStream); + JcrUtils.closeQuietly(binary); + } + } + + @Override + public String uploadImage(Node context, Node parentNode, String fileName, InputStream in, String contentType) { + InputStream inputStream = null; + try { + String previousResourceName = null; + if (parentNode.hasNode(fileName)) { + Node node = parentNode.getNode(fileName); + previousResourceName = getResourceName(node); + if (node.hasNode(JCR_CONTENT)) { + node.getNode(JCR_CONTENT).remove(); + node.addNode(JCR_CONTENT, NT_RESOURCE); + } + } + + byte[] arr = IOUtils.toByteArray(in); + Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr); + inputStream = new ByteArrayInputStream(arr); + ImageData id = new ImageData(inputStream); + processNewImageFile(context, fileNode, id); + + String mime = contentType != null ? contentType : Files.probeContentType(Paths.get(fileName)); + if (mime != null) { + fileNode.getNode(JCR_CONTENT).setProperty(Property.JCR_MIMETYPE, mime); + } + fileNode.getSession().save(); + + // reset resource manager + ResourceManager resourceManager = RWT.getResourceManager(); + if (previousResourceName != null && resourceManager.isRegistered(previousResourceName)) { + resourceManager.unregister(previousResourceName); + if (log.isDebugEnabled()) + log.debug("Unregistered image " + previousResourceName); + } + return CmsUiUtils.getDataPath(fileNode); + } catch (IOException e) { + throw new RuntimeException("Cannot upload image " + fileName + " in " + parentNode, e); + } catch (RepositoryException e) { + throw new JcrException(e); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + /** Does nothing by default. */ + protected void processNewImageFile(Node context, Node fileNode, ImageData id) + throws RepositoryException, IOException { + } + + @Override + protected String noImg(Cms2DSize size) { + return CmsUiUtils.noImg(size); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java new file mode 100644 index 000000000..284d2bd0c --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/MenuLink.java @@ -0,0 +1,22 @@ +package org.argeo.cms.ui.util; + +import org.argeo.cms.swt.CmsStyles; + +/** + * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on + * a {@link CmsLink} when simple menus are used. + */ +public class MenuLink extends CmsLink { + public MenuLink() { + setCustom(CmsStyles.CMS_MENU_LINK); + } + + public MenuLink(String label, String target, String custom) { + super(label, target, custom); + } + + public MenuLink(String label, String target) { + super(label, target, CmsStyles.CMS_MENU_LINK); + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java new file mode 100644 index 000000000..ab6a29f7d --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java @@ -0,0 +1,97 @@ +package org.argeo.cms.ui.util; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** A header in three parts */ +public class SimpleCmsHeader implements CmsUiProvider { + private List lead = new ArrayList(); + private List center = new ArrayList(); + private List end = new ArrayList(); + + private Boolean subPartsSameWidth = false; + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + Composite header = new Composite(parent, SWT.NONE); + header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER); + header.setBackgroundMode(SWT.INHERIT_DEFAULT); + header.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false))); + + configurePart(context, header, lead); + configurePart(context, header, center); + configurePart(context, header, end); + return header; + } + + protected void configurePart(Node context, Composite parent, List partProviders) + throws RepositoryException { + final int style; + final String custom; + if (lead == partProviders) { + style = SWT.LEAD; + custom = CmsStyles.CMS_HEADER_LEAD; + } else if (center == partProviders) { + style = SWT.CENTER; + custom = CmsStyles.CMS_HEADER_CENTER; + } else if (end == partProviders) { + style = SWT.END; + custom = CmsStyles.CMS_HEADER_END; + } else { + throw new CmsException("Unsupported part providers " + partProviders); + } + + Composite part = new Composite(parent, SWT.NONE); + part.setData(RWT.CUSTOM_VARIANT, custom); + GridData gridData = new GridData(style, SWT.FILL, true, true); + part.setLayoutData(gridData); + part.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(partProviders.size(), subPartsSameWidth))); + for (CmsUiProvider uiProvider : partProviders) { + Control subPart = uiProvider.createUi(part, context); + subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + } + } + + public void setLead(List lead) { + this.lead = lead; + } + + public void setCenter(List center) { + this.center = center; + } + + public void setEnd(List end) { + this.end = end; + } + + public void setSubPartsSameWidth(Boolean subPartsSameWidth) { + this.subPartsSameWidth = subPartsSameWidth; + } + + public List getLead() { + return lead; + } + + public List getCenter() { + return center; + } + + public List getEnd() { + return end; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java new file mode 100644 index 000000000..c61a2fc9e --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java @@ -0,0 +1,118 @@ +package org.argeo.cms.ui.util; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.ui.CmsUiProvider; +import org.argeo.jcr.JcrUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class SimpleDynamicPages implements CmsUiProvider { + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + if (context == null) + throw new CmsException("Context cannot be null"); + parent.setLayout(new GridLayout(2, false)); + + // parent + if (!context.getPath().equals("/")) { + new CmsLink("..", context.getParent().getPath()).createUi(parent, + context); + new Label(parent, SWT.NONE).setText(context.getParent() + .getPrimaryNodeType().getName()); + } + + // context + Label contextL = new Label(parent, SWT.NONE); + contextL.setData(RWT.MARKUP_ENABLED, true); + contextL.setText("" + context.getName() + ""); + new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType() + .getName()); + + // children + // Label childrenL = new Label(parent, SWT.NONE); + // childrenL.setData(RWT.MARKUP_ENABLED, true); + // childrenL.setText("Children:"); + // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, + // false, 2, 1)); + + for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { + Node child = nIt.nextNode(); + new CmsLink(child.getName(), child.getPath()).createUi(parent, + context); + + new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType() + .getName()); + } + + // properties + // Label propsL = new Label(parent, SWT.NONE); + // propsL.setData(RWT.MARKUP_ENABLED, true); + // propsL.setText("Properties:"); + // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, + // 2, 1)); + for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { + Property property = pIt.nextProperty(); + + Label label = new Label(parent, SWT.NONE); + label.setText(property.getName()); + label.setToolTipText(JcrUtils + .getPropertyDefinitionAsString(property)); + + new Label(parent, SWT.NONE).setText(getPropAsString(property)); + } + + return null; + } + + private String getPropAsString(Property property) + throws RepositoryException { + String result = ""; + DateFormat timeFormatter = new SimpleDateFormat(""); + if (property.isMultiple()) { + result = getMultiAsString(property, ", "); + } else { + Value value = property.getValue(); + if (value.getType() == PropertyType.BINARY) + result = ""; + else if (value.getType() == PropertyType.DATE) + result = timeFormatter.format(value.getDate().getTime()); + else + result = value.getString(); + } + return result; + } + + private String getMultiAsString(Property property, String separator) + throws RepositoryException { + if (separator == null) + separator = "; "; + Value[] values = property.getValues(); + StringBuilder builder = new StringBuilder(); + for (Value val : values) { + String currStr = val.getString(); + if (!"".equals(currStr.trim())) + builder.append(currStr).append(separator); + } + if (builder.lastIndexOf(separator) >= 0) + return builder.substring(0, builder.length() - separator.length()); + else + return builder.toString(); + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java new file mode 100644 index 000000000..63e504b7a --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java @@ -0,0 +1,32 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +public class SimpleStaticPage implements CmsUiProvider { + private String text; + + @Override + public Control createUi(Composite parent, Node context) + throws RepositoryException { + Label textC = new Label(parent, SWT.WRAP); + textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT); + textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); + textC.setText(text); + + return textC; + } + + public void setText(String text) { + this.text = text; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java new file mode 100644 index 000000000..b5fca2699 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SimpleStyle.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.util; + +import org.argeo.api.cms.ux.CmsStyle; + +/** Simple styles used by the CMS UI utilities. */ +public enum SimpleStyle implements CmsStyle { + link; +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java new file mode 100644 index 000000000..1e17dc930 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java @@ -0,0 +1,71 @@ +package org.argeo.cms.ui.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.argeo.cms.swt.CmsException; +import org.eclipse.rap.rwt.service.ResourceLoader; +import org.osgi.framework.Bundle; + +/** {@link ResourceLoader} caching stylesheets. */ +public class StyleSheetResourceLoader implements ResourceLoader { + private Bundle themeBundle; + private Map stylesheets = new LinkedHashMap(); + + public StyleSheetResourceLoader(Bundle themeBundle) { + this.themeBundle = themeBundle; + } + + @Override + public InputStream getResourceAsStream(String resourceName) throws IOException { + if (!stylesheets.containsKey(resourceName)) { + // TODO deal with other bundles + // Bundle bundle = bundleContext.getBundle(); + // String location = + // bundle.getLocation().substring("initial@reference:".length()); + // if (location.startsWith("file:")) { + // Path path = null; + // try { + // path = Paths.get(new URI(location)); + // } catch (URISyntaxException e) { + // e.printStackTrace(); + // } + // if (path != null) { + // Path resourcePath = path.resolve(resourceName); + // if (Files.exists(resourcePath)) + // return Files.newInputStream(resourcePath); + // } + // } + + URL res = themeBundle.getEntry(resourceName); + if (res == null) + throw new CmsException( + "Entry " + resourceName + " not found in bundle " + themeBundle.getSymbolicName()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copy(res.openStream(), out); + stylesheets.put(resourceName, new StyleSheet(out.toByteArray())); + } + return new ByteArrayInputStream(stylesheets.get(resourceName).getData()); + // return res.openStream(); + } + + private class StyleSheet { + private byte[] data; + + public StyleSheet(byte[] data) { + super(); + this.data = data; + } + + public byte[] getData() { + return data; + } + + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java new file mode 100644 index 000000000..5a0078163 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/SystemNotifications.java @@ -0,0 +1,129 @@ +package org.argeo.cms.ui.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.commons.io.IOUtils; +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** Shell displaying system notifications such as exceptions */ +public class SystemNotifications extends Shell implements CmsStyles, + MouseListener { + private static final long serialVersionUID = -8129377525216022683L; + + private Control source; + + public SystemNotifications(Control source) { + super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); + + this.source = source; + + // TODO UI + // setLocation(source.toDisplay(source.getSize().x - getSize().x, + // source.getSize().y)); + setLayout(new GridLayout()); + addMouseListener(this); + + addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + close(); + dispose(); + } + }); + + } + + public void notifyException(Throwable exception) { + Composite pane = this; + + Label lbl = new Label(pane, SWT.NONE); + lbl.setText(exception.getLocalizedMessage() + + (exception instanceof CmsException ? "" : "(" + + exception.getClass().getName() + ")") + "\n"); + lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + lbl.addMouseListener(this); + if (exception.getCause() != null) + appendCause(pane, exception.getCause()); + + StringBuilder mailToUrl = new StringBuilder("mailto:?"); + try { + mailToUrl.append("subject=").append( + URLEncoder.encode( + "Exception " + + new SimpleDateFormat("yyyy-MM-dd hh:mm") + .format(new Date()), "UTF-8") + .replace("+", "%20")); + + StringWriter sw = new StringWriter(); + exception.printStackTrace(new PrintWriter(sw)); + IOUtils.closeQuietly(sw); + + // see + // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character + String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace( + "+", "%20"); + mailToUrl.append("&body=").append(encoded); + } catch (UnsupportedEncodingException e) { + mailToUrl.append("&body=").append("Could not encode: ") + .append(e.getMessage()); + } + Label mailTo = new Label(pane, SWT.NONE); + CmsSwtUtils.markup(mailTo); + mailTo.setText("Send details"); + mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); + + pack(); + layout(); + + setLocation(source.toDisplay(source.getSize().x - getSize().x, + source.getSize().y - getSize().y)); + open(); + } + + private void appendCause(Composite parent, Throwable e) { + Label lbl = new Label(parent, SWT.NONE); + lbl.setText(" caused by: " + e.getLocalizedMessage() + " (" + + e.getClass().getName() + ")" + "\n"); + lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + lbl.addMouseListener(this); + if (e.getCause() != null) + appendCause(parent, e.getCause()); + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + } + + @Override + public void mouseDown(MouseEvent e) { + close(); + dispose(); + } + + @Override + public void mouseUp(MouseEvent e) { + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java new file mode 100644 index 000000000..09aeff60f --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenu.java @@ -0,0 +1,56 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; + +import org.argeo.cms.swt.CmsException; +import org.argeo.cms.swt.auth.CmsLoginShell; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** The site-related user menu */ +public class UserMenu extends CmsLoginShell { + private final Control source; + private final Node context; + + public UserMenu(Control source, Node context) { + // FIXME pass CMS context + super(CmsUiUtils.getCmsView(), null); + this.context = context; + createUi(); + if (source == null) + throw new CmsException("Source control cannot be null."); + this.source = source; + open(); + } + + @Override + protected Shell createShell() { + return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + } + + @Override + public void open() { + Shell shell = getShell(); + shell.pack(); + shell.layout(); + shell.setLocation(source.toDisplay(source.getSize().x - shell.getSize().x, source.getSize().y)); + shell.addShellListener(new ShellAdapter() { + private static final long serialVersionUID = 5178980294808435833L; + + @Override + public void shellDeactivated(ShellEvent e) { + closeShell(); + } + }); + super.open(); + } + + protected Node getContext() { + return context; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java new file mode 100644 index 000000000..317a7b55b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/UserMenuLink.java @@ -0,0 +1,84 @@ +package org.argeo.cms.ui.util; + +import javax.jcr.Node; + +import org.argeo.cms.CmsMsg; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.swt.CmsStyles; +import org.argeo.cms.swt.auth.CmsLoginShell; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** Open the user menu when clicked */ +public class UserMenuLink extends MenuLink { + + public UserMenuLink() { + setCustom(CmsStyles.CMS_USER_MENU_LINK); + } + + @Override + public Control createUi(Composite parent, Node context) { + if (CurrentUser.isAnonymous()) + setLabel(CmsMsg.login.lead()); + else { + setLabel(CurrentUser.getDisplayName()); + } + Label link = (Label) ((Composite) super.createUi(parent, context)).getChildren()[0]; + link.addMouseListener(new UserMenuLinkController(context)); + return link.getParent(); + } + + protected CmsLoginShell createUserMenu(Control source, Node context) { + return new UserMenu(source.getParent(), context); + } + + private class UserMenuLinkController implements MouseListener, DisposeListener { + private static final long serialVersionUID = 3634864186295639792L; + + private CmsLoginShell userMenu = null; + private long lastDisposeTS = 0l; + + private final Node context; + + public UserMenuLinkController(Node context) { + this.context = context; + } + + // + // MOUSE LISTENER + // + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 1) { + Control source = (Control) e.getSource(); + if (userMenu == null) { + long durationSinceLastDispose = System.currentTimeMillis() - lastDisposeTS; + // avoid to reopen the menu, if one has clicked gain + if (durationSinceLastDispose > 200) { + userMenu = createUserMenu(source, context); + userMenu.getShell().addDisposeListener(this); + } + } + } + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + } + + @Override + public void mouseUp(MouseEvent e) { + } + + @Override + public void widgetDisposed(DisposeEvent event) { + userMenu = null; + lastDisposeTS = System.currentTimeMillis(); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java new file mode 100644 index 000000000..7f846c932 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/VerticalMenu.java @@ -0,0 +1,44 @@ +package org.argeo.cms.ui.util; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +public class VerticalMenu implements CmsUiProvider { + private List items = new ArrayList(); + + @Override + public Control createUi(Composite parent, Node context) throws RepositoryException { + Composite part = new Composite(parent, SWT.NONE); + part.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); +// part.setData(RWT.CUSTOM_VARIANT, custom); + part.setLayout(CmsSwtUtils.noSpaceGridLayout()); + for (CmsUiProvider uiProvider : items) { + Control subPart = uiProvider.createUi(part, context); + subPart.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); + } + return part; + } + + public void add(CmsUiProvider uiProvider) { + items.add(uiProvider); + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java new file mode 100644 index 000000000..566df883c --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/util/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS UI utilities. */ +package org.argeo.cms.ui.util; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java new file mode 100644 index 000000000..e23846e92 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java @@ -0,0 +1,351 @@ +package org.argeo.cms.ui.viewers; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Observable; +import java.util.Observer; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.security.auth.Subject; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.CmsEditable; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.swt.widgets.ScrolledPage; +import org.argeo.jcr.JcrException; +import org.eclipse.jface.viewers.ContentViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Widget; +import org.xml.sax.SAXParseException; + +/** Base class for viewers related to a page */ +public abstract class AbstractPageViewer extends ContentViewer implements Observer { + private static final long serialVersionUID = 5438688173410341485L; + + private final static CmsLog log = CmsLog.getLog(AbstractPageViewer.class); + + private final boolean readOnly; + /** The basis for the layouts, typically a ScrolledPage. */ + private final Composite page; + private final CmsEditable cmsEditable; + + private MouseListener mouseListener; + private FocusListener focusListener; + + private SwtEditablePart edited; + private ISelection selection = StructuredSelection.EMPTY; + + private AccessControlContext accessControlContext; + + protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) { + // read only at UI level + readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); + + this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE : cmsEditable; + if (this.cmsEditable instanceof Observable) + ((Observable) this.cmsEditable).addObserver(this); + + if (cmsEditable.canEdit()) { + mouseListener = createMouseListener(); + focusListener = createFocusListener(); + } + page = findPage(parent); + accessControlContext = AccessController.getContext(); + } + + /** + * Can be called to simplify the called to isModelInitialized() and initModel() + */ + protected void initModelIfNeeded(Node node) { + try { + if (!isModelInitialized(node)) + if (getCmsEditable().canEdit()) { + initModel(node); + node.getSession().save(); + } + } catch (RepositoryException e) { + throw new JcrException("Cannot initialize model", e); + } + } + + /** Called if user can edit and model is not initialized */ + protected Boolean isModelInitialized(Node node) throws RepositoryException { + return true; + } + + /** Called if user can edit and model is not initialized */ + protected void initModel(Node node) throws RepositoryException { + } + + /** Create (retrieve) the MouseListener to use. */ + protected MouseListener createMouseListener() { + return new MouseAdapter() { + private static final long serialVersionUID = 1L; + }; + } + + /** Create (retrieve) the FocusListener to use. */ + protected FocusListener createFocusListener() { + return new FocusListener() { + private static final long serialVersionUID = 1L; + + @Override + public void focusLost(FocusEvent event) { + } + + @Override + public void focusGained(FocusEvent event) { + } + }; + } + + protected Composite findPage(Composite composite) { + if (composite instanceof ScrolledPage) { + return (ScrolledPage) composite; + } else { + if (composite.getParent() == null) + return composite; + return findPage(composite.getParent()); + } + } + + public void layoutPage() { + if (page != null) + page.layout(true, true); + } + + protected void showControl(Control control) { + if (page != null && (page instanceof ScrolledPage)) + ((ScrolledPage) page).showControl(control); + } + + @Override + public void update(Observable o, Object arg) { + if (o == cmsEditable) + editingStateChanged(cmsEditable); + } + + /** To be overridden in order to provide the actual refresh */ + protected void refresh(Control control) throws RepositoryException { + } + + /** To be overridden.Save the edited part. */ + protected void save(SwtEditablePart part) throws RepositoryException { + } + + /** Prepare the edited part */ + protected void prepare(SwtEditablePart part, Object caretPosition) { + } + + /** Notified when the editing state changed. Does nothing, to be overridden */ + protected void editingStateChanged(CmsEditable cmsEditable) { + } + + @Override + public void refresh() { + // TODO check actual context in order to notice a discrepancy + Subject viewerSubject = getViewerSubject(); + Subject.doAs(viewerSubject, (PrivilegedAction) () -> { + try { + if (cmsEditable.canEdit() && !readOnly) + mouseListener = createMouseListener(); + else + mouseListener = null; + refresh(getControl()); + // layout(getControl()); + if (!getControl().isDisposed()) + layoutPage(); + } catch (RepositoryException e) { + throw new JcrException("Cannot refresh", e); + } + return null; + }); + } + + @Override + public void setSelection(ISelection selection, boolean reveal) { + this.selection = selection; + } + + protected void updateContent(SwtEditablePart part) throws RepositoryException { + } + + // LOW LEVEL EDITION + protected void edit(SwtEditablePart part, Object caretPosition) { + try { + if (edited == part) + return; + + if (edited != null && edited != part) { + SwtEditablePart previouslyEdited = edited; + try { + stopEditing(true); + } catch (Exception e) { + notifyEditionException(e); + edit(previouslyEdited, caretPosition); + return; + } + } + + part.startEditing(); + edited = part; + updateContent(part); + prepare(part, caretPosition); + edited.getControl().addFocusListener(new FocusListener() { + private static final long serialVersionUID = 6883521812717097017L; + + @Override + public void focusLost(FocusEvent event) { + stopEditing(true); + } + + @Override + public void focusGained(FocusEvent event) { + } + }); + + layout(part.getControl()); + showControl(part.getControl()); + } catch (RepositoryException e) { + throw new JcrException("Cannot edit " + part, e); + } + } + + protected void stopEditing(Boolean save) { + if (edited instanceof Widget && ((Widget) edited).isDisposed()) { + edited = null; + return; + } + + assert edited != null; + if (edited == null) { + if (log.isTraceEnabled()) + log.warn("Told to stop editing while not editing anything"); + return; + } + + try { + if (save) + save(edited); + + edited.stopEditing(); + SwtEditablePart editablePart = edited; + Control control = ((SwtEditablePart) edited).getControl(); + edited = null; + // TODO make edited state management more robust + updateContent(editablePart); + layout(control); + } catch (RepositoryException e) { + throw new JcrException("Cannot stop editing", e); + } finally { + edited = null; + } + } + + // METHODS AVAILABLE TO EXTENDING CLASSES + protected void saveEdit() { + if (edited != null) + stopEditing(true); + } + + protected void cancelEdit() { + if (edited != null) + stopEditing(false); + } + + /** Layout this controls from the related base page. */ + public void layout(Control... controls) { + page.layout(controls); + } + + /** + * Find the first {@link SwtEditablePart} in the parents hierarchy of this control + */ + protected SwtEditablePart findDataParent(Control parent) { + if (parent instanceof SwtEditablePart) { + return (SwtEditablePart) parent; + } + if (parent.getParent() != null) + return findDataParent(parent.getParent()); + else + throw new IllegalStateException("No data parent found"); + } + + // UTILITIES + /** Check whether the edited part is in a proper state */ + protected void checkEdited() { + if (edited == null || (edited instanceof Widget) && ((Widget) edited).isDisposed()) + throw new IllegalStateException("Edited should not be null or disposed at this stage"); + } + + /** Persist all changes. */ + protected void persistChanges(Session session) throws RepositoryException { + session.save(); + session.refresh(false); + // TODO notify that changes have been persisted + } + + /** Convenience method using a Node in order to save the underlying session. */ + protected void persistChanges(Node anyNode) throws RepositoryException { + persistChanges(anyNode.getSession()); + } + + /** Notify edition exception */ + protected void notifyEditionException(Throwable e) { + Throwable eToLog = e; + if (e instanceof IllegalArgumentException) + if (e.getCause() instanceof SAXParseException) + eToLog = e.getCause(); + log.error(eToLog.getMessage(), eToLog); +// if (log.isTraceEnabled()) +// log.trace("Full stack of " + eToLog.getMessage(), e); + // TODO Light error notification popup + } + + protected Subject getViewerSubject() { + Subject res = null; + if (accessControlContext != null) { + res = Subject.getSubject(accessControlContext); + } + if (res == null) + throw new IllegalStateException("No subject associated with this viewer"); + return res; + } + + // GETTERS / SETTERS + public boolean isReadOnly() { + return readOnly; + } + + protected SwtEditablePart getEdited() { + return edited; + } + + public MouseListener getMouseListener() { + return mouseListener; + } + + public FocusListener getFocusListener() { + return focusListener; + } + + public CmsEditable getCmsEditable() { + return cmsEditable; + } + + @Override + public ISelection getSelection() { + return selection; + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java new file mode 100644 index 000000000..4ca45d191 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/ItemPart.java @@ -0,0 +1,9 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +/** An editable part related to a JCR Item */ +public interface ItemPart { + public Item getItem() throws RepositoryException; +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java new file mode 100644 index 000000000..298fbdea9 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java @@ -0,0 +1,94 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; +import javax.jcr.version.VersionManager; + +import org.argeo.api.cms.ux.CmsEditionEvent; +import org.argeo.cms.ux.AbstractCmsEditable; +import org.argeo.jcr.JcrException; +import org.eclipse.rap.rwt.RWT; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** Provides the CmsEditable semantic based on JCR versioning. */ +public class JcrVersionCmsEditable extends AbstractCmsEditable { + private final String nodePath;// cache + private final VersionManager versionManager; + private final Boolean canEdit; + + public JcrVersionCmsEditable(Node node) throws RepositoryException { + this.nodePath = node.getPath(); + if (node.getSession().hasPermission(node.getPath(), Session.ACTION_SET_PROPERTY)) { + // was Session.ACTION_ADD_NODE + canEdit = true; + if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) { + node.addMixin(NodeType.MIX_VERSIONABLE); + node.getSession().save(); + } + versionManager = node.getSession().getWorkspace().getVersionManager(); + } else { + canEdit = false; + versionManager = null; + } + + // bind keys + if (canEdit) { + Display display = Display.getCurrent(); + display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN", "CTRL+E" }); + display.addFilter(SWT.KeyDown, new Listener() { + private static final long serialVersionUID = -4378653870463187318L; + + public void handleEvent(Event e) { + boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0; + if (ctrlPressed && e.keyCode == '\r') + stopEditing(); + else if (ctrlPressed && e.keyCode == 'E') + stopEditing(); + } + }); + } + } + + @Override + public Boolean canEdit() { + return canEdit; + } + + public Boolean isEditing() { + try { + if (!canEdit()) + return false; + return versionManager.isCheckedOut(nodePath); + } catch (RepositoryException e) { + throw new JcrException("Cannot check whether " + nodePath + " is editing", e); + } + } + + @Override + public void startEditing() { + try { + versionManager.checkout(nodePath); +// setChanged(); + } catch (RepositoryException e1) { + throw new JcrException("Cannot publish " + nodePath, e1); + } + notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.START_EDITING, this)); + } + + @Override + public void stopEditing() { + try { + versionManager.checkin(nodePath); +// setChanged(); + } catch (RepositoryException e1) { + throw new JcrException("Cannot publish " + nodePath, e1); + } + notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.STOP_EDITING, this)); + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java new file mode 100644 index 000000000..b51d4fcba --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/NodePart.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Node; + +/** An editable part related to a node */ +public interface NodePart extends ItemPart { + public Node getNode(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java new file mode 100644 index 000000000..793079e20 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.viewers; + +import javax.jcr.Property; + +/** An editable part related to a JCR Property */ +public interface PropertyPart extends ItemPart { + public Property getProperty(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java new file mode 100644 index 000000000..b27fa3845 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/Section.java @@ -0,0 +1,166 @@ +package org.argeo.cms.ui.viewers; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.SwtEditablePart; +import org.argeo.cms.ui.widgets.JcrComposite; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** A structured UI related to a JCR context. */ +public class Section extends JcrComposite { + private static final long serialVersionUID = -5933796173755739207L; + + private final Section parentSection; + private Composite sectionHeader; + private final Integer relativeDepth; + + public Section(Composite parent, int style, Node node) { + this(parent, findSection(parent), style, node); + } + + public Section(Section section, int style, Node node) { + this(section, section, style, node); + } + + protected Section(Composite parent, Section parentSection, int style, Node node) { + super(parent, style, node); + try { + this.parentSection = parentSection; + if (parentSection != null) { + relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); + } else { + relativeDepth = 0; + } + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } catch (RepositoryException e) { + throw new IllegalStateException("Cannot create section from " + node, e); + } + } + + public Map getSubSections() throws RepositoryException { + LinkedHashMap result = new LinkedHashMap(); + for (Control child : getChildren()) { + if (child instanceof Composite) { + collectDirectSubSections((Composite) child, result); + } + } + return Collections.unmodifiableMap(result); + } + + private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) + throws RepositoryException { + if (composite == sectionHeader || composite instanceof SwtEditablePart) + return; + if (composite instanceof Section) { + Section section = (Section) composite; + subSections.put(section.getNodeId(), section); + return; + } + + for (Control child : composite.getChildren()) + if (child instanceof Composite) + collectDirectSubSections((Composite) child, subSections); + } + + public Composite createHeader() { + return createHeader(this); + } + + public Composite createHeader(Composite parent) { + if (sectionHeader != null) + sectionHeader.dispose(); + + sectionHeader = new Composite(parent, SWT.NONE); + sectionHeader.setLayoutData(CmsSwtUtils.fillWidth()); + sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout()); + // sectionHeader.moveAbove(null); + // layout(); + return sectionHeader; + } + + public Composite getHeader() { + if (sectionHeader != null && sectionHeader.isDisposed()) + sectionHeader = null; + return sectionHeader; + } + + // SECTION PARTS + public SectionPart getSectionPart(String partId) { + for (Control child : getChildren()) { + if (child instanceof SectionPart) { + SectionPart sectionPart = (SectionPart) child; + if (sectionPart.getPartId().equals(partId)) + return sectionPart; + } + } + return null; + } + + public SectionPart nextSectionPart(SectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) { + for (int j = i + 1; j < children.length; j++) { + if (children[i + 1] instanceof SectionPart) { + return (SectionPart) children[i + 1]; + } + } + +// if (i + 1 < children.length) { +// Composite next = (Composite) children[i + 1]; +// return (SectionPart) next; +// } else { +// // next section +// } + } + } + return null; + } + + public SectionPart previousSectionPart(SectionPart sectionPart) { + Control[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + if (sectionPart == children[i]) + if (i != 0) { + Composite previous = (Composite) children[i - 1]; + return (SectionPart) previous; + } else { + // previous section + } + } + return null; + } + + @Override + public String toString() { + if (parentSection == null) + return "Main section " + getNode(); + return "Section " + getNode(); + } + + public Section getParentSection() { + return parentSection; + } + + public Integer getRelativeDepth() { + return relativeDepth; + } + + /** Recursively finds the related section in the parents (can be itself) */ + public static Section findSection(Control control) { + if (control == null) + return null; + if (control instanceof Section) + return (Section) control; + else + return findSection(control.getParent()); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java new file mode 100644 index 000000000..4278c83cc --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/SectionPart.java @@ -0,0 +1,10 @@ +package org.argeo.cms.ui.viewers; + +import org.argeo.cms.swt.SwtEditablePart; + +/** An editable part dynamically related to a Section */ +public interface SectionPart extends SwtEditablePart, NodePart { + public String getPartId(); + + public Section getSection(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java new file mode 100644 index 000000000..2f0793127 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS generic viewers, based on JFace. */ +package org.argeo.cms.ui.viewers; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java new file mode 100644 index 000000000..95d9e8ee9 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableImage.java @@ -0,0 +1,112 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.util.CmsUiUtils; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** A stylable and editable image. */ +public abstract class EditableImage extends StyledControl { + private static final long serialVersionUID = -5689145523114022890L; + private final static CmsLog log = CmsLog.getLog(EditableImage.class); + + private Cms2DSize preferredImageSize; + private Boolean loaded = false; + + public EditableImage(Composite parent, int swtStyle) { + super(parent, swtStyle); + } + + public EditableImage(Composite parent, int swtStyle, Cms2DSize preferredImageSize) { + super(parent, swtStyle); + this.preferredImageSize = preferredImageSize; + } + + public EditableImage(Composite parent, int style, Node node, boolean cacheImmediately, Cms2DSize preferredImageSize) + throws RepositoryException { + super(parent, style, node, cacheImmediately); + this.preferredImageSize = preferredImageSize; + } + + @Override + protected void setContainerLayoutData(Composite composite) { + // composite.setLayoutData(fillWidth()); + } + + @Override + protected void setControlLayoutData(Control control) { + // control.setLayoutData(fillWidth()); + } + + /** To be overriden. */ + protected String createImgTag() throws RepositoryException { + return CmsUiUtils + .noImg(preferredImageSize != null ? preferredImageSize : new Cms2DSize(getSize().x, getSize().y)); + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle()); + // lbl.setLayoutData(CmsUiUtils.fillWidth()); + CmsSwtUtils.markup(lbl); + CmsSwtUtils.style(lbl, style); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + load(lbl); + return lbl; + } + + /** To be overriden. */ + protected synchronized Boolean load(Control control) { + String imgTag; + try { + imgTag = createImgTag(); + } catch (Exception e) { + // throw new CmsException("Cannot retrieve image", e); + log.error("Cannot retrieve image", e); + imgTag = CmsUiUtils.noImg(preferredImageSize); + loaded = false; + } + + if (imgTag == null) { + loaded = false; + imgTag = CmsUiUtils.noImg(preferredImageSize); + } else + loaded = true; + if (control != null) { + ((Label) control).setText(imgTag); + control.setSize(preferredImageSize != null + ? new Point(preferredImageSize.getWidth(), preferredImageSize.getHeight()) + : getSize()); + } else { + loaded = false; + } + getParent().layout(); + return loaded; + } + + public void setPreferredSize(Cms2DSize size) { + this.preferredImageSize = size; + if (!loaded) { + load((Label) getControl()); + } + } + + protected Text createText(Composite box, String style) { + Text text = new Text(box, getStyle()); + CmsSwtUtils.style(text, style); + return text; + } + + public Cms2DSize getPreferredImageSize() { + return preferredImageSize; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java new file mode 100644 index 000000000..e3499ac4b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/EditableText.java @@ -0,0 +1,145 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** Editable text part displaying styled text. */ +public class EditableText extends StyledControl { + private static final long serialVersionUID = -6372283442330912755L; + + private boolean editable = true; + + private Color highlightColor; + private Composite highlight; + + private boolean useTextAsLabel = false; + + public EditableText(Composite parent, int style) { + super(parent, style); + editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); + highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); + } + + public EditableText(Composite parent, int style, Item item) throws RepositoryException { + this(parent, style, item, false); + } + + public EditableText(Composite parent, int style, Item item, boolean cacheImmediately) throws RepositoryException { + super(parent, style, item, cacheImmediately); + editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); + highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing() && getEditable()) { + return createText(box, style, true); + } else { + if (useTextAsLabel) { + return createTextLabel(box, style); + } else { + return createLabel(box, style); + } + } + } + + protected Label createLabel(Composite box, String style) { + Label lbl = new Label(box, getStyle() | SWT.WRAP); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + if (style != null) + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + protected Text createTextLabel(Composite box, String style) { + Text lbl = new Text(box, getStyle() | SWT.MULTI); + lbl.setEditable(false); + lbl.setLayoutData(CmsSwtUtils.fillWidth()); + if (style != null) + CmsSwtUtils.style(lbl, style); + CmsSwtUtils.markup(lbl); + if (mouseListener != null) + lbl.addMouseListener(mouseListener); + return lbl; + } + + protected Text createText(Composite box, String style, boolean editable) { + highlight = new Composite(box, SWT.NONE); + highlight.setBackground(highlightColor); + GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false); + highlightGd.widthHint = 5; + highlightGd.heightHint = 3; + highlight.setLayoutData(highlightGd); + + final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP); + text.setEditable(editable); + GridData textLayoutData = CmsSwtUtils.fillWidth(); + // textLayoutData.heightHint = preferredHeight; + text.setLayoutData(textLayoutData); + if (style != null) + CmsSwtUtils.style(text, style); + text.setFocus(); + return text; + } + + @Override + protected void clear(boolean deep) { + if (highlight != null) + highlight.dispose(); + super.clear(deep); + } + + public void setText(String text) { + Control child = getControl(); + if (child instanceof Label) + ((Label) child).setText(text); + else if (child instanceof Text) + ((Text) child).setText(text); + } + + public Text getAsText() { + return (Text) getControl(); + } + + public Label getAsLabel() { + return (Label) getControl(); + } + + public String getText() { + Control child = getControl(); + + if (child instanceof Label) + return ((Label) child).getText(); + else if (child instanceof Text) + return ((Text) child).getText(); + else + throw new IllegalStateException("Unsupported control " + child.getClass()); + } + + /** @deprecated Use {@link #isEditable()} instead. */ + @Deprecated + public boolean getEditable() { + return isEditable(); + } + + public boolean isEditable() { + return editable; + } + + public void setUseTextAsLabel(boolean useTextAsLabel) { + this.useTextAsLabel = useTextAsLabel; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java new file mode 100644 index 000000000..41063fa47 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/Img.java @@ -0,0 +1,155 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsImageManager; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.internal.JcrFileUploadReceiver; +import org.argeo.cms.ui.viewers.NodePart; +import org.argeo.cms.ui.viewers.Section; +import org.argeo.cms.ui.viewers.SectionPart; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrException; +import org.eclipse.rap.fileupload.FileUploadHandler; +import org.eclipse.rap.fileupload.FileUploadListener; +import org.eclipse.rap.fileupload.FileUploadReceiver; +import org.eclipse.rap.rwt.service.ServerPushSession; +import org.eclipse.rap.rwt.widgets.FileUpload; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** An image within the Argeo Text framework */ +public class Img extends EditableImage implements SectionPart, NodePart { + private static final long serialVersionUID = 6233572783968188476L; + + private final Section section; + + private final CmsImageManager imageManager; + private FileUploadHandler currentUploadHandler = null; + private FileUploadListener fileUploadListener; + + public Img(Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize) throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, preferredImageSize, null); + setStyle(TextStyles.TEXT_IMAGE); + } + + public Img(Composite parent, int swtStyle, Node imgNode) throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, null, null); + setStyle(TextStyles.TEXT_IMAGE); + } + + public Img(Composite parent, int swtStyle, Node imgNode, CmsImageManager imageManager) + throws RepositoryException { + this(Section.findSection(parent), parent, swtStyle, imgNode, null, imageManager); + setStyle(TextStyles.TEXT_IMAGE); + } + + Img(Section section, Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize, + CmsImageManager imageManager) throws RepositoryException { + super(parent, swtStyle, imgNode, false, preferredImageSize); + this.section = section; + this.imageManager = imageManager != null ? imageManager + : (CmsImageManager) CmsSwtUtils.getCmsView(section).getImageManager(); + CmsSwtUtils.style(this, TextStyles.TEXT_IMG); + } + + @Override + protected Control createControl(Composite box, String style) { + if (isEditing()) { + try { + return createImageChooser(box, style); + } catch (RepositoryException e) { + throw new JcrException("Cannot create image chooser", e); + } + } else { + return createLabel(box, style); + } + } + + @Override + public synchronized void stopEditing() { + super.stopEditing(); + fileUploadListener = null; + } + + @Override + protected synchronized Boolean load(Control lbl) { + Node imgNode = getNode(); + boolean loaded = imageManager.load(imgNode, lbl, getPreferredImageSize()); + // getParent().layout(); + return loaded; + } + + protected Node getUploadFolder() { + return Jcr.getParent(getNode()); + } + + protected String getUploadName() { + Node node = getNode(); + return Jcr.getName(node) + '[' + Jcr.getIndex(node) + ']'; + } + + protected CmsImageManager getImageManager() { + return imageManager; + } + + protected Control createImageChooser(Composite box, String style) throws RepositoryException { + JcrFileUploadReceiver receiver = new JcrFileUploadReceiver(this, getUploadFolder(), getUploadName(), + imageManager); + if (currentUploadHandler != null) + currentUploadHandler.dispose(); + currentUploadHandler = prepareUpload(receiver); + final ServerPushSession pushSession = new ServerPushSession(); + final FileUpload fileUpload = new FileUpload(box, SWT.NONE); + CmsSwtUtils.style(fileUpload, style); + fileUpload.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -9158471843941668562L; + + @Override + public void widgetSelected(SelectionEvent e) { + pushSession.start(); + fileUpload.submit(currentUploadHandler.getUploadUrl()); + } + }); + return fileUpload; + } + + protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) { + final FileUploadHandler uploadHandler = new FileUploadHandler(receiver); + if (fileUploadListener != null) + uploadHandler.addUploadListener(fileUploadListener); + return uploadHandler; + } + + @Override + public Section getSection() { + return section; + } + + public void setFileUploadListener(FileUploadListener fileUploadListener) { + this.fileUploadListener = fileUploadListener; + if (currentUploadHandler != null) + currentUploadHandler.addUploadListener(fileUploadListener); + } + + @Override + public Node getItem() throws RepositoryException { + return getNode(); + } + + @Override + public String getPartId() { + return getNodeId(); + } + + @Override + public String toString() { + return "Img #" + getPartId(); + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java new file mode 100644 index 000000000..6b54c0a7b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java @@ -0,0 +1,213 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; +import javax.jcr.ItemNotFoundException; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.jcr.JcrException; +import org.eclipse.swt.widgets.Composite; + +/** A composite which can (optionally) manage a JCR Item. */ +public class JcrComposite extends Composite { + private static final long serialVersionUID = -1447009015451153367L; + + private Session session; + + private String nodeId; + private String property = null; + private Node cache; + + /** Regular composite constructor. No layout is set. */ + public JcrComposite(Composite parent, int style) { + super(parent, style); + session = null; + nodeId = null; + } + + public JcrComposite(Composite parent, int style, Item item) { + this(parent, style, item, false); + } + + public JcrComposite(Composite parent, int style, Item item, boolean cacheImmediately) { + super(parent, style); + if (item != null) + try { + this.session = item.getSession(); +// if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { +// // (useless?) optimization: we only save a pointer to the session, +// // not even a reference to the item +// this.nodeId = null; +// } else { + Node node; + Property property = null; + if (item instanceof Node) { + node = (Node) item; + } else {// Property + property = (Property) item; + if (property.isMultiple())// TODO manage property index + throw new UnsupportedOperationException("Multiple properties not supported yet."); + this.property = property.getName(); + node = property.getParent(); + } + this.nodeId = node.getIdentifier(); + if (cacheImmediately) + this.cache = node; +// } + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } catch (RepositoryException e) { + throw new IllegalStateException("Cannot create composite from " + item, e); + } + } + + public synchronized Node getNode() { + try { + if (!itemIsNode()) + throw new IllegalStateException("Item is not a Node"); + return getNodeInternal(); + } catch (RepositoryException e) { + throw new JcrException("Cannot get node " + nodeId, e); + } + } + + private synchronized Node getNodeInternal() throws RepositoryException { + if (cache != null) + return cache; + else if (session != null) + if (nodeId != null) + return session.getNodeByIdentifier(nodeId); + else + return null; + else + return null; + } + + public synchronized String getPropertyName() { + try { + return getProperty().getName(); + } catch (RepositoryException e) { + throw new JcrException("Cannot get property name", e); + } + } + + public synchronized Node getPropertyNode() { + try { + return getProperty().getNode(); + } catch (RepositoryException e) { + throw new JcrException("Cannot get property name", e); + } + } + + public synchronized Property getProperty() { + try { + if (itemIsNode()) + throw new IllegalStateException("Item is not a Property"); + Node node = getNodeInternal(); + if (!node.hasProperty(property)) + throw new IllegalStateException("Property " + property + " is not set on " + node); + return node.getProperty(property); + } catch (RepositoryException e) { + throw new JcrException("Cannot get property " + property + " from node " + nodeId, e); + } + } + + public synchronized boolean itemIsNode() { + return property == null; + } + + public synchronized boolean itemExists() { + if (session == null) + return false; + try { + Node n = session.getNodeByIdentifier(nodeId); + if (!itemIsNode()) + return n.hasProperty(property); + else + return true; + } catch (ItemNotFoundException e) { + return false; + } catch (RepositoryException e) { + throw new JcrException("Cannot check whether node exists", e); + } + } + + /** Set/update the cache or change the node */ + public synchronized void setNode(Node node) { + if (!itemIsNode()) + throw new IllegalArgumentException("Cannot set a Node on a Property"); + + if (node == null) {// clear cache + this.cache = null; + return; + } + + try { +// if (session != null || session != node.getSession())// check session +// throw new IllegalArgumentException("Uncompatible session"); +// if (session == null) + session = node.getSession(); + if (nodeId == null || !nodeId.equals(node.getIdentifier())) { + nodeId = node.getIdentifier(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } catch (RepositoryException e) { + throw new IllegalStateException(e); + } + } + + /** Set/update the cache or change the property */ + public synchronized void setProperty(Property prop) { + if (itemIsNode()) + throw new IllegalArgumentException("Cannot set a Property on a Node"); + + if (prop == null) {// clear cache + this.cache = null; + return; + } + + try { + if (session == null || session != prop.getSession())// check session + throw new IllegalArgumentException("Uncompatible session"); + + Node node = prop.getNode(); + if (nodeId == null || !nodeId.equals(node.getIdentifier()) || !property.equals(prop.getName())) { + nodeId = node.getIdentifier(); + property = prop.getName(); + cache = node; + itemUpdated(); + } else { + cache = node;// set/update cache + } + } catch (RepositoryException e) { + throw new IllegalStateException(e); + } + } + + public synchronized String getNodeId() { + return nodeId; + } + + /** Change the node, does nothing if same. */ + public synchronized void setNodeId(String nodeId) throws RepositoryException { + if (this.nodeId != null && this.nodeId.equals(nodeId)) + return; + this.nodeId = nodeId; + if (cache != null) + cache = session.getNodeByIdentifier(this.nodeId); + itemUpdated(); + } + + protected synchronized void itemUpdated() { + layout(); + } + +// public Session getSession() { +// return session; +// } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java new file mode 100644 index 000000000..e3a5cb473 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/StyledControl.java @@ -0,0 +1,153 @@ +package org.argeo.cms.ui.widgets; + +import javax.jcr.Item; + +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.ui.CmsUiConstants; +import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** Editable text part displaying styled text. */ +public abstract class StyledControl extends JcrComposite implements CmsUiConstants { + private static final long serialVersionUID = -6372283442330912755L; + private Control control; + + private Composite container; + private Composite box; + + protected MouseListener mouseListener; + protected FocusListener focusListener; + + private Boolean editing = Boolean.FALSE; + + private Composite ancestorToLayout; + + public StyledControl(Composite parent, int swtStyle) { + super(parent, swtStyle); + setLayout(CmsSwtUtils.noSpaceGridLayout()); + } + + public StyledControl(Composite parent, int style, Item item) { + super(parent, style, item); + } + + public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) { + super(parent, style, item, cacheImmediately); + } + + protected abstract Control createControl(Composite box, String style); + + protected Composite createBox() { + Composite box = new Composite(container, SWT.INHERIT_DEFAULT); + setContainerLayoutData(box); + box.setLayout(CmsSwtUtils.noSpaceGridLayout(3)); + return box; + } + + protected Composite createContainer() { + Composite container = new Composite(this, SWT.INHERIT_DEFAULT); + setContainerLayoutData(container); + container.setLayout(CmsSwtUtils.noSpaceGridLayout()); + return container; + } + + public Control getControl() { + return control; + } + + protected synchronized Boolean isEditing() { + return editing; + } + + public synchronized void startEditing() { + assert !isEditing(); + editing = true; + // int height = control.getSize().y; + String style = (String) EclipseUiSpecificUtils.getStyleData(control); + clear(false); + refreshControl(style); + + // add the focus listener to the newly created edition control + if (focusListener != null) + control.addFocusListener(focusListener); + } + + public synchronized void stopEditing() { + assert isEditing(); + editing = false; + String style = (String) EclipseUiSpecificUtils.getStyleData(control); + clear(false); + refreshControl(style); + } + + protected void refreshControl(String style) { + control = createControl(box, style); + setControlLayoutData(control); + if (ancestorToLayout != null) + ancestorToLayout.layout(true, true); + else + getParent().layout(true, true); + } + + public void setStyle(String style) { + Object currentStyle = null; + if (control != null) + currentStyle = EclipseUiSpecificUtils.getStyleData(control); + if (currentStyle != null && currentStyle.equals(style)) + return; + + clear(true); + refreshControl(style); + + if (style != null) { + CmsSwtUtils.style(box, style + "_box"); + CmsSwtUtils.style(container, style + "_container"); + } + } + + /** To be overridden */ + protected void setControlLayoutData(Control control) { + control.setLayoutData(CmsSwtUtils.fillWidth()); + } + + /** To be overridden */ + protected void setContainerLayoutData(Composite composite) { + composite.setLayoutData(CmsSwtUtils.fillWidth()); + } + + protected void clear(boolean deep) { + if (deep) { + for (Control control : getChildren()) + control.dispose(); + container = createContainer(); + box = createBox(); + } else { + control.dispose(); + } + } + + public void setMouseListener(MouseListener mouseListener) { + if (this.mouseListener != null && control != null) + control.removeMouseListener(this.mouseListener); + this.mouseListener = mouseListener; + if (control != null && this.mouseListener != null) + control.addMouseListener(mouseListener); + } + + public void setFocusListener(FocusListener focusListener) { + if (this.focusListener != null && control != null) + control.removeFocusListener(this.focusListener); + this.focusListener = focusListener; + if (control != null && this.focusListener != null) + control.addFocusListener(focusListener); + } + + public void setAncestorToLayout(Composite ancestorToLayout) { + this.ancestorToLayout = ancestorToLayout; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java new file mode 100644 index 000000000..e461ed0df --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/TextStyles.java @@ -0,0 +1,37 @@ +package org.argeo.cms.ui.widgets; + +/** Styles references in the CSS. */ +public interface TextStyles { + /** The whole page area */ + public final static String TEXT_AREA = "text_area"; + /** Area providing controls for editing text */ + public final static String TEXT_EDITOR_HEADER = "text_editor_header"; + /** The styled composite for editing the text */ + public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite"; + /** A section */ + public final static String TEXT_SECTION = "text_section"; + /** A paragraph */ + public final static String TEXT_PARAGRAPH = "text_paragraph"; + /** An image */ + public final static String TEXT_IMG = "text_img"; + /** The dialog to edit styled paragraph */ + public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog"; + + /* + * DEFAULT TEXT STYLES + */ + /** Default style for text body */ + public final static String TEXT_DEFAULT = "text_default"; + /** Fixed-width, typically code */ + public final static String TEXT_PRE = "text_pre"; + /** Quote */ + public final static String TEXT_QUOTE = "text_quote"; + /** Title */ + public final static String TEXT_TITLE = "text_title"; + /** Header (to be dynamically completed with the depth, e.g. text_h1) */ + public final static String TEXT_H = "text_h"; + + /** Default style for images */ + public final static String TEXT_IMAGE = "text_image"; + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java new file mode 100644 index 000000000..514f75380 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/widgets/package-info.java @@ -0,0 +1,2 @@ +/** Argeo CMS generic widgets, based on SWT. */ +package org.argeo.cms.ui.widgets; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java new file mode 100644 index 000000000..fdafa982e --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java @@ -0,0 +1,138 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.AbstractTreeContentProvider; +import org.argeo.eclipse.ui.EclipseUiException; + +/** Canonical implementation of tree content provider manipulating JCR nodes. */ +public abstract class AbstractNodeContentProvider extends + AbstractTreeContentProvider { + private static final long serialVersionUID = -4905836490027272569L; + + private final static CmsLog log = CmsLog + .getLog(AbstractNodeContentProvider.class); + + private Session session; + + public AbstractNodeContentProvider(Session session) { + this.session = session; + } + + /** + * Whether this path is a base path (and thus has no parent). By default it + * returns true if path is '/' (root node) + */ + protected Boolean isBasePath(String path) { + // root node + return path.equals("/"); + } + + @Override + public Object[] getChildren(Object element) { + Object[] children; + if (element instanceof Node) { + try { + Node node = (Node) element; + children = getChildren(node); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get children of " + element, e); + } + } else if (element instanceof WrappedNode) { + WrappedNode wrappedNode = (WrappedNode) element; + try { + children = getChildren(wrappedNode.getNode()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get children of " + + wrappedNode, e); + } + } else if (element instanceof NodesWrapper) { + NodesWrapper node = (NodesWrapper) element; + children = node.getChildren(); + } else { + children = super.getChildren(element); + } + + children = sort(element, children); + return children; + } + + /** Do not sort by default. To be overidden to provide custom sort. */ + protected Object[] sort(Object parent, Object[] children) { + return children; + } + + /** + * To be overridden in order to filter out some nodes. Does nothing by + * default. The provided list is a temporary one and can thus be modified + * directly . (e.g. via an iterator) + */ + protected List filterChildren(List children) + throws RepositoryException { + return children; + } + + protected Object[] getChildren(Node node) throws RepositoryException { + List nodes = new ArrayList(); + for (NodeIterator nit = node.getNodes(); nit.hasNext();) + nodes.add(nit.nextNode()); + nodes = filterChildren(nodes); + return nodes.toArray(); + } + + @Override + public Object getParent(Object element) { + if (element instanceof Node) { + Node node = (Node) element; + try { + String path = node.getPath(); + if (isBasePath(path)) + return null; + else + return node.getParent(); + } catch (RepositoryException e) { + log.warn("Cannot get parent of " + element + ": " + e); + return null; + } + } else if (element instanceof WrappedNode) { + WrappedNode wrappedNode = (WrappedNode) element; + return wrappedNode.getParent(); + } else if (element instanceof NodesWrapper) { + NodesWrapper nodesWrapper = (NodesWrapper) element; + return this.getParent(nodesWrapper.getNode()); + } + return super.getParent(element); + } + + @Override + public boolean hasChildren(Object element) { + try { + if (element instanceof Node) { + Node node = (Node) element; + return node.hasNodes(); + } else if (element instanceof WrappedNode) { + WrappedNode wrappedNode = (WrappedNode) element; + return wrappedNode.getNode().hasNodes(); + } else if (element instanceof NodesWrapper) { + NodesWrapper nodesWrapper = (NodesWrapper) element; + return nodesWrapper.hasChildren(); + } + + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot check whether " + element + + " has children", e); + } + return super.hasChildren(element); + } + + public Session getSession() { + return session; + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java new file mode 100644 index 000000000..b880a63c5 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java @@ -0,0 +1,83 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.RepositoryException; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventIterator; +import javax.jcr.observation.EventListener; + +import org.argeo.api.cms.CmsLog; +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.swt.widgets.Display; + +/** + * {@link EventListener} which simplifies running actions within the UI thread. + */ +public abstract class AsyncUiEventListener implements EventListener { + // private final static Log logSuper = LogFactory + // .getLog(AsyncUiEventListener.class); + private final CmsLog logThis = CmsLog.getLog(getClass()); + + private final Display display; + + public AsyncUiEventListener(Display display) { + super(); + this.display = display; + } + + /** Called asynchronously in the UI thread. */ + protected abstract void onEventInUiThread(List events) throws RepositoryException; + + /** + * Whether these events should be processed in the UI or skipped with no UI + * job created. + */ + protected Boolean willProcessInUiThread(List events) throws RepositoryException { + return true; + } + + protected CmsLog getLog() { + return logThis; + } + + public final void onEvent(final EventIterator eventIterator) { + final List events = new ArrayList(); + while (eventIterator.hasNext()) + events.add(eventIterator.nextEvent()); + + if (logThis.isTraceEnabled()) + logThis.trace("Received " + events.size() + " events"); + + try { + if (!willProcessInUiThread(events)) + return; + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot test skip events " + events, e); + } + + // Job job = new Job("JCR Events") { + // protected IStatus run(IProgressMonitor monitor) { + // if (display.isDisposed()) { + // logSuper.warn("Display is disposed cannot update UI"); + // return Status.CANCEL_STATUS; + // } + + if (!display.isDisposed()) + display.asyncExec(new Runnable() { + public void run() { + try { + onEventInUiThread(events); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot process events " + events, e); + } + } + }); + + // return Status.OK_STATUS; + // } + // }; + // job.schedule(); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java new file mode 100644 index 000000000..22ffeafc6 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java @@ -0,0 +1,82 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +/** + * Default label provider to manage node and corresponding UI objects. It + * provides reasonable overwrite-able default for known JCR types. + */ +public class DefaultNodeLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = 1216182332792151235L; + + public String getText(Object element) { + try { + if (element instanceof Node) { + return getText((Node) element); + } else if (element instanceof WrappedNode) { + return getText(((WrappedNode) element).getNode()); + } else if (element instanceof NodesWrapper) { + return getText(((NodesWrapper) element).getNode()); + } + return super.getText(element); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get text for of " + element, e); + } + } + + protected String getText(Node node) throws RepositoryException { + if (node.isNodeType(NodeType.MIX_TITLE) + && node.hasProperty(Property.JCR_TITLE)) + return node.getProperty(Property.JCR_TITLE).getString(); + else + return node.getName(); + } + + @Override + public Image getImage(Object element) { + try { + if (element instanceof Node) { + return getImage((Node) element); + } else if (element instanceof WrappedNode) { + return getImage(((WrappedNode) element).getNode()); + } else if (element instanceof NodesWrapper) { + return getImage(((NodesWrapper) element).getNode()); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot retrieve image for " + element, e); + } + return super.getImage(element); + } + + protected Image getImage(Node node) throws RepositoryException { + // FIXME who uses that? + return null; + } + + @Override + public String getToolTipText(Object element) { + try { + if (element instanceof Node) { + return getToolTipText((Node) element); + } else if (element instanceof WrappedNode) { + return getToolTipText(((WrappedNode) element).getNode()); + } else if (element instanceof NodesWrapper) { + return getToolTipText(((NodesWrapper) element).getNode()); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get tooltip for " + element, e); + } + return super.getToolTipText(element); + } + + protected String getToolTipText(Node node) throws RepositoryException { + return null; + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java new file mode 100644 index 000000000..420154b83 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java @@ -0,0 +1,149 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator; +import org.argeo.eclipse.ui.jcr.lists.RowViewerComparator; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Table; + +/** Utility methods to simplify UI development using SWT (or RWT), jface and JCR. */ +public class JcrUiUtils { + + /** + * Centralizes management of updating property value. Among other to avoid + * infinite loop when the new value is the same as the ones that is already + * stored in JCR. + * + * @return true if the value as changed + */ + public static boolean setJcrProperty(Node node, String propName, + int propertyType, Object value) { + try { + switch (propertyType) { + case PropertyType.STRING: + if ("".equals((String) value) + && (!node.hasProperty(propName) || node + .hasProperty(propName) + && "".equals(node.getProperty(propName) + .getString()))) + // workaround the fact that the Text widget value cannot be + // set to null + return false; + else if (node.hasProperty(propName) + && node.getProperty(propName).getString() + .equals((String) value)) + // nothing changed yet + return false; + else { + node.setProperty(propName, (String) value); + return true; + } + case PropertyType.BOOLEAN: + if (node.hasProperty(propName) + && node.getProperty(propName).getBoolean() == (Boolean) value) + // nothing changed yet + return false; + else { + node.setProperty(propName, (Boolean) value); + return true; + } + case PropertyType.DATE: + if (node.hasProperty(propName) + && node.getProperty(propName).getDate() + .equals((Calendar) value)) + // nothing changed yet + return false; + else { + node.setProperty(propName, (Calendar) value); + return true; + } + case PropertyType.LONG: + Long lgValue = (Long) value; + + if (lgValue == null) + lgValue = 0L; + + if (node.hasProperty(propName) + && node.getProperty(propName).getLong() == lgValue) + // nothing changed yet + return false; + else { + node.setProperty(propName, lgValue); + return true; + } + + default: + throw new EclipseUiException("Unimplemented property save"); + } + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error while setting property", + re); + } + } + + /** + * Creates a new selection adapter in order to provide sorting abitily on a + * SWT Table that display a row list + **/ + public static SelectionAdapter getRowSelectionAdapter(final int index, + final int propertyType, final String selectorName, + final String propertyName, final RowViewerComparator comparator, + final TableViewer viewer) { + SelectionAdapter selectionAdapter = new SelectionAdapter() { + private static final long serialVersionUID = -5738918304901437720L; + + @Override + public void widgetSelected(SelectionEvent e) { + Table table = viewer.getTable(); + comparator.setColumn(propertyType, selectorName, propertyName); + int dir = table.getSortDirection(); + if (table.getSortColumn() == table.getColumn(index)) { + dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; + } else { + dir = SWT.DOWN; + } + table.setSortDirection(dir); + table.setSortColumn(table.getColumn(index)); + viewer.refresh(); + } + }; + return selectionAdapter; + } + + /** + * Creates a new selection adapter in order to provide sorting abitily on a + * swt table that display a row list + **/ + public static SelectionAdapter getNodeSelectionAdapter(final int index, + final int propertyType, final String propertyName, + final NodeViewerComparator comparator, final TableViewer viewer) { + SelectionAdapter selectionAdapter = new SelectionAdapter() { + private static final long serialVersionUID = -1683220869195484625L; + + @Override + public void widgetSelected(SelectionEvent e) { + Table table = viewer.getTable(); + comparator.setColumn(propertyType, propertyName); + int dir = table.getSortDirection(); + if (table.getSortColumn() == table.getColumn(index)) { + dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; + } else { + dir = SWT.DOWN; + } + table.setSortDirection(dir); + table.setSortColumn(table.getColumn(index)); + viewer.refresh(); + } + }; + return selectionAdapter; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java new file mode 100644 index 000000000..7e12becd8 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java @@ -0,0 +1,123 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; + +/** Simplifies writing JCR-based column label provider. */ +public class NodeColumnLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -6586692836928505358L; + + protected String getNodeText(Node node) throws RepositoryException { + return super.getText(node); + } + + protected String getNodeToolTipText(Node node) throws RepositoryException { + return super.getToolTipText(node); + } + + protected Image getNodeImage(Node node) throws RepositoryException { + return super.getImage(node); + } + + protected Font getNodeFont(Node node) throws RepositoryException { + return super.getFont(node); + } + + public Color getNodeBackground(Node node) throws RepositoryException { + return super.getBackground(node); + } + + public Color getNodeForeground(Node node) throws RepositoryException { + return super.getForeground(node); + } + + @Override + public String getText(Object element) { + try { + if (element instanceof Node) + return getNodeText((Node) element); + else if (element instanceof NodeElement) + return getNodeText(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Image getImage(Object element) { + try { + if (element instanceof Node) + return getNodeImage((Node) element); + else if (element instanceof NodeElement) + return getNodeImage(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public String getToolTipText(Object element) { + try { + if (element instanceof Node) + return getNodeToolTipText((Node) element); + else if (element instanceof NodeElement) + return getNodeToolTipText(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Font getFont(Object element) { + try { + if (element instanceof Node) + return getNodeFont((Node) element); + else if (element instanceof NodeElement) + return getNodeFont(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getBackground(Object element) { + try { + if (element instanceof Node) + return getNodeBackground((Node) element); + else if (element instanceof NodeElement) + return getNodeBackground(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getForeground(Object element) { + try { + if (element instanceof Node) + return getNodeForeground((Node) element); + else if (element instanceof NodeElement) + return getNodeForeground(((NodeElement) element).getNode()); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java new file mode 100644 index 000000000..787c92ed5 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java @@ -0,0 +1,8 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; + +/** An element which is related to a JCR {@link Node}. */ +public interface NodeElement { + Node getNode(); +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java new file mode 100644 index 000000000..2f3d64d6b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java @@ -0,0 +1,36 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.IElementComparer; + +/** Element comparer for JCR node, to be used in JFace viewers. */ +public class NodeElementComparer implements IElementComparer { + + public boolean equals(Object a, Object b) { + try { + if ((a instanceof Node) && (b instanceof Node)) { + Node nodeA = (Node) a; + Node nodeB = (Node) b; + return nodeA.getIdentifier().equals(nodeB.getIdentifier()); + } else { + return a.equals(b); + } + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot compare nodes", e); + } + } + + public int hashCode(Object element) { + try { + if (element instanceof Node) + return ((Node) element).getIdentifier().hashCode(); + return element.hashCode(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get hash code", e); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java new file mode 100644 index 000000000..2f808a51f --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java @@ -0,0 +1,71 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; + +/** + * Element of tree which is based on a node, but whose children are not + * necessarily this node children. + */ +public class NodesWrapper { + private final Node node; + + public NodesWrapper(Node node) { + super(); + this.node = node; + } + + protected NodeIterator getNodeIterator() throws RepositoryException { + return node.getNodes(); + } + + protected List getWrappedNodes() throws RepositoryException { + List nodes = new ArrayList(); + for (NodeIterator nit = getNodeIterator(); nit.hasNext();) + nodes.add(new WrappedNode(this, nit.nextNode())); + return nodes; + } + + public Object[] getChildren() { + try { + return getWrappedNodes().toArray(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get wrapped children", e); + } + } + + /** + * @return true by default because we don't want to compute the wrapped + * nodes twice + */ + public Boolean hasChildren() { + return true; + } + + public Node getNode() { + return node; + } + + @Override + public int hashCode() { + return node.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NodesWrapper) + return node.equals(((NodesWrapper) obj).getNode()); + else + return false; + } + + public String toString() { + return "nodes wrapper based on " + node; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java new file mode 100644 index 000000000..934fa6781 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java @@ -0,0 +1,35 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.NodeIterator; +import javax.jcr.RepositoryException; +import javax.jcr.query.Query; + +import org.argeo.jcr.JcrException; +import org.argeo.jcr.JcrUtils; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** Content provider based on a JCR {@link Query}. */ +public class QueryTableContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = 760371460907204722L; + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + Query query = (Query) inputElement; + try { + NodeIterator nit = query.execute().getNodes(); + return JcrUtils.nodeIteratorToList(nit).toArray(); + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java new file mode 100644 index 000000000..70c71efea --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java @@ -0,0 +1,111 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; + +/** Simplifies writing JCR-based column label provider. */ +public class RowColumnLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -6586692836928505358L; + + protected String getRowText(Row row) throws RepositoryException { + return super.getText(row); + } + + protected String getRowToolTipText(Row row) throws RepositoryException { + return super.getToolTipText(row); + } + + protected Image getRowImage(Row row) throws RepositoryException { + return super.getImage(row); + } + + protected Font getRowFont(Row row) throws RepositoryException { + return super.getFont(row); + } + + public Color getRowBackground(Row row) throws RepositoryException { + return super.getBackground(row); + } + + public Color getRowForeground(Row row) throws RepositoryException { + return super.getForeground(row); + } + + @Override + public String getText(Object element) { + try { + if (element instanceof Row) + return getRowText((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Image getImage(Object element) { + try { + if (element instanceof Row) + return getRowImage((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public String getToolTipText(Object element) { + try { + if (element instanceof Row) + return getRowToolTipText((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Font getFont(Object element) { + try { + if (element instanceof Row) + return getRowFont((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getBackground(Object element) { + try { + if (element instanceof Row) + return getRowBackground((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + + @Override + public Color getForeground(Object element) { + try { + if (element instanceof Row) + return getRowForeground((Row) element); + else + throw new IllegalArgumentException("Unsupported element type " + element.getClass()); + } catch (RepositoryException e) { + throw new IllegalStateException("Repository exception when accessing " + element, e); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java new file mode 100644 index 000000000..cb235d76b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java @@ -0,0 +1,59 @@ +package org.argeo.eclipse.ui.jcr; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.jcr.JcrUtils; + +/** Simple JCR node content provider taking a list of String as base path. */ +public class SimpleNodeContentProvider extends AbstractNodeContentProvider { + private static final long serialVersionUID = -8245193308831384269L; + private final List basePaths; + private Boolean mkdirs = false; + + public SimpleNodeContentProvider(Session session, String... basePaths) { + this(session, Arrays.asList(basePaths)); + } + + public SimpleNodeContentProvider(Session session, List basePaths) { + super(session); + this.basePaths = basePaths; + } + + @Override + protected Boolean isBasePath(String path) { + if (basePaths.contains(path)) + return true; + return super.isBasePath(path); + } + + public Object[] getElements(Object inputElement) { + try { + List baseNodes = new ArrayList(); + for (String basePath : basePaths) + if (mkdirs && !getSession().itemExists(basePath)) + baseNodes.add(JcrUtils.mkdirs(getSession(), basePath)); + else + baseNodes.add(getSession().getNode(basePath)); + return baseNodes.toArray(); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot get base nodes for " + basePaths, + e); + } + } + + public List getBasePaths() { + return basePaths; + } + + public void setMkdirs(Boolean mkdirs) { + this.mkdirs = mkdirs; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java new file mode 100644 index 000000000..1ce315429 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java @@ -0,0 +1,80 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.version.Version; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +/** Simplifies writing JCR-based column label provider. */ +public class VersionColumnLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -6117690082313161159L; + + protected String getVersionText(Version version) throws RepositoryException { + return super.getText(version); + } + + protected String getVersionToolTipText(Version version) throws RepositoryException { + return super.getToolTipText(version); + } + + protected Image getVersionImage(Version version) throws RepositoryException { + return super.getImage(version); + } + + protected String getUserName(Version version) throws RepositoryException { + Node node = version.getFrozenNode(); + if(node.hasProperty(Property.JCR_LAST_MODIFIED_BY)) + return node.getProperty(Property.JCR_LAST_MODIFIED_BY).getString(); + if(node.hasProperty(Property.JCR_CREATED_BY)) + return node.getProperty(Property.JCR_CREATED_BY).getString(); + return null; + } + +// protected String getActivityTitle(Version version) throws RepositoryException { +// Node activity = getActivity(version); +// if (activity == null) +// return null; +// if (activity.hasProperty("jcr:activityTitle")) +// return activity.getProperty("jcr:activityTitle").getString(); +// else +// return activity.getName(); +// } +// +// protected Node getActivity(Version version) throws RepositoryException { +// if (version.hasProperty(Property.JCR_ACTIVITY)) { +// return version.getProperty(Property.JCR_ACTIVITY).getNode(); +// } else +// return null; +// } + + @Override + public String getText(Object element) { + try { + return getVersionText((Version) element); + } catch (RepositoryException e) { + throw new RuntimeException("Runtime repository exception when accessing " + element, e); + } + } + + @Override + public Image getImage(Object element) { + try { + return getVersionImage((Version) element); + } catch (RepositoryException e) { + throw new RuntimeException("Runtime repository exception when accessing " + element, e); + } + } + + @Override + public String getToolTipText(Object element) { + try { + return getVersionToolTipText((Version) element); + } catch (RepositoryException e) { + throw new RuntimeException("Runtime repository exception when accessing " + element, e); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java new file mode 100644 index 000000000..32e5d30c1 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java @@ -0,0 +1,27 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.version.VersionHistory; + +import org.argeo.jcr.Jcr; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** Content provider based on a {@link VersionHistory}. */ +public class VersionHistoryContentProvider implements IStructuredContentProvider { + private static final long serialVersionUID = -4921107883428887012L; + + @Override + public void dispose() { + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + VersionHistory versionHistory = (VersionHistory) inputElement; + return Jcr.getLinearVersions(versionHistory).toArray(); + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java new file mode 100644 index 000000000..43df1fe01 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java @@ -0,0 +1,41 @@ +package org.argeo.eclipse.ui.jcr; + +import javax.jcr.Node; + +/** Wrap a node (created from a {@link NodesWrapper}) */ +public class WrappedNode { + private final NodesWrapper parent; + private final Node node; + + public WrappedNode(NodesWrapper parent, Node node) { + super(); + this.parent = parent; + this.node = node; + } + + public NodesWrapper getParent() { + return parent; + } + + public Node getNode() { + return node; + } + + public String toString() { + return "wrapped " + node; + } + + @Override + public int hashCode() { + return node.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WrappedNode) + return node.equals(((WrappedNode) obj).getNode()); + else + return false; + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java new file mode 100644 index 000000000..c5dd73317 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java @@ -0,0 +1,116 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.query.Row; + +import org.argeo.eclipse.ui.ColumnDefinition; + +/** + * Utility object to manage column in various tables and extracts displaying + * data from JCR + */ +public class JcrColumnDefinition extends ColumnDefinition { + private final static int DEFAULT_COLUMN_SIZE = 120; + + private String selectorName; + private String propertyName; + private int propertyType; + private int columnSize; + + /** + * Use this kind of columns to configure a table that displays JCR + * {@link Row} + * + * @param selectorName + * @param propertyName + * @param propertyType + * @param headerLabel + */ + public JcrColumnDefinition(String selectorName, String propertyName, + int propertyType, String headerLabel) { + super(new SimpleJcrRowLabelProvider(selectorName, propertyName), + headerLabel); + this.selectorName = selectorName; + this.propertyName = propertyName; + this.propertyType = propertyType; + this.columnSize = DEFAULT_COLUMN_SIZE; + } + + /** + * Use this kind of columns to configure a table that displays JCR + * {@link Row} + * + * @param selectorName + * @param propertyName + * @param propertyType + * @param headerLabel + * @param columnSize + */ + public JcrColumnDefinition(String selectorName, String propertyName, + int propertyType, String headerLabel, int columnSize) { + super(new SimpleJcrRowLabelProvider(selectorName, propertyName), + headerLabel, columnSize); + this.selectorName = selectorName; + this.propertyName = propertyName; + this.propertyType = propertyType; + this.columnSize = columnSize; + } + + /** + * Use this kind of columns to configure a table that displays JCR + * {@link Node} + * + * @param propertyName + * @param propertyType + * @param headerLabel + * @param columnSize + */ + public JcrColumnDefinition(String propertyName, int propertyType, + String headerLabel, int columnSize) { + super(new SimpleJcrNodeLabelProvider(propertyName), headerLabel, + columnSize); + this.propertyName = propertyName; + this.propertyType = propertyType; + this.columnSize = columnSize; + } + + public String getSelectorName() { + return selectorName; + } + + public void setSelectorName(String selectorName) { + this.selectorName = selectorName; + } + + public String getPropertyName() { + return propertyName; + } + + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + public int getPropertyType() { + return propertyType; + } + + public void setPropertyType(int propertyType) { + this.propertyType = propertyType; + } + + public int getColumnSize() { + return columnSize; + } + + public void setColumnSize(int columnSize) { + this.columnSize = columnSize; + } + + public String getHeaderLabel() { + return super.getLabel(); + } + + public void setHeaderLabel(String headerLabel) { + super.setLabel(headerLabel); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java new file mode 100644 index 000000000..341b3abee --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java @@ -0,0 +1,190 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import java.math.BigDecimal; +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.jcr.ValueFormatException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; + +/** + * Base comparator to enable ordering on Table or Tree viewer that display Jcr + * Nodes. + * + * Note that the following snippet must be added before setting the comparator + * to the corresponding control: + * // IMPORTANT: initialize comparator before setting it + * JcrColumnDefinition firstCol = colDefs.get(0); + * comparator.setColumn(firstCol.getPropertyType(), + * firstCol.getPropertyName()); + * viewer.setComparator(comparator); + */ +public class NodeViewerComparator extends ViewerComparator { + private static final long serialVersionUID = -7782916140737279027L; + + protected String propertyName; + + protected int propertyType; + public static final int ASCENDING = 0, DESCENDING = 1; + protected int direction = DESCENDING; + + public NodeViewerComparator() { + } + + /** + * e1 and e2 must both be Jcr nodes. + * + * @param viewer + * @param e1 + * @param e2 + * @return + */ + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + int rc = 0; + long lc = 0; + + try { + Node n1 = (Node) e1; + Node n2 = (Node) e2; + + Value v1 = null; + Value v2 = null; + if (n1.hasProperty(propertyName)) + v1 = n1.getProperty(propertyName).getValue(); + if (n2.hasProperty(propertyName)) + v2 = n2.getProperty(propertyName).getValue(); + + if (v2 == null && v1 == null) + return 0; + else if (v2 == null) + return -1; + else if (v1 == null) + return 1; + + switch (propertyType) { + case PropertyType.STRING: + rc = v1.getString().compareTo(v2.getString()); + break; + case PropertyType.BOOLEAN: + boolean b1 = v1.getBoolean(); + boolean b2 = v2.getBoolean(); + if (b1 == b2) + rc = 0; + else + // we assume true is greater than false + rc = b1 ? 1 : -1; + break; + case PropertyType.DATE: + Calendar c1 = v1.getDate(); + Calendar c2 = v2.getDate(); + if (c1 == null || c2 == null) + // log.trace("undefined date"); + ; + lc = c1.getTimeInMillis() - c2.getTimeInMillis(); + if (lc < Integer.MIN_VALUE) + rc = -1; + else if (lc > Integer.MAX_VALUE) + rc = 1; + else + rc = (int) lc; + break; + case PropertyType.LONG: + long l1; + long l2; + // TODO Sometimes an empty string is set instead of a long + try { + l1 = v1.getLong(); + } catch (ValueFormatException ve) { + l1 = 0; + } + try { + l2 = v2.getLong(); + } catch (ValueFormatException ve) { + l2 = 0; + } + + lc = l1 - l2; + if (lc < Integer.MIN_VALUE) + rc = -1; + else if (lc > Integer.MAX_VALUE) + rc = 1; + else + rc = (int) lc; + break; + case PropertyType.DECIMAL: + BigDecimal bd1 = v1.getDecimal(); + BigDecimal bd2 = v2.getDecimal(); + rc = bd1.compareTo(bd2); + break; + case PropertyType.DOUBLE: + Double d1 = v1.getDouble(); + Double d2 = v2.getDouble(); + rc = d1.compareTo(d2); + break; + default: + throw new EclipseUiException( + "Unimplemented comparaison for PropertyType " + + propertyType); + } + // If descending order, flip the direction + if (direction == DESCENDING) { + rc = -rc; + } + + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error " + + "while comparing nodes", re); + } + return rc; + } + + /** + * @param propertyType + * Corresponding JCR type + * @param propertyName + * name of the property to use. + */ + public void setColumn(int propertyType, String propertyName) { + if (this.propertyName != null && this.propertyName.equals(propertyName)) { + // Same column as last sort; toggle the direction + direction = 1 - direction; + } else { + // New column; do an ascending sort + this.propertyType = propertyType; + this.propertyName = propertyName; + direction = ASCENDING; + } + } + + // Getters and setters + protected String getPropertyName() { + return propertyName; + } + + protected void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + protected int getPropertyType() { + return propertyType; + } + + protected void setPropertyType(int propertyType) { + this.propertyType = propertyType; + } + + protected int getDirection() { + return direction; + } + + protected void setDirection(int direction) { + this.direction = direction; + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java new file mode 100644 index 000000000..455fb0dcd --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java @@ -0,0 +1,62 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.Viewer; + +/** + * Base comparator to enable ordering on Table or Tree viewer that display Jcr + * rows + */ +public class RowViewerComparator extends NodeViewerComparator { + private static final long serialVersionUID = 7020939505172625113L; + protected String selectorName; + + public RowViewerComparator() { + } + + /** + * e1 and e2 must both be Jcr rows. + * + * @param viewer + * @param e1 + * @param e2 + * @return + */ + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + try { + Node n1 = ((Row) e1).getNode(selectorName); + Node n2 = ((Row) e2).getNode(selectorName); + return super.compare(viewer, n1, n2); + } catch (RepositoryException re) { + throw new EclipseUiException("Unexpected error " + + "while comparing nodes", re); + } + } + + /** + * @param propertyType + * Corresponding JCR type + * @param propertyName + * name of the property to use. + */ + public void setColumn(int propertyType, String selectorName, + String propertyName) { + if (this.selectorName != null && getPropertyName() != null + && this.selectorName.equals(selectorName) + && this.getPropertyName().equals(propertyName)) { + // Same column as last sort; toggle the direction + setDirection(1 - getDirection()); + } else { + // New column; do a descending sort + setPropertyType(propertyType); + setPropertyName(propertyName); + this.selectorName = selectorName; + setDirection(NodeViewerComparator.ASCENDING); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java new file mode 100644 index 000000000..aa2e3375c --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java @@ -0,0 +1,120 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; + +import javax.jcr.Node; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import javax.jcr.Value; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** Base implementation of a label provider for controls that display JCR Nodes */ +public class SimpleJcrNodeLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = -5215787695436221993L; + + private final static String DEFAULT_DATE_FORMAT = "EEE, dd MMM yyyy"; + private final static String DEFAULT_NUMBER_FORMAT = "#,##0.0"; + + private DateFormat dateFormat; + private NumberFormat numberFormat; + + final private String propertyName; + + /** + * Default Label provider for a given property of a node. Using default + * pattern for date and number formating + */ + public SimpleJcrNodeLabelProvider(String propertyName) { + this.propertyName = propertyName; + dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT); + numberFormat = DecimalFormat.getInstance(); + ((DecimalFormat) numberFormat).applyPattern(DEFAULT_NUMBER_FORMAT); + } + + /** + * Label provider for a given property of a node optionally precising date + * and/or number format patterns + */ + public SimpleJcrNodeLabelProvider(String propertyName, + String dateFormatPattern, String numberFormatPattern) { + this.propertyName = propertyName; + dateFormat = new SimpleDateFormat( + dateFormatPattern == null ? DEFAULT_DATE_FORMAT + : dateFormatPattern); + numberFormat = DecimalFormat.getInstance(); + ((DecimalFormat) numberFormat) + .applyPattern(numberFormatPattern == null ? DEFAULT_NUMBER_FORMAT + : numberFormatPattern); + } + + @Override + public String getText(Object element) { + try { + Node currNode = (Node) element; + + if (currNode.hasProperty(propertyName)) { + if (currNode.getProperty(propertyName).isMultiple()) { + StringBuilder builder = new StringBuilder(); + for (Value value : currNode.getProperty(propertyName) + .getValues()) { + String currStr = getSingleValueAsString(value); + if (notEmptyString(currStr)) + builder.append(currStr).append("; "); + } + if (builder.length() > 0) + builder.deleteCharAt(builder.length() - 2); + + return builder.toString(); + } else + return getSingleValueAsString(currNode.getProperty( + propertyName).getValue()); + } else + return ""; + } catch (RepositoryException re) { + throw new EclipseUiException("Unable to get text from row", re); + } + } + + private String getSingleValueAsString(Value value) + throws RepositoryException { + switch (value.getType()) { + case PropertyType.STRING: + return value.getString(); + case PropertyType.BOOLEAN: + return "" + value.getBoolean(); + case PropertyType.DATE: + return dateFormat.format(value.getDate().getTime()); + case PropertyType.LONG: + return "" + value.getLong(); + case PropertyType.DECIMAL: + return numberFormat.format(value.getDecimal()); + case PropertyType.DOUBLE: + return numberFormat.format(value.getDouble()); + case PropertyType.NAME: + return value.getString(); + default: + throw new EclipseUiException("Unimplemented label provider " + + "for property type " + value.getType() + + " while getting property " + propertyName + " - value: " + + value.getString()); + + } + } + + private boolean notEmptyString(String string) { + return string != null && !"".equals(string.trim()); + } + + public void setDateFormat(String dateFormatPattern) { + dateFormat = new SimpleDateFormat(dateFormatPattern); + } + + public void setNumberFormat(String numberFormatPattern) { + ((DecimalFormat) numberFormat).applyPattern(numberFormatPattern); + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java new file mode 100644 index 000000000..5d421f64b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java @@ -0,0 +1,47 @@ +package org.argeo.eclipse.ui.jcr.lists; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.query.Row; + +import org.argeo.eclipse.ui.EclipseUiException; + +/** + * Base implementation of a label provider for widgets that display JCR Rows. + */ +public class SimpleJcrRowLabelProvider extends SimpleJcrNodeLabelProvider { + private static final long serialVersionUID = -3414654948197181740L; + + final private String selectorName; + + /** + * Default Label provider for a given property of a row. Using default + * pattern for date and number formating + */ + public SimpleJcrRowLabelProvider(String selectorName, String propertyName) { + super(propertyName); + this.selectorName = selectorName; + } + + /** + * Label provider for a given property of a node optionally precising date + * and/or number format patterns + */ + public SimpleJcrRowLabelProvider(String selectorName, String propertyName, + String dateFormatPattern, String numberFormatPattern) { + super(propertyName, dateFormatPattern, numberFormatPattern); + this.selectorName = selectorName; + } + + @Override + public String getText(Object element) { + try { + Row currRow = (Row) element; + Node currNode = currRow.getNode(selectorName); + return super.getText(currNode); + } catch (RepositoryException re) { + throw new EclipseUiException("Unable to get Node " + selectorName + + " from row " + element, re); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java new file mode 100644 index 000000000..3678aab88 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace JCR utilities for lists. */ +package org.argeo.eclipse.ui.jcr.lists; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java new file mode 100644 index 000000000..19e3cc3ff --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace JCR utilities. */ +package org.argeo.eclipse.ui.jcr; \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java new file mode 100644 index 000000000..c82e666d1 --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java @@ -0,0 +1,129 @@ +package org.argeo.eclipse.ui.jcr.util; + +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.FileProvider; + +/** + * Implements a FileProvider for UI purposes. Note that it might not be very + * reliable as long as we have not fixed login and multi repository issues that + * will be addressed in the next version. + * + * NOTE: id used here is the real id of the JCR Node, not the JCR Path + * + * Relies on common approach for JCR file handling implementation. + * + */ +@SuppressWarnings("deprecation") +public class JcrFileProvider implements FileProvider { + + // private Object[] rootNodes; + private Node refNode; + + /** + * Must be set in order for the provider to be able to get current session + * and thus have the ability to get the file node corresponding to a given + * file ID + * + * @param refNode + */ + public void setReferenceNode(Node refNode) { + // FIXME : this introduces some concurrency ISSUES. + this.refNode = refNode; + } + + public byte[] getByteArrayFileFromId(String fileId) { + InputStream fis = null; + byte[] ba = null; + Node child = getFileNodeFromId(fileId); + try { + fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); + ba = IOUtils.toByteArray(fis); + + } catch (Exception e) { + throw new EclipseUiException("Stream error while opening file", e); + } finally { + IOUtils.closeQuietly(fis); + } + return ba; + } + + public InputStream getInputStreamFromFileId(String fileId) { + try { + InputStream fis = null; + + Node child = getFileNodeFromId(fileId); + fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); + return fis; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot get stream from file node for Id " + fileId, re); + } + } + + /** + * Throws an exception if the node is not found in the current repository (a + * bit like a FileNotFoundException) + * + * @param fileId + * @return Returns the child node of the nt:file node. It is the child node + * that have the jcr:data property where actual file is stored. + * never null + */ + private Node getFileNodeFromId(String fileId) { + try { + Node result = refNode.getSession().getNodeByIdentifier(fileId); + + // rootNodes: for (int j = 0; j < rootNodes.length; j++) { + // // in case we have a classic JCR Node + // if (rootNodes[j] instanceof Node) { + // Node curNode = (Node) rootNodes[j]; + // if (result != null) + // break rootNodes; + // } // Case of a repository Node + // else if (rootNodes[j] instanceof RepositoryNode) { + // Object[] nodes = ((RepositoryNode) rootNodes[j]) + // .getChildren(); + // for (int i = 0; i < nodes.length; i++) { + // Node node = (Node) nodes[i]; + // result = node.getSession().getNodeByIdentifier(fileId); + // if (result != null) + // break rootNodes; + // } + // } + // } + + // Sanity checks + if (result == null) + throw new EclipseUiException("File node not found for ID" + fileId); + + Node child = null; + + boolean isValid = true; + if (!result.isNodeType(NodeType.NT_FILE)) + // useless: mandatory child node + // || !result.hasNode(Property.JCR_CONTENT)) + isValid = false; + else { + child = result.getNode(Property.JCR_CONTENT); + if (!(child.isNodeType(NodeType.NT_RESOURCE) || child.hasProperty(Property.JCR_DATA))) + isValid = false; + } + + if (!isValid) + throw new EclipseUiException("ERROR: In the current implemented model, '" + NodeType.NT_FILE + + "' file node must have a child node named jcr:content " + + "that has a BINARY Property named jcr:data " + "where the actual data is stored"); + return child; + + } catch (RepositoryException re) { + throw new EclipseUiException("Erreur while getting file node of ID " + fileId, re); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java new file mode 100644 index 000000000..fb123992f --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java @@ -0,0 +1,21 @@ +package org.argeo.eclipse.ui.jcr.util; + +import java.util.Comparator; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; + +/** Compares two JCR items (node or properties) based on their names. */ +public class JcrItemsComparator implements Comparator { + public int compare(Item o1, Item o2) { + try { + // TODO: put folder before files + return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); + } catch (RepositoryException e) { + throw new EclipseUiException("Cannot compare " + o1 + " and " + o2, e); + } + } + +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java new file mode 100644 index 000000000..54b795f3b --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java @@ -0,0 +1,36 @@ +package org.argeo.eclipse.ui.jcr.util; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.argeo.eclipse.ui.EclipseUiException; +import org.eclipse.jface.viewers.IElementComparer; + +/** Compare JCR nodes based on their JCR identifiers, for use in JFace viewers. */ +public class NodeViewerComparer implements IElementComparer { + + // force comparison on Node IDs only. + public boolean equals(Object elementA, Object elementB) { + if (!(elementA instanceof Node) || !(elementB instanceof Node)) { + return elementA == null ? elementB == null : elementA + .equals(elementB); + } else { + + boolean result = false; + try { + String idA = ((Node) elementA).getIdentifier(); + String idB = ((Node) elementB).getIdentifier(); + result = idA == null ? idB == null : idA.equals(idB); + } catch (RepositoryException re) { + throw new EclipseUiException("cannot compare nodes", re); + } + + return result; + } + } + + public int hashCode(Object element) { + // TODO enhanced this method. + return element.getClass().toString().hashCode(); + } +} \ No newline at end of file diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java new file mode 100644 index 000000000..291d579ac --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java @@ -0,0 +1,98 @@ +package org.argeo.eclipse.ui.jcr.util; + +import java.io.InputStream; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.argeo.eclipse.ui.EclipseUiException; +import org.argeo.eclipse.ui.FileProvider; + +/** + * Implements a FileProvider for UI purposes. Unlike the + * JcrFileProvider , it relies on a single session and manages + * nodes with path only. + * + * Note that considered id is the JCR path + * + * Relies on common approach for JCR file handling implementation. + */ +@SuppressWarnings("deprecation") +public class SingleSessionFileProvider implements FileProvider { + + private Session session; + + public SingleSessionFileProvider(Session session) { + this.session = session; + } + + public byte[] getByteArrayFileFromId(String fileId) { + InputStream fis = null; + byte[] ba = null; + Node child = getFileNodeFromId(fileId); + try { + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + ba = IOUtils.toByteArray(fis); + + } catch (Exception e) { + throw new EclipseUiException("Stream error while opening file", e); + } finally { + IOUtils.closeQuietly(fis); + } + return ba; + } + + public InputStream getInputStreamFromFileId(String fileId) { + try { + InputStream fis = null; + + Node child = getFileNodeFromId(fileId); + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + return fis; + } catch (RepositoryException re) { + throw new EclipseUiException("Cannot get stream from file node for Id " + + fileId, re); + } + } + + /** + * + * @param fileId + * @return Returns the child node of the nt:file node. It is the child node + * that have the jcr:data property where actual file is stored. + * never null + */ + private Node getFileNodeFromId(String fileId) { + try { + Node result = null; + result = session.getNode(fileId); + + // Sanity checks + if (result == null) + throw new EclipseUiException("File node not found for ID" + fileId); + + // Ensure that the node have the correct type. + if (!result.isNodeType(NodeType.NT_FILE)) + throw new EclipseUiException( + "Cannot open file children Node that are not of " + + NodeType.NT_RESOURCE + " type."); + + Node child = result.getNodes().nextNode(); + if (child == null || !child.isNodeType(NodeType.NT_RESOURCE)) + throw new EclipseUiException( + "ERROR: IN the current implemented model, " + + NodeType.NT_FILE + + " file node must have one and only one child of the nt:ressource, where actual data is stored"); + return child; + } catch (RepositoryException re) { + throw new EclipseUiException("Erreur while getting file node of ID " + + fileId, re); + } + } +} diff --git a/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java new file mode 100644 index 000000000..016348cda --- /dev/null +++ b/jcr/org.argeo.cms.jcr.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java @@ -0,0 +1,2 @@ +/** Generic SWT/JFace JCR helpers. */ +package org.argeo.eclipse.ui.jcr.util; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/.classpath b/jcr/org.argeo.cms.ui/.classpath deleted file mode 100644 index 81fe078c2..000000000 --- a/jcr/org.argeo.cms.ui/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/jcr/org.argeo.cms.ui/.project b/jcr/org.argeo.cms.ui/.project deleted file mode 100644 index e52eb8ef7..000000000 --- a/jcr/org.argeo.cms.ui/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.argeo.cms.ui - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/jcr/org.argeo.cms.ui/META-INF/.gitignore b/jcr/org.argeo.cms.ui/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/jcr/org.argeo.cms.ui/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/jcr/org.argeo.cms.ui/bnd.bnd b/jcr/org.argeo.cms.ui/bnd.bnd deleted file mode 100644 index c3c609c70..000000000 --- a/jcr/org.argeo.cms.ui/bnd.bnd +++ /dev/null @@ -1,23 +0,0 @@ -Bundle-Activator: org.argeo.cms.ui.internal.Activator -Bundle-ActivationPolicy: lazy - -Import-Package: org.eclipse.swt,\ -org.eclipse.jface.window,\ -org.eclipse.core.commands,\ -javax.jcr.security,\ -org.eclipse.rap.fileupload;version="[2.1,4)",\ -org.eclipse.rap.rwt;version="[2.1,4)",\ -org.eclipse.rap.rwt.application;version="[2.1,4)",\ -org.eclipse.rap.rwt.client;version="[2.1,4)",\ -org.eclipse.rap.rwt.client.service;version="[2.1,4)",\ -org.eclipse.rap.rwt.service;version="[2.1,4)",\ -org.eclipse.rap.rwt.widgets;version="[2.1,4)",\ -org.osgi.*;version=0.0.0,\ -javax.servlet.*;version="[3,5)",\ -* - -## TODO: in order to enable single sourcing, we have introduced dummy RAP packages -# in the RCP specific bundle org.argeo.eclipse.ui.rap. -# this was working with RAP 2.x but since we upgrade Rap to 3.1.x, -# there is a version range issue cause org.argeo.eclipse.ui.rap bundle is still in 2.x -# We enable large RAP version range as a workaround that must be fixed \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/build.properties b/jcr/org.argeo.cms.ui/build.properties deleted file mode 100644 index c6baffa00..000000000 --- a/jcr/org.argeo.cms.ui/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - icons/ diff --git a/jcr/org.argeo.cms.ui/icons/loading.gif b/jcr/org.argeo.cms.ui/icons/loading.gif deleted file mode 100644 index 3288d1035..000000000 Binary files a/jcr/org.argeo.cms.ui/icons/loading.gif and /dev/null differ diff --git a/jcr/org.argeo.cms.ui/icons/noPic-goldenRatio-640px.png b/jcr/org.argeo.cms.ui/icons/noPic-goldenRatio-640px.png deleted file mode 100644 index 039650638..000000000 Binary files a/jcr/org.argeo.cms.ui/icons/noPic-goldenRatio-640px.png and /dev/null differ diff --git a/jcr/org.argeo.cms.ui/icons/noPic-square-640px.png b/jcr/org.argeo.cms.ui/icons/noPic-square-640px.png deleted file mode 100644 index 8e3abb518..000000000 Binary files a/jcr/org.argeo.cms.ui/icons/noPic-square-640px.png and /dev/null differ diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiConstants.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiConstants.java deleted file mode 100644 index c6a62e9e0..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiConstants.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.argeo.cms.ui; - -import org.argeo.api.cms.ux.Cms2DSize; - -/** Commons constants */ -@Deprecated -public interface CmsUiConstants { - // DATAKEYS -// public final static String STYLE = EclipseUiConstants.CSS_CLASS; -// public final static String MARKUP = EclipseUiConstants.MARKUP_SUPPORT; - @Deprecated - /* RWT.CUSTOM_ITEM_HEIGHT */ - public final static String ITEM_HEIGHT = "org.eclipse.rap.rwt.customItemHeight"; - - // EVENT DETAILS - @Deprecated - /* RWT.HYPERLINK */ - public final static int HYPERLINK = 1 << 26; - - // STANDARD RESOURCES - public final static String LOADING_IMAGE = "icons/loading.gif"; - - public final static String NO_IMAGE = "icons/noPic-square-640px.png"; - public final static Cms2DSize NO_IMAGE_SIZE = new Cms2DSize(320, 320); - public final static Float NO_IMAGE_RATIO = 1f; - // MISCEALLENEOUS - String DATE_TIME_FORMAT = "dd/MM/yyyy, HH:mm"; -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java deleted file mode 100644 index 5f2377be5..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/CmsUiProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.argeo.cms.ui; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.acr.Content; -import org.argeo.cms.jcr.acr.JcrContent; -import org.argeo.cms.swt.acr.SwtUiProvider; -import org.argeo.jcr.JcrException; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Stateless factory building an SWT user interface given a JCR context. */ -public interface CmsUiProvider extends SwtUiProvider { - /** - * Initialises a user interface. - * - * @param parent the parent composite - * @param context a context node (holding the JCR underlying session), or null - */ - default Control createUi(Composite parent, Node context) throws RepositoryException { - // does nothing by default - return null; - } - - default Control createUiPart(Composite parent, Node context) { - try { - return createUi(parent, context); - } catch (RepositoryException e) { - throw new JcrException("Cannot create UI for context " + context, e); - } - } - - @Override - default Control createUiPart(Composite parent, Content context) { - if (context == null) - return createUiPart(parent, (Node) null); - if (context instanceof JcrContent) { - Node node = ((JcrContent) context).getJcrNode(); - return createUiPart(parent, node); - } else { -// CmsLog.getLog(CmsUiProvider.class) -// .warn("In " + getClass() + ", content " + context + " is not compatible with JCR."); -// return createUiPart(parent, (Node) null); - - throw new IllegalArgumentException( - "In " + getClass() + ", content " + context + " is not compatible with JCR"); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java deleted file mode 100644 index 5d77c156c..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/LifeCycleUiProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.argeo.cms.ui; - -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -/** CmsUiProvider notified of initialisation with a system session. */ -public interface LifeCycleUiProvider extends CmsUiProvider { - public void init(Session adminSession) throws RepositoryException; - - public void destroy(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java deleted file mode 100644 index 4ce468826..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/AbstractFormPart.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; -/** - * AbstractFormPart implements IFormPart interface and can be used as a - * convenient base class for concrete form parts. If a method contains - * code that must be called, look for instructions to call 'super' - * when overriding. - * - * @see org.eclipse.ui.forms.widgets.Section - * @since 1.0 - */ -public abstract class AbstractFormPart implements IFormPart { - private IManagedForm managedForm; - private boolean dirty = false; - private boolean stale = true; - /** - * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm) - */ - public void initialize(IManagedForm form) { - this.managedForm = form; - } - /** - * Returns the form that manages this part. - * - * @return the managed form - */ - public IManagedForm getManagedForm() { - return managedForm; - } - /** - * Disposes the part. Subclasses should override to release any system - * resources. - */ - public void dispose() { - } - /** - * Commits the part. Subclasses should call 'super' when overriding. - * - * @param onSave - * true if the request to commit has arrived as a - * result of the 'save' action. - */ - public void commit(boolean onSave) { - dirty = false; - } - /** - * Sets the overall form input. Subclases may elect to override the method - * and adjust according to the form input. - * - * @param input - * the form input object - * @return false - */ - public boolean setFormInput(Object input) { - return false; - } - /** - * Instructs the part to grab keyboard focus. - */ - public void setFocus() { - } - /** - * Refreshes the section after becoming stale (falling behind data in the - * model). Subclasses must call 'super' when overriding this method. - */ - public void refresh() { - stale = false; - // since we have refreshed, any changes we had in the - // part are gone and we are not dirty - dirty = false; - } - /** - * Marks the part dirty. Subclasses should call this method as a result of - * user interaction with the widgets in the section. - */ - public void markDirty() { - dirty = true; - managedForm.dirtyStateChanged(); - } - /** - * Tests whether the part is dirty i.e. its widgets have state that is - * newer than the data in the model. - * - * @return true if the part is dirty, false - * otherwise. - */ - public boolean isDirty() { - return dirty; - } - /** - * Tests whether the part is stale i.e. its widgets have state that is - * older than the data in the model. - * - * @return true if the part is stale, false - * otherwise. - */ - public boolean isStale() { - return stale; - } - /** - * Marks the part stale. Subclasses should call this method as a result of - * model notification that indicates that the content of the section is no - * longer in sync with the model. - */ - public void markStale() { - stale = true; - managedForm.staleStateChanged(); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java deleted file mode 100644 index 32b031b86..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormColors.java +++ /dev/null @@ -1,730 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.RGB; -//import org.eclipse.swt.internal.graphics.Graphics; -import org.eclipse.swt.widgets.Display; - -/** - * Manages colors that will be applied to forms and form widgets. The colors are - * chosen to make the widgets look correct in the editor area. If a different - * set of colors is needed, subclass this class and override 'initialize' and/or - * 'initializeColors'. - * - * @since 1.0 - */ -public class FormColors { - /** - * Key for the form title foreground color. - * - * @deprecated use IFormColors.TITLE. - */ - public static final String TITLE = IFormColors.TITLE; - - /** - * Key for the tree/table border color. - * - * @deprecated use IFormColors.BORDER - */ - public static final String BORDER = IFormColors.BORDER; - - /** - * Key for the section separator color. - * - * @deprecated use IFormColors.SEPARATOR. - */ - public static final String SEPARATOR = IFormColors.SEPARATOR; - - /** - * Key for the section title bar background. - * - * @deprecated use IFormColors.TB_BG - */ - public static final String TB_BG = IFormColors.TB_BG; - - /** - * Key for the section title bar foreground. - * - * @deprecated use IFormColors.TB_FG - */ - public static final String TB_FG = IFormColors.TB_FG; - - /** - * Key for the section title bar gradient. - * - * @deprecated use IFormColors.TB_GBG - */ - public static final String TB_GBG = IFormColors.TB_GBG; - - /** - * Key for the section title bar border. - * - * @deprecated use IFormColors.TB_BORDER. - */ - public static final String TB_BORDER = IFormColors.TB_BORDER; - - /** - * Key for the section toggle color. Since 3.1, this color is used for all - * section styles. - * - * @deprecated use IFormColors.TB_TOGGLE. - */ - public static final String TB_TOGGLE = IFormColors.TB_TOGGLE; - - /** - * Key for the section toggle hover color. - * - * @deprecated use IFormColors.TB_TOGGLE_HOVER. - */ - public static final String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER; - - protected Map colorRegistry = new HashMap(10); - - private LocalResourceManager resources; - - protected Color background; - - protected Color foreground; - - private boolean shared; - - protected Display display; - - protected Color border; - - /** - * Creates form colors using the provided display. - * - * @param display - * the display to use - */ - public FormColors(Display display) { - this.display = display; - initialize(); - } - - /** - * Returns the display used to create colors. - * - * @return the display - */ - public Display getDisplay() { - return display; - } - - /** - * Initializes the colors. Subclasses can override this method to change the - * way colors are created. Alternatively, only the color table can be - * modified by overriding initializeColorTable(). - * - * @see #initializeColorTable - */ - protected void initialize() { - background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); - foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); - initializeColorTable(); - updateBorderColor(); - } - - /** - * Allocates colors for the following keys: BORDER, SEPARATOR and - * TITLE. Subclasses can override to allocate these colors differently. - */ - protected void initializeColorTable() { - createTitleColor(); - createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB()); - RGB black = getSystemColor(SWT.COLOR_BLACK); - RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT); - createColor(IFormColors.BORDER, blend(borderRGB, black, 80)); - } - - /** - * Allocates colors for the section tool bar (all the keys that start with - * TB). Since these colors are only needed when TITLE_BAR style is used with - * the Section widget, they are not needed all the time and are allocated on - * demand. Consequently, this method will do nothing if the colors have been - * already initialized. Call this method prior to using colors with the TB - * keys to ensure they are available. - */ - public void initializeSectionToolBarColors() { - if (colorRegistry.containsKey(IFormColors.TB_BG)) - return; - createTitleBarGradientColors(); - createTitleBarOutlineColors(); - createTwistieColors(); - } - - /** - * Allocates additional colors for the form header, namely background - * gradients, bottom separator keylines and DND highlights. Since these - * colors are only needed for clients that want to use these particular - * style of header rendering, they are not needed all the time and are - * allocated on demand. Consequently, this method will do nothing if the - * colors have been already initialized. Call this method prior to using - * color keys with the H_ prefix to ensure they are available. - */ - protected void initializeFormHeaderColors() { - if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2)) - return; - createFormHeaderColors(); - } - - /** - * Returns the RGB value of the system color represented by the code - * argument, as defined in SWT class. - * - * @param code - * the system color constant as defined in SWT - * class. - * @return the RGB value of the system color - */ - public RGB getSystemColor(int code) { - return getDisplay().getSystemColor(code).getRGB(); - } - - /** - * Creates the color for the specified key using the provided RGB object. - * The color object will be returned and also put into the registry. When - * the class is disposed, the color will be disposed with it. - * - * @param key - * the unique color key - * @param rgb - * the RGB object - * @return the allocated color object - */ - public Color createColor(String key, RGB rgb) { - // RAP [rh] changes due to missing Color constructor -// Color c = getResourceManager().createColor(rgb); -// Color prevC = (Color) colorRegistry.get(key); -// if (prevC != null && !prevC.isDisposed()) -// getResourceManager().destroyColor(prevC.getRGB()); -// Color c = Graphics.getColor(rgb); - Color c = new Color(display, rgb); - colorRegistry.put(key, c); - return c; - } - - /** - * Creates a color that can be used for areas of the form that is inactive. - * These areas can contain images, links, controls and other content but are - * considered auxilliary to the main content area. - * - *

- * The color should not be disposed because it is managed by this class. - * - * @return the inactive form color - */ - public Color getInactiveBackground() { - String key = "__ncbg__"; //$NON-NLS-1$ - Color color = getColor(key); - if (color == null) { - RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION); - // a blend of 95% white and 5% list selection system color - RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5); - color = createColor(key, ncbg); - } - return color; - } - - /** - * Creates the color for the specified key using the provided RGB values. - * The color object will be returned and also put into the registry. If - * there is already another color object under the same key in the registry, - * the existing object will be disposed. When the class is disposed, the - * color will be disposed with it. - * - * @param key - * the unique color key - * @param r - * red value - * @param g - * green value - * @param b - * blue value - * @return the allocated color object - */ - public Color createColor(String key, int r, int g, int b) { - return createColor(key, new RGB(r,g,b)); - } - - /** - * Computes the border color relative to the background. Allocated border - * color is designed to work well with white. Otherwise, stanard widget - * background color will be used. - */ - protected void updateBorderColor() { - if (isWhiteBackground()) - border = getColor(IFormColors.BORDER); - else { - border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); - Color bg = getImpliedBackground(); - if (border.getRed() == bg.getRed() - && border.getGreen() == bg.getGreen() - && border.getBlue() == bg.getBlue()) - border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW); - } - } - - /** - * Sets the background color. All the toolkits that use this class will - * share the same background. - * - * @param bg - * background color - */ - public void setBackground(Color bg) { - this.background = bg; - updateBorderColor(); - updateFormHeaderColors(); - } - - /** - * Sets the foreground color. All the toolkits that use this class will - * share the same foreground. - * - * @param fg - * foreground color - */ - public void setForeground(Color fg) { - this.foreground = fg; - } - - /** - * Returns the current background color. - * - * @return the background color - */ - public Color getBackground() { - return background; - } - - /** - * Returns the current foreground color. - * - * @return the foreground color - */ - public Color getForeground() { - return foreground; - } - - /** - * Returns the computed border color. Border color depends on the background - * and is recomputed whenever the background changes. - * - * @return the current border color - */ - public Color getBorderColor() { - return border; - } - - /** - * Tests if the background is white. White background has RGB value - * 255,255,255. - * - * @return true if background is white, false - * otherwise. - */ - public boolean isWhiteBackground() { - Color bg = getImpliedBackground(); - return bg.getRed() == 255 && bg.getGreen() == 255 - && bg.getBlue() == 255; - } - - /** - * Returns the color object for the provided key or null if - * not in the registry. - * - * @param key - * the color key - * @return color object if found, or null if not. - */ - public Color getColor(String key) { - if (key.startsWith(IFormColors.TB_PREFIX)) - initializeSectionToolBarColors(); - else if (key.startsWith(IFormColors.H_PREFIX)) - initializeFormHeaderColors(); - return (Color) colorRegistry.get(key); - } - - /** - * Disposes all the colors in the registry. - */ - public void dispose() { - if (resources != null) - resources.dispose(); - resources = null; - colorRegistry = null; - } - - /** - * Marks the colors shared. This prevents toolkits that share this object - * from disposing it. - */ - public void markShared() { - this.shared = true; - } - - /** - * Tests if the colors are shared. - * - * @return true if shared, false otherwise. - */ - public boolean isShared() { - return shared; - } - - /** - * Blends c1 and c2 based in the provided ratio. - * - * @param c1 - * first color - * @param c2 - * second color - * @param ratio - * percentage of the first color in the blend (0-100) - * @return the RGB value of the blended color - */ - public static RGB blend(RGB c1, RGB c2, int ratio) { - int r = blend(c1.red, c2.red, ratio); - int g = blend(c1.green, c2.green, ratio); - int b = blend(c1.blue, c2.blue, ratio); - return new RGB(r, g, b); - } - - /** - * Tests the source RGB for range. - * - * @param rgb - * the tested RGB - * @param from - * range start (excluding the value itself) - * @param to - * range end (excluding the value itself) - * @return true if at least one of the primary colors in the - * source RGB are within the provided range, false - * otherwise. - */ - public static boolean testAnyPrimaryColor(RGB rgb, int from, int to) { - if (testPrimaryColor(rgb.red, from, to)) - return true; - if (testPrimaryColor(rgb.green, from, to)) - return true; - if (testPrimaryColor(rgb.blue, from, to)) - return true; - return false; - } - - /** - * Tests the source RGB for range. - * - * @param rgb - * the tested RGB - * @param from - * range start (excluding the value itself) - * @param to - * tange end (excluding the value itself) - * @return true if at least two of the primary colors in the - * source RGB are within the provided range, false - * otherwise. - */ - public static boolean testTwoPrimaryColors(RGB rgb, int from, int to) { - int total = 0; - if (testPrimaryColor(rgb.red, from, to)) - total++; - if (testPrimaryColor(rgb.green, from, to)) - total++; - if (testPrimaryColor(rgb.blue, from, to)) - total++; - return total >= 2; - } - - /** - * Blends two primary color components based on the provided ratio. - * - * @param v1 - * first component - * @param v2 - * second component - * @param ratio - * percentage of the first component in the blend - * @return - */ - private static int blend(int v1, int v2, int ratio) { - int b = (ratio * v1 + (100 - ratio) * v2) / 100; - return Math.min(255, b); - } - - private Color getImpliedBackground() { - if (getBackground() != null) - return getBackground(); - return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); - } - - private static boolean testPrimaryColor(int value, int from, int to) { - return value > from && value < to; - } - - private void createTitleColor() { - /* - * RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light - * if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80); - * else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK, - * 50); createColor(TITLE, rgb); - */ - RGB bg = getImpliedBackground().getRGB(); - RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION); - RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND); - RGB rgb = listSelection; - - // Group 1 - // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or - // between 0 and 120, then use 100% LIST_SELECTION as it is (no - // additions) - // Examples: XP Default, Win Classic Standard, Win High Con White, Win - // Classic Marine - if (testTwoPrimaryColors(listSelection, -1, 121)) - rgb = listSelection; - // Group 2 - // When LIST_BACKGROUND = white (255, 255, 255) or not black, text - // colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over - // LIST_BACKGROUND - // Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or - // between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION - // foreground colour - // Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX - // Aqua, OSX Graphite, Linux GTK - else if (testTwoPrimaryColors(listSelection, 120, 256) - || (bg.red == 0 && bg.green == 0 && bg.blue == 0)) - rgb = blend(listSelection, listForeground, 50); - // Group 3 - // When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION - // @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND - // Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to - // LIST_SELECTION foreground colour - // Examples: Win High Con Black, Win High Con #1, Win High Con #2 - // (covered in the second part of the OR clause above) - createColor(IFormColors.TITLE, rgb); - } - - private void createTwistieColors() { - RGB rgb = getColor(IFormColors.TITLE).getRGB(); - RGB white = getSystemColor(SWT.COLOR_WHITE); - createColor(TB_TOGGLE, rgb); - rgb = blend(rgb, white, 60); - createColor(TB_TOGGLE_HOVER, rgb); - } - - private void createTitleBarGradientColors() { - RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - RGB bg = getImpliedBackground().getRGB(); - - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(tbBg, 179, 256)) - tbBg = blend(tbBg, bg, 30); - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(tbBg, 120, 180)) - tbBg = blend(tbBg, bg, 20); - - // Group 3 - // Rule: Everything else - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else { - tbBg = blend(tbBg, bg, 10); - } - - createColor(IFormColors.TB_BG, tbBg); - - // for backward compatibility - createColor(TB_GBG, tbBg); - } - - private void createTitleBarOutlineColors() { - // title bar outline - border color - RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - RGB bg = getImpliedBackground().getRGB(); - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(tbBorder, 179, 256)) - tbBorder = blend(tbBorder, bg, 70); - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - - // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(tbBorder, 120, 180)) - tbBorder = blend(tbBorder, bg, 50); - - // Group 3 - // Rule: Everything else - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - - // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - else { - tbBorder = blend(tbBorder, bg, 30); - } - createColor(FormColors.TB_BORDER, tbBorder); - } - - private void updateFormHeaderColors() { - if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) { - disposeIfFound(IFormColors.H_GRADIENT_END); - disposeIfFound(IFormColors.H_GRADIENT_START); - disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1); - disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2); - disposeIfFound(IFormColors.H_HOVER_LIGHT); - disposeIfFound(IFormColors.H_HOVER_FULL); - initializeFormHeaderColors(); - } - } - - private void disposeIfFound(String key) { - Color color = getColor(key); - if (color != null) { - colorRegistry.remove(key); - // RAP [rh] changes due to missing Color#dispose() -// color.dispose(); - } - } - - private void createFormHeaderColors() { - createFormHeaderGradientColors(); - createFormHeaderKeylineColors(); - createFormHeaderDNDColors(); - } - - private void createFormHeaderGradientColors() { - RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - Color bgColor = getImpliedBackground(); - RGB bg = bgColor.getRGB(); - RGB bottom, top; - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(titleBg, 179, 256)) { - bottom = blend(titleBg, bg, 30); - top = bg; - } - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - // Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(titleBg, 120, 180)) { - bottom = blend(titleBg, bg, 20); - top = bg; - } - - // Group 3 - // Rule: If at least 2 of the RGB values are equal to or between 0 and - // 120, then apply specified opacity for Group 3 - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - // Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND - // Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND - else { - bottom = blend(titleBg, bg, 10); - top = bg; - } - createColor(IFormColors.H_GRADIENT_END, top); - createColor(IFormColors.H_GRADIENT_START, bottom); - } - - private void createFormHeaderKeylineColors() { - RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND); - Color bgColor = getImpliedBackground(); - RGB bg = bgColor.getRGB(); - RGB keyline2; - // H_BOTTOM_KEYLINE1 - createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255)); - - // H_BOTTOM_KEYLINE2 - // Group 1 - // Rule: If at least 2 of the RGB values are equal to or between 180 and - // 255, then apply specified opacity for Group 1 - // Examples: Vista, XP Silver, Wn High Con #2 - // Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND - if (testTwoPrimaryColors(titleBg, 179, 256)) - keyline2 = blend(titleBg, bg, 70); - - // Group 2 - // Rule: If at least 2 of the RGB values are equal to or between 121 and - // 179, then apply specified opacity for Group 2 - // Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black - // Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND - else if (testTwoPrimaryColors(titleBg, 120, 180)) - keyline2 = blend(titleBg, bg, 50); - - // Group 3 - // Rule: If at least 2 of the RGB values are equal to or between 0 and - // 120, then apply specified opacity for Group 3 - // Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX - // Aqua, Wn High Con White, Wn High Con #1 - - // Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND - else - keyline2 = blend(titleBg, bg, 30); - // H_BOTTOM_KEYLINE2 - createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2); - } - - private void createFormHeaderDNDColors() { - RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT); - Color bgColor = getImpliedBackground(); - RGB bg = bgColor.getRGB(); - RGB light, full; - // ALL Themes - // - // Light Highlight - // When *near* the 'hot' area - // Rule: If near the title in the 'hot' area, show background highlight - // TITLE_BACKGROUND_GRADIENT @ 40% - light = blend(titleBg, bg, 40); - // Full Highlight - // When *on* the title area (regions 1 and 2) - // Rule: If near the title in the 'hot' area, show background highlight - // TITLE_BACKGROUND_GRADIENT @ 60% - full = blend(titleBg, bg, 60); - // H_DND_LIGHT - // H_DND_FULL - createColor(IFormColors.H_HOVER_LIGHT, light); - createColor(IFormColors.H_HOVER_FULL, full); - } - - private LocalResourceManager getResourceManager() { - if (resources == null) - resources = new LocalResourceManager(JFaceResources.getResources()); - return resources; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java deleted file mode 100644 index 9e931ba50..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormFonts.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import java.util.HashMap; - -import org.eclipse.jface.resource.DeviceResourceException; -import org.eclipse.jface.resource.FontDescriptor; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Device; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.FontData; -//import org.eclipse.swt.internal.graphics.Graphics; -import org.eclipse.swt.widgets.Display; - -public class FormFonts { - private static FormFonts instance; - - public static FormFonts getInstance() { - if (instance == null) - instance = new FormFonts(); - return instance; - } - - private LocalResourceManager resources; - private HashMap descriptors; - - private FormFonts() { - } - - private class BoldFontDescriptor extends FontDescriptor { - private FontData[] fFontData; - - BoldFontDescriptor(Font font) { - // RAP [if] Changes due to different way of creating fonts - // fFontData = font.getFontData(); - // for (int i = 0; i < fFontData.length; i++) { - // fFontData[i].setStyle(fFontData[i].getStyle() | SWT.BOLD); - // } - FontData fontData = font.getFontData()[0]; - // Font boldFont = Graphics.getFont( fontData.getName(), - // fontData.getHeight(), - // fontData.getStyle() | SWT.BOLD ); - Font boldFont = new Font(Display.getCurrent(), fontData.getName(), fontData.getHeight(), - fontData.getStyle() | SWT.BOLD); - fFontData = boldFont.getFontData(); - } - - public boolean equals(Object obj) { - if (obj instanceof BoldFontDescriptor) { - BoldFontDescriptor desc = (BoldFontDescriptor) obj; - if (desc.fFontData.length != fFontData.length) - return false; - for (int i = 0; i < fFontData.length; i++) - if (!fFontData[i].equals(desc.fFontData[i])) - return false; - return true; - } - return false; - } - - public int hashCode() { - int hash = 0; - for (int i = 0; i < fFontData.length; i++) - hash = hash * 7 + fFontData[i].hashCode(); - return hash; - } - - public Font createFont(Device device) throws DeviceResourceException { - // RAP [if] Changes due to different way of creating fonts - return new Font(device, fFontData[0]); - // return Graphics.getFont( fFontData[ 0 ] ); - } - - public void destroyFont(Font previouslyCreatedFont) { - // RAP [if] unnecessary - // previouslyCreatedFont.dispose(); - } - } - - public Font getBoldFont(Display display, Font font) { - checkHashMaps(); - BoldFontDescriptor desc = new BoldFontDescriptor(font); - Font result = getResourceManager().createFont(desc); - descriptors.put(result, desc); - return result; - } - - public boolean markFinished(Font boldFont) { - checkHashMaps(); - BoldFontDescriptor desc = (BoldFontDescriptor) descriptors.get(boldFont); - if (desc != null) { - getResourceManager().destroyFont(desc); - if (getResourceManager().find(desc) == null) { - descriptors.remove(boldFont); - validateHashMaps(); - } - return true; - - } - // if the image was not found, dispose of it for the caller - // RAP [if] unnecessary - // boldFont.dispose(); - return false; - } - - private LocalResourceManager getResourceManager() { - if (resources == null) - resources = new LocalResourceManager(JFaceResources.getResources()); - return resources; - } - - private void checkHashMaps() { - if (descriptors == null) - descriptors = new HashMap(); - } - - private void validateHashMaps() { - if (descriptors.size() == 0) - descriptors = null; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java deleted file mode 100644 index 99271046a..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormToolkit.java +++ /dev/null @@ -1,913 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.window.Window; -import org.eclipse.swt.SWT; -//import org.eclipse.swt.custom.CCombo; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.KeyAdapter; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -// RAP [rh] Paint events missing -//import org.eclipse.swt.events.PaintEvent; -//import org.eclipse.swt.events.PaintListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -//RAP [rh] GC missing -//import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Point; -//import org.eclipse.swt.graphics.RGB; -//import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -//import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Label; -//import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.Widget; -//import org.eclipse.ui.forms.FormColors; -//import org.eclipse.ui.forms.HyperlinkGroup; -//import org.eclipse.ui.forms.IFormColors; -//import org.eclipse.ui.internal.forms.widgets.FormFonts; -//import org.eclipse.ui.internal.forms.widgets.FormUtil; - -/** - * The toolkit is responsible for creating SWT controls adapted to work in - * Eclipse forms. In addition to changing their presentation properties (fonts, - * colors etc.), various listeners are attached to make them behave correctly in - * the form context. - *

- * In addition to being the control factory, the toolkit is also responsible for - * painting flat borders for select controls, managing hyperlink groups and - * control colors. - *

- * The toolkit creates some of the most common controls used to populate Eclipse - * forms. Controls that must be created using their constructors, - * adapt() method is available to change its properties in the - * same way as with the supported toolkit controls. - *

- * Typically, one toolkit object is created per workbench part (for example, an - * editor or a form wizard). The toolkit is disposed when the part is disposed. - * To conserve resources, it is possible to create one color object for the - * entire plug-in and share it between several toolkits. The plug-in is - * responsible for disposing the colors (disposing the toolkit that uses shared - * color object will not dispose the colors). - *

- * FormToolkit is normally instantiated, but can also be subclassed if some of - * the methods needs to be modified. In those cases, super must - * be called to preserve normal behaviour. - * - * @since 1.0 - */ -public class FormToolkit { - public static final String KEY_DRAW_BORDER = "FormWidgetFactory.drawBorder"; //$NON-NLS-1$ - - public static final String TREE_BORDER = "treeBorder"; //$NON-NLS-1$ - - public static final String TEXT_BORDER = "textBorder"; //$NON-NLS-1$ - - private int borderStyle = SWT.NULL; - - private FormColors colors; - - private int orientation = Window.getDefaultOrientation(); - - // private KeyListener deleteListener; - // RAP [rh] Paint events missing -// private BorderPainter borderPainter; - - private BoldFontHolder boldFontHolder; - -// private HyperlinkGroup hyperlinkGroup; - - private boolean isDisposed = false; - - /* default */ - VisibilityHandler visibilityHandler; - - /* default */ - KeyboardHandler keyboardHandler; - - // RAP [rh] Paint events missing -// private class BorderPainter implements PaintListener { -// public void paintControl(PaintEvent event) { -// Composite composite = (Composite) event.widget; -// Control[] children = composite.getChildren(); -// for (int i = 0; i < children.length; i++) { -// Control c = children[i]; -// boolean inactiveBorder = false; -// boolean textBorder = false; -// if (!c.isVisible()) -// continue; -// /* -// * if (c.getEnabled() == false && !(c instanceof CCombo)) -// * continue; -// */ -// if (c instanceof Hyperlink) -// continue; -// Object flag = c.getData(KEY_DRAW_BORDER); -// if (flag != null) { -// if (flag.equals(Boolean.FALSE)) -// continue; -// if (flag.equals(TREE_BORDER)) -// inactiveBorder = true; -// else if (flag.equals(TEXT_BORDER)) -// textBorder = true; -// } -// if (getBorderStyle() == SWT.BORDER) { -// if (!inactiveBorder && !textBorder) { -// continue; -// } -// if (c instanceof Text || c instanceof Table -// || c instanceof Tree) -// continue; -// } -// if (!inactiveBorder -// && (c instanceof Text || c instanceof CCombo || textBorder)) { -// Rectangle b = c.getBounds(); -// GC gc = event.gc; -// gc.setForeground(c.getBackground()); -// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, -// b.height + 1); -// // gc.setForeground(getBorderStyle() == SWT.BORDER ? colors -// // .getBorderColor() : colors.getForeground()); -// gc.setForeground(colors.getBorderColor()); -// if (c instanceof CCombo) -// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, -// b.height + 1); -// else -// gc.drawRectangle(b.x - 1, b.y - 2, b.width + 1, -// b.height + 3); -// } else if (inactiveBorder || c instanceof Table -// || c instanceof Tree) { -// Rectangle b = c.getBounds(); -// GC gc = event.gc; -// gc.setForeground(colors.getBorderColor()); -// gc.drawRectangle(b.x - 1, b.y - 1, b.width + 1, -// b.height + 1); -// } -// } -// } -// } - - private static class VisibilityHandler extends FocusAdapter { - public void focusGained(FocusEvent e) { - Widget w = e.widget; - if (w instanceof Control) { - FormUtil.ensureVisible((Control) w); - } - } - } - - private static class KeyboardHandler extends KeyAdapter { - public void keyPressed(KeyEvent e) { - Widget w = e.widget; - if (w instanceof Control) { - if (e.doit) - FormUtil.processKey(e.keyCode, (Control) w); - } - } - } - - private class BoldFontHolder { - private Font normalFont; - - private Font boldFont; - - public BoldFontHolder() { - } - - public Font getBoldFont(Font font) { - createBoldFont(font); - return boldFont; - } - - private void createBoldFont(Font font) { - if (normalFont == null || !normalFont.equals(font)) { - normalFont = font; - dispose(); - } - if (boldFont == null) { - boldFont = FormFonts.getInstance().getBoldFont(colors.getDisplay(), - normalFont); - } - } - - public void dispose() { - if (boldFont != null) { - FormFonts.getInstance().markFinished(boldFont); - boldFont = null; - } - } - } - - /** - * Creates a toolkit that is self-sufficient (will manage its own colors). - *

- * Clients that call this method must call {@link #dispose()} when they - * are finished using the toolkit. - * - */ - public FormToolkit(Display display) { - this(new FormColors(display)); - } - - /** - * Creates a toolkit that will use the provided (shared) colors. The toolkit - * will dispose the colors if and only if they are not marked as - * shared via the markShared() method. - *

- * Clients that call this method must call {@link #dispose()} when they - * are finished using the toolkit. - * - * @param colors - * the shared colors - */ - public FormToolkit(FormColors colors) { - this.colors = colors; - initialize(); - } - - /** - * Creates a button as a part of the form. - * - * @param parent - * the button parent - * @param text - * an optional text for the button (can be null) - * @param style - * the button style (for example, SWT.PUSH) - * @return the button widget - */ - public Button createButton(Composite parent, String text, int style) { - Button button = new Button(parent, style | SWT.FLAT | orientation); - if (text != null) - button.setText(text); - adapt(button, true, true); - return button; - } - - /** - * Creates the composite as a part of the form. - * - * @param parent - * the composite parent - * @return the composite widget - */ - public Composite createComposite(Composite parent) { - return createComposite(parent, SWT.NULL); - } - - /** - * Creates the composite as part of the form using the provided style. - * - * @param parent - * the composite parent - * @param style - * the composite style - * @return the composite widget - */ - public Composite createComposite(Composite parent, int style) { -// Composite composite = new LayoutComposite(parent, style | orientation); - Composite composite = new Composite(parent, style | orientation); - adapt(composite); - return composite; - } - - /** - * Creats the composite that can server as a separator between various parts - * of a form. Separator height should be controlled by setting the height - * hint on the layout data for the composite. - * - * @param parent - * the separator parent - * @return the separator widget - */ -// RAP [rh] createCompositeSeparator: currently no useful implementation possible, delete? - public Composite createCompositeSeparator(Composite parent) { - final Composite composite = new Composite(parent, orientation); -// RAP [rh] GC and paint events missing -// composite.addListener(SWT.Paint, new Listener() { -// public void handleEvent(Event e) { -// if (composite.isDisposed()) -// return; -// Rectangle bounds = composite.getBounds(); -// GC gc = e.gc; -// gc.setForeground(colors.getColor(IFormColors.SEPARATOR)); -// if (colors.getBackground() != null) -// gc.setBackground(colors.getBackground()); -// gc.fillGradientRectangle(0, 0, bounds.width, bounds.height, -// false); -// } -// }); -// if (parent instanceof Section) -// ((Section) parent).setSeparatorControl(composite); - return composite; - } - - /** - * Creates a label as a part of the form. - * - * @param parent - * the label parent - * @param text - * the label text - * @return the label widget - */ - public Label createLabel(Composite parent, String text) { - return createLabel(parent, text, SWT.NONE); - } - - /** - * Creates a label as a part of the form. - * - * @param parent - * the label parent - * @param text - * the label text - * @param style - * the label style - * @return the label widget - */ - public Label createLabel(Composite parent, String text, int style) { - Label label = new Label(parent, style | orientation); - if (text != null) - label.setText(text); - adapt(label, false, false); - return label; - } - - /** - * Creates a hyperlink as a part of the form. The hyperlink will be added to - * the hyperlink group that belongs to this toolkit. - * - * @param parent - * the hyperlink parent - * @param text - * the text of the hyperlink - * @param style - * the hyperlink style - * @return the hyperlink widget - */ -// public Hyperlink createHyperlink(Composite parent, String text, int style) { -// Hyperlink hyperlink = new Hyperlink(parent, style | orientation); -// if (text != null) -// hyperlink.setText(text); -// hyperlink.addFocusListener(visibilityHandler); -// hyperlink.addKeyListener(keyboardHandler); -// hyperlinkGroup.add(hyperlink); -// return hyperlink; -// } - - /** - * Creates an image hyperlink as a part of the form. The hyperlink will be - * added to the hyperlink group that belongs to this toolkit. - * - * @param parent - * the hyperlink parent - * @param style - * the hyperlink style - * @return the image hyperlink widget - */ -// public ImageHyperlink createImageHyperlink(Composite parent, int style) { -// ImageHyperlink hyperlink = new ImageHyperlink(parent, style -// | orientation); -// hyperlink.addFocusListener(visibilityHandler); -// hyperlink.addKeyListener(keyboardHandler); -// hyperlinkGroup.add(hyperlink); -// return hyperlink; -// } - - /** - * Creates a rich text as a part of the form. - * - * @param parent - * the rich text parent - * @param trackFocus - * if true, the toolkit will monitor focus - * transfers to ensure that the hyperlink in focus is visible in - * the form. - * @return the rich text widget - * @since 1.2 - */ -// public FormText createFormText(Composite parent, boolean trackFocus) { -// FormText engine = new FormText(parent, SWT.WRAP | orientation); -// engine.marginWidth = 1; -// engine.marginHeight = 0; -// engine.setHyperlinkSettings(getHyperlinkGroup()); -// adapt(engine, trackFocus, true); -// engine.setMenu(parent.getMenu()); -// return engine; -// } - - /** - * Adapts a control to be used in a form that is associated with this - * toolkit. This involves adjusting colors and optionally adding handlers to - * ensure focus tracking and keyboard management. - * - * @param control - * a control to adapt - * @param trackFocus - * if true, form will be scrolled horizontally - * and/or vertically if needed to ensure that the control is - * visible when it gains focus. Set it to false if - * the control is not capable of gaining focus. - * @param trackKeyboard - * if true, the control that is capable of - * gaining focus will be tracked for certain keys that are - * important to the underlying form (for example, PageUp, - * PageDown, ScrollUp, ScrollDown etc.). Set it to - * false if the control is not capable of gaining - * focus or these particular key event are already used by the - * control. - */ - public void adapt(Control control, boolean trackFocus, boolean trackKeyboard) { - control.setBackground(colors.getBackground()); - control.setForeground(colors.getForeground()); -// if (control instanceof ExpandableComposite) { -// ExpandableComposite ec = (ExpandableComposite) control; -// if (ec.toggle != null) { -// if (trackFocus) -// ec.toggle.addFocusListener(visibilityHandler); -// if (trackKeyboard) -// ec.toggle.addKeyListener(keyboardHandler); -// } -// if (ec.textLabel != null) { -// if (trackFocus) -// ec.textLabel.addFocusListener(visibilityHandler); -// if (trackKeyboard) -// ec.textLabel.addKeyListener(keyboardHandler); -// } -// return; -// } - if (trackFocus) - control.addFocusListener(visibilityHandler); - if (trackKeyboard) - control.addKeyListener(keyboardHandler); - } - - /** - * Adapts a composite to be used in a form associated with this toolkit. - * - * @param composite - * the composite to adapt - */ - public void adapt(Composite composite) { - composite.setBackground(colors.getBackground()); - composite.addMouseListener(new MouseAdapter() { - public void mouseDown(MouseEvent e) { - ((Control) e.widget).setFocus(); - } - }); - if (composite.getParent() != null) - composite.setMenu(composite.getParent().getMenu()); - } - - /** - * A helper method that ensures the provided control is visible when - * ScrolledComposite is somewhere in the parent chain. If scroll bars are - * visible and the control is clipped, the client of the scrolled composite - * will be scrolled to reveal the control. - * - * @param c - * the control to reveal - */ - public static void ensureVisible(Control c) { - FormUtil.ensureVisible(c); - } - - /** - * Creates a section as a part of the form. - * - * @param parent - * the section parent - * @param sectionStyle - * the section style - * @return the section widget - */ -// public Section createSection(Composite parent, int sectionStyle) { -// Section section = new Section(parent, orientation, sectionStyle); -// section.setMenu(parent.getMenu()); -// adapt(section, true, true); -// if (section.toggle != null) { -// section.toggle.setHoverDecorationColor(colors -// .getColor(IFormColors.TB_TOGGLE_HOVER)); -// section.toggle.setDecorationColor(colors -// .getColor(IFormColors.TB_TOGGLE)); -// } -// section.setFont(boldFontHolder.getBoldFont(parent.getFont())); -// if ((sectionStyle & Section.TITLE_BAR) != 0 -// || (sectionStyle & Section.SHORT_TITLE_BAR) != 0) { -// colors.initializeSectionToolBarColors(); -// section.setTitleBarBackground(colors.getColor(IFormColors.TB_BG)); -// section.setTitleBarBorderColor(colors -// .getColor(IFormColors.TB_BORDER)); -// } -// // call setTitleBarForeground regardless as it also sets the label color -// section.setTitleBarForeground(colors -// .getColor(IFormColors.TB_TOGGLE)); -// return section; -// } - - /** - * Creates an expandable composite as a part of the form. - * - * @param parent - * the expandable composite parent - * @param expansionStyle - * the expandable composite style - * @return the expandable composite widget - */ -// public ExpandableComposite createExpandableComposite(Composite parent, -// int expansionStyle) { -// ExpandableComposite ec = new ExpandableComposite(parent, orientation, -// expansionStyle); -// ec.setMenu(parent.getMenu()); -// adapt(ec, true, true); -// ec.setFont(boldFontHolder.getBoldFont(ec.getFont())); -// return ec; -// } - - /** - * Creates a separator label as a part of the form. - * - * @param parent - * the separator parent - * @param style - * the separator style - * @return the separator label - */ - public Label createSeparator(Composite parent, int style) { - Label label = new Label(parent, SWT.SEPARATOR | style | orientation); - label.setBackground(colors.getBackground()); - label.setForeground(colors.getBorderColor()); - return label; - } - - /** - * Creates a table as a part of the form. - * - * @param parent - * the table parent - * @param style - * the table style - * @return the table widget - */ - public Table createTable(Composite parent, int style) { - Table table = new Table(parent, style | borderStyle | orientation); - adapt(table, false, false); - // hookDeleteListener(table); - return table; - } - - /** - * Creates a text as a part of the form. - * - * @param parent - * the text parent - * @param value - * the text initial value - * @return the text widget - */ - public Text createText(Composite parent, String value) { - return createText(parent, value, SWT.SINGLE); - } - - /** - * Creates a text as a part of the form. - * - * @param parent - * the text parent - * @param value - * the text initial value - * @param style - * the text style - * @return the text widget - */ - public Text createText(Composite parent, String value, int style) { - Text text = new Text(parent, borderStyle | style | orientation); - if (value != null) - text.setText(value); - text.setForeground(colors.getForeground()); - text.setBackground(colors.getBackground()); - text.addFocusListener(visibilityHandler); - return text; - } - - /** - * Creates a tree widget as a part of the form. - * - * @param parent - * the tree parent - * @param style - * the tree style - * @return the tree widget - */ - public Tree createTree(Composite parent, int style) { - Tree tree = new Tree(parent, borderStyle | style | orientation); - adapt(tree, false, false); - // hookDeleteListener(tree); - return tree; - } - - /** - * Creates a scrolled form widget in the provided parent. If you do not - * require scrolling because there is already a scrolled composite up the - * parent chain, use 'createForm' instead. - * - * @param parent - * the scrolled form parent - * @return the form that can scroll itself - * @see #createForm - */ - public ScrolledComposite createScrolledForm(Composite parent) { - ScrolledComposite form = new ScrolledComposite(parent, SWT.V_SCROLL - | SWT.H_SCROLL | orientation); - form.setExpandHorizontal(true); - form.setExpandVertical(true); - form.setBackground(colors.getBackground()); - form.setForeground(colors.getColor(IFormColors.TITLE)); - form.setFont(JFaceResources.getHeaderFont()); - return form; - } - - /** - * Creates a form widget in the provided parent. Note that this widget does - * not scroll its content, so make sure there is a scrolled composite up the - * parent chain. If you require scrolling, use 'createScrolledForm' instead. - * - * @param parent - * the form parent - * @return the form that does not scroll - * @see #createScrolledForm - */ -// public Form createForm(Composite parent) { -// Form formContent = new Form(parent, orientation); -// formContent.setBackground(colors.getBackground()); -// formContent.setForeground(colors.getColor(IFormColors.TITLE)); -// formContent.setFont(JFaceResources.getHeaderFont()); -// return formContent; -// } - - /** - * Takes advantage of the gradients and other capabilities to decorate the - * form heading using colors computed based on the current skin and - * operating system. - * - * @param form - * the form to decorate - */ - -// public void decorateFormHeading(Form form) { -// Color top = colors.getColor(IFormColors.H_GRADIENT_END); -// Color bot = colors.getColor(IFormColors.H_GRADIENT_START); -// form.setTextBackground(new Color[] { top, bot }, new int[] { 100 }, -// true); -// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE1, colors -// .getColor(IFormColors.H_BOTTOM_KEYLINE1)); -// form.setHeadColor(IFormColors.H_BOTTOM_KEYLINE2, colors -// .getColor(IFormColors.H_BOTTOM_KEYLINE2)); -// form.setHeadColor(IFormColors.H_HOVER_LIGHT, colors -// .getColor(IFormColors.H_HOVER_LIGHT)); -// form.setHeadColor(IFormColors.H_HOVER_FULL, colors -// .getColor(IFormColors.H_HOVER_FULL)); -// form.setHeadColor(IFormColors.TB_TOGGLE, colors -// .getColor(IFormColors.TB_TOGGLE)); -// form.setHeadColor(IFormColors.TB_TOGGLE_HOVER, colors -// .getColor(IFormColors.TB_TOGGLE_HOVER)); -// form.setSeparatorVisible(true); -// } - - /** - * Creates a scrolled page book widget as a part of the form. - * - * @param parent - * the page book parent - * @param style - * the text style - * @return the scrolled page book widget - */ -// public ScrolledPageBook createPageBook(Composite parent, int style) { -// ScrolledPageBook book = new ScrolledPageBook(parent, style -// | orientation); -// adapt(book, true, true); -// book.setMenu(parent.getMenu()); -// return book; -// } - - /** - * Disposes the toolkit. - */ - public void dispose() { - if (isDisposed) { - return; - } - isDisposed = true; - if (colors.isShared() == false) { - colors.dispose(); - colors = null; - } - boldFontHolder.dispose(); - } - - /** - * Returns the hyperlink group that manages hyperlinks for this toolkit. - * - * @return the hyperlink group - */ -// public HyperlinkGroup getHyperlinkGroup() { -// return hyperlinkGroup; -// } - - /** - * Sets the background color for the entire toolkit. The method delegates - * the call to the FormColors object and also updates the hyperlink group so - * that hyperlinks and other objects are in sync. - * - * @param bg - * the new background color - */ - public void setBackground(Color bg) { -// hyperlinkGroup.setBackground(bg); - colors.setBackground(bg); - } - - /** - * Refreshes the hyperlink colors by loading from JFace settings. - */ -// public void refreshHyperlinkColors() { -// hyperlinkGroup.initializeDefaultForegrounds(colors.getDisplay()); -// } - -// RAP [rh] paintBordersFor not useful as no GC to actually paint borders -// /** -// * Paints flat borders for widgets created by this toolkit within the -// * provided parent. Borders will not be painted if the global border style -// * is SWT.BORDER (i.e. if native borders are used). Call this method during -// * creation of a form composite to get the borders of its children painted. -// * Care should be taken when selection layout margins. At least one pixel -// * pargin width and height must be chosen to allow the toolkit to paint the -// * border on the parent around the widgets. -// *

-// * Borders are painted for some controls that are selected by the toolkit by -// * default. If a control needs a border but is not on its list, it is -// * possible to force border in the following way: -// * -// *

-//	 *
-//	 *
-//	 *
-//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
-//	 *
-//	 *             or
-//	 *
-//	 *             widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
-//	 *
-//	 *
-//	 *
-//	 * 
-// * -// * @param parent -// * the parent that owns the children for which the border needs -// * to be painted. -// */ -// public void paintBordersFor(Composite parent) { -// // if (borderStyle == SWT.BORDER) -// // return; -// if (borderPainter == null) -// borderPainter = new BorderPainter(); -// parent.addPaintListener(borderPainter); -// } - - /** - * Returns the colors used by this toolkit. - * - * @return the color object - */ - public FormColors getColors() { - return colors; - } - - /** - * Returns the border style used for various widgets created by this - * toolkit. The intent of the toolkit is to create controls with styles that - * yield a 'flat' appearance. On systems where the native borders are - * already flat, we set the style to SWT.BORDER and don't paint the borders - * ourselves. Otherwise, the style is set to SWT.NULL, and borders are - * painted by the toolkit. - * - * @return the global border style - */ - public int getBorderStyle() { - return borderStyle; - } - - /** - * Returns the margin required around the children whose border is being - * painted by the toolkit using {@link #paintBordersFor(Composite)}. Since - * the border is painted around the controls on the parent, a number of - * pixels needs to be reserved for this border. For windowing systems where - * the native border is used, this margin is 0. - * - * @return the margin in the parent when children have their border painted - */ - public int getBorderMargin() { - return getBorderStyle() == SWT.BORDER ? 0 : 2; - } - - /** - * Sets the border style to be used when creating widgets. The toolkit - * chooses the correct style based on the platform but this value can be - * changed using this method. - * - * @param style - * SWT.BORDER or SWT.NULL - * @see #getBorderStyle - */ - public void setBorderStyle(int style) { - this.borderStyle = style; - } - - /** - * A utility method that ensures that the control is visible in the scrolled - * composite. The prerequisite for this method is that the control has a - * class that extends ScrolledComposite somewhere in the parent chain. If - * the control is partially or fully clipped, the composite is scrolled to - * set by setting the origin to the control origin. - * - * @param c - * the control to make visible - * @param verticalOnly - * if true, the scrolled composite will be - * scrolled only vertically if needed. Otherwise, the scrolled - * composite origin will be set to the control origin. - */ - public static void setControlVisible(Control c, boolean verticalOnly) { - ScrolledComposite scomp = FormUtil.getScrolledComposite(c); - if (scomp == null) - return; - Point location = FormUtil.getControlLocation(scomp, c); - scomp.setOrigin(location); - } - - private void initialize() { - initializeBorderStyle(); -// hyperlinkGroup = new HyperlinkGroup(colors.getDisplay()); -// hyperlinkGroup.setBackground(colors.getBackground()); - visibilityHandler = new VisibilityHandler(); - keyboardHandler = new KeyboardHandler(); - boldFontHolder = new BoldFontHolder(); - } - -// RAP [rh] revise detection of border style: can't ask OS here - private void initializeBorderStyle() { -// String osname = System.getProperty("os.name"); //$NON-NLS-1$ -// String osversion = System.getProperty("os.version"); //$NON-NLS-1$ -// if (osname.startsWith("Windows") && "5.1".compareTo(osversion) <= 0) { //$NON-NLS-1$ //$NON-NLS-2$ -// // Skinned widgets used on newer Windows (e.g. XP (5.1), Vista -// // (6.0)) -// // Check for Windows Classic. If not used, set the style to BORDER -// RGB rgb = colors.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); -// if (rgb.red != 212 || rgb.green != 208 || rgb.blue != 200) -// borderStyle = SWT.BORDER; -// } else if (osname.startsWith("Mac")) //$NON-NLS-1$ -// borderStyle = SWT.BORDER; - - borderStyle = SWT.BORDER; - } - - /** - * Returns the orientation that all the widgets created by this toolkit will - * inherit, if set. Can be SWT.NULL, - * SWT.LEFT_TO_RIGHT and SWT.RIGHT_TO_LEFT. - * - * @return orientation style for this toolkit, or SWT.NULL if - * not set. The default orientation is inherited from the Window - * default orientation. - * @see org.eclipse.jface.window.Window#getDefaultOrientation() - */ - - public int getOrientation() { - return orientation; - } - - /** - * Sets the orientation that all the widgets created by this toolkit will - * inherit. Can be SWT.NULL, SWT.LEFT_TO_RIGHT - * and SWT.RIGHT_TO_LEFT. - * - * @param orientation - * style for this toolkit. - */ - - public void setOrientation(int orientation) { - this.orientation = orientation; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java deleted file mode 100644 index 76e3f1104..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/FormUtil.java +++ /dev/null @@ -1,522 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.events.MouseEvent; -//import org.eclipse.swt.graphics.Device; -import org.eclipse.swt.graphics.FontMetrics; -import org.eclipse.swt.graphics.GC; -//import org.eclipse.swt.graphics.Image; -//import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Layout; -//import org.eclipse.swt.widgets.ScrollBar; -import org.eclipse.swt.widgets.Text; -//import org.eclipse.ui.forms.widgets.ColumnLayout; -//import org.eclipse.ui.forms.widgets.Form; -//import org.eclipse.ui.forms.widgets.FormText; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ILayoutExtension; -// -//import com.ibm.icu.text.BreakIterator; - -public class FormUtil { - public static final String PLUGIN_ID = "org.eclipse.ui.forms"; //$NON-NLS-1$ - - static final int H_SCROLL_INCREMENT = 5; - - static final int V_SCROLL_INCREMENT = 64; - - public static final String DEBUG = PLUGIN_ID + "/debug"; //$NON-NLS-1$ - - public static final String DEBUG_TEXT = DEBUG + "/text"; //$NON-NLS-1$ - public static final String DEBUG_TEXTSIZE = DEBUG + "/textsize"; //$NON-NLS-1$ - - public static final String DEBUG_FOCUS = DEBUG + "/focus"; //$NON-NLS-1$ - - public static final String FOCUS_SCROLLING = "focusScrolling"; //$NON-NLS-1$ - - public static final String IGNORE_BODY = "__ignore_body__"; //$NON-NLS-1$ - - public static Text createText(Composite parent, String label, - FormToolkit factory) { - return createText(parent, label, factory, 1); - } - - public static Text createText(Composite parent, String label, - FormToolkit factory, int span) { - factory.createLabel(parent, label); - Text text = factory.createText(parent, ""); //$NON-NLS-1$ - int hfill = span == 1 ? GridData.FILL_HORIZONTAL - : GridData.HORIZONTAL_ALIGN_FILL; - GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); - gd.horizontalSpan = span; - text.setLayoutData(gd); - return text; - } - - public static Text createText(Composite parent, String label, - FormToolkit factory, int span, int style) { - Label l = factory.createLabel(parent, label); - if ((style & SWT.MULTI) != 0) { - GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); - l.setLayoutData(gd); - } - Text text = factory.createText(parent, "", style); //$NON-NLS-1$ - int hfill = span == 1 ? GridData.FILL_HORIZONTAL - : GridData.HORIZONTAL_ALIGN_FILL; - GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); - gd.horizontalSpan = span; - text.setLayoutData(gd); - return text; - } - - public static Text createText(Composite parent, FormToolkit factory, - int span) { - Text text = factory.createText(parent, ""); //$NON-NLS-1$ - int hfill = span == 1 ? GridData.FILL_HORIZONTAL - : GridData.HORIZONTAL_ALIGN_FILL; - GridData gd = new GridData(hfill | GridData.VERTICAL_ALIGN_CENTER); - gd.horizontalSpan = span; - text.setLayoutData(gd); - return text; - } - - public static int computeMinimumWidth(GC gc, String text) { -// BreakIterator wb = BreakIterator.getWordInstance(); -// wb.setText(text); -// int last = 0; -// -// int width = 0; -// -// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { -// String word = text.substring(last, loc); -// Point extent = gc.textExtent(word); -// width = Math.max(width, extent.x); -// last = loc; -// } -// String lastWord = text.substring(last); -// Point extent = gc.textExtent(lastWord); -// width = Math.max(width, extent.x); -// return width; - return 0; - } - - public static Point computeWrapSize(GC gc, String text, int wHint) { -// BreakIterator wb = BreakIterator.getWordInstance(); -// wb.setText(text); - FontMetrics fm = gc.getFontMetrics(); - int lineHeight = fm.getHeight(); - - int saved = 0; - int last = 0; - int height = lineHeight; - int maxWidth = 0; -// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { -// String word = text.substring(saved, loc); -// Point extent = gc.textExtent(word); -// if (extent.x > wHint) { -// // overflow -// saved = last; -// height += extent.y; -// // switch to current word so maxWidth will accommodate very long single words -// word = text.substring(last, loc); -// extent = gc.textExtent(word); -// } -// maxWidth = Math.max(maxWidth, extent.x); -// last = loc; -// } - /* - * Correct the height attribute in case it was calculated wrong due to wHint being less than maxWidth. - * The recursive call proved to be the only thing that worked in all cases. Some attempts can be made - * to estimate the height, but the algorithm needs to be run again to be sure. - */ - if (maxWidth > wHint) - return computeWrapSize(gc, text, maxWidth); - return new Point(maxWidth, height); - } - -// RAP [rh] paintWrapText unnecessary -// public static void paintWrapText(GC gc, String text, Rectangle bounds) { -// paintWrapText(gc, text, bounds, false); -// } - -// RAP [rh] paintWrapText unnecessary -// public static void paintWrapText(GC gc, String text, Rectangle bounds, -// boolean underline) { -// BreakIterator wb = BreakIterator.getWordInstance(); -// wb.setText(text); -// FontMetrics fm = gc.getFontMetrics(); -// int lineHeight = fm.getHeight(); -// int descent = fm.getDescent(); -// -// int saved = 0; -// int last = 0; -// int y = bounds.y; -// int width = bounds.width; -// -// for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { -// String line = text.substring(saved, loc); -// Point extent = gc.textExtent(line); -// -// if (extent.x > width) { -// // overflow -// String prevLine = text.substring(saved, last); -// gc.drawText(prevLine, bounds.x, y, true); -// if (underline) { -// Point prevExtent = gc.textExtent(prevLine); -// int lineY = y + lineHeight - descent + 1; -// gc -// .drawLine(bounds.x, lineY, bounds.x + prevExtent.x, -// lineY); -// } -// -// saved = last; -// y += lineHeight; -// } -// last = loc; -// } -// // paint the last line -// String lastLine = text.substring(saved, last); -// gc.drawText(lastLine, bounds.x, y, true); -// if (underline) { -// int lineY = y + lineHeight - descent + 1; -// Point lastExtent = gc.textExtent(lastLine); -// gc.drawLine(bounds.x, lineY, bounds.x + lastExtent.x, lineY); -// } -// } - - public static ScrolledComposite getScrolledComposite(Control c) { - Composite parent = c.getParent(); - - while (parent != null) { - if (parent instanceof ScrolledComposite) { - return (ScrolledComposite) parent; - } - parent = parent.getParent(); - } - return null; - } - - public static void ensureVisible(Control c) { - ScrolledComposite scomp = getScrolledComposite(c); - if (scomp != null) { - Object data = scomp.getData(FOCUS_SCROLLING); - if (data == null || !data.equals(Boolean.FALSE)) - FormUtil.ensureVisible(scomp, c); - } - } - - public static void ensureVisible(ScrolledComposite scomp, Control control) { - // if the control is a FormText we do not need to scroll since it will - // ensure visibility of its segments as necessary -// if (control instanceof FormText) -// return; - Point controlSize = control.getSize(); - Point controlOrigin = getControlLocation(scomp, control); - ensureVisible(scomp, controlOrigin, controlSize); - } - - public static void ensureVisible(ScrolledComposite scomp, - Point controlOrigin, Point controlSize) { - Rectangle area = scomp.getClientArea(); - Point scompOrigin = scomp.getOrigin(); - - int x = scompOrigin.x; - int y = scompOrigin.y; - - // horizontal right, but only if the control is smaller - // than the client area - if (controlSize.x < area.width - && (controlOrigin.x + controlSize.x > scompOrigin.x - + area.width)) { - x = controlOrigin.x + controlSize.x - area.width; - } - // horizontal left - make sure the left edge of - // the control is showing - if (controlOrigin.x < x) { - if (controlSize.x < area.width) - x = controlOrigin.x + controlSize.x - area.width; - else - x = controlOrigin.x; - } - // vertical bottom - if (controlSize.y < area.height - && (controlOrigin.y + controlSize.y > scompOrigin.y - + area.height)) { - y = controlOrigin.y + controlSize.y - area.height; - } - // vertical top - make sure the top of - // the control is showing - if (controlOrigin.y < y) { - if (controlSize.y < area.height) - y = controlOrigin.y + controlSize.y - area.height; - else - y = controlOrigin.y; - } - - if (scompOrigin.x != x || scompOrigin.y != y) { - // scroll to reveal - scomp.setOrigin(x, y); - } - } - - public static void ensureVisible(ScrolledComposite scomp, Control control, - MouseEvent e) { - Point controlOrigin = getControlLocation(scomp, control); - int rX = controlOrigin.x + e.x; - int rY = controlOrigin.y + e.y; - Rectangle area = scomp.getClientArea(); - Point scompOrigin = scomp.getOrigin(); - - int x = scompOrigin.x; - int y = scompOrigin.y; - // System.out.println("Ensure: area="+area+", origin="+scompOrigin+", - // cloc="+controlOrigin+", csize="+controlSize+", x="+x+", y="+y); - - // horizontal right - if (rX > scompOrigin.x + area.width) { - x = rX - area.width; - } - // horizontal left - else if (rX < x) { - x = rX; - } - // vertical bottom - if (rY > scompOrigin.y + area.height) { - y = rY - area.height; - } - // vertical top - else if (rY < y) { - y = rY; - } - - if (scompOrigin.x != x || scompOrigin.y != y) { - // scroll to reveal - scomp.setOrigin(x, y); - } - } - - public static Point getControlLocation(ScrolledComposite scomp, - Control control) { - int x = 0; - int y = 0; - Control content = scomp.getContent(); - Control currentControl = control; - for (;;) { - if (currentControl == content) - break; - Point location = currentControl.getLocation(); - // if (location.x > 0) - // x += location.x; - // if (location.y > 0) - // y += location.y; - x += location.x; - y += location.y; - currentControl = currentControl.getParent(); - } - return new Point(x, y); - } - - static void scrollVertical(ScrolledComposite scomp, boolean up) { - scroll(scomp, 0, up ? -V_SCROLL_INCREMENT : V_SCROLL_INCREMENT); - } - - static void scrollHorizontal(ScrolledComposite scomp, boolean left) { - scroll(scomp, left ? -H_SCROLL_INCREMENT : H_SCROLL_INCREMENT, 0); - } - - static void scrollPage(ScrolledComposite scomp, boolean up) { - Rectangle clientArea = scomp.getClientArea(); - int increment = up ? -clientArea.height : clientArea.height; - scroll(scomp, 0, increment); - } - - static void scroll(ScrolledComposite scomp, int xoffset, int yoffset) { - Point origin = scomp.getOrigin(); - Point contentSize = scomp.getContent().getSize(); - int xorigin = origin.x + xoffset; - int yorigin = origin.y + yoffset; - xorigin = Math.max(xorigin, 0); - xorigin = Math.min(xorigin, contentSize.x - 1); - yorigin = Math.max(yorigin, 0); - yorigin = Math.min(yorigin, contentSize.y - 1); - scomp.setOrigin(xorigin, yorigin); - } - -// RAP [rh] FormUtil#updatePageIncrement: empty implementation - public static void updatePageIncrement(ScrolledComposite scomp) { -// ScrollBar vbar = scomp.getVerticalBar(); -// if (vbar != null) { -// Rectangle clientArea = scomp.getClientArea(); -// int increment = clientArea.height - 5; -// vbar.setPageIncrement(increment); -// } -// ScrollBar hbar = scomp.getHorizontalBar(); -// if (hbar != null) { -// Rectangle clientArea = scomp.getClientArea(); -// int increment = clientArea.width - 5; -// hbar.setPageIncrement(increment); -// } - } - - public static void processKey(int keyCode, Control c) { - if (c.isDisposed()) { - return; - } - ScrolledComposite scomp = FormUtil.getScrolledComposite(c); - if (scomp != null) { - if (c instanceof Combo) - return; - switch (keyCode) { - case SWT.ARROW_DOWN: - if (scomp.getData("novarrows") == null) //$NON-NLS-1$ - FormUtil.scrollVertical(scomp, false); - break; - case SWT.ARROW_UP: - if (scomp.getData("novarrows") == null) //$NON-NLS-1$ - FormUtil.scrollVertical(scomp, true); - break; - case SWT.ARROW_LEFT: - FormUtil.scrollHorizontal(scomp, true); - break; - case SWT.ARROW_RIGHT: - FormUtil.scrollHorizontal(scomp, false); - break; - case SWT.PAGE_UP: - FormUtil.scrollPage(scomp, true); - break; - case SWT.PAGE_DOWN: - FormUtil.scrollPage(scomp, false); - break; - } - } - } - - public static boolean isWrapControl(Control c) { - if ((c.getStyle() & SWT.WRAP) != 0) - return true; - if (c instanceof Composite) { - return false; -// return ((Composite) c).getLayout() instanceof ILayoutExtension; - } - return false; - } - - public static int getWidthHint(int wHint, Control c) { - boolean wrap = isWrapControl(c); - return wrap ? wHint : SWT.DEFAULT; - } - - public static int getHeightHint(int hHint, Control c) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); -// if (layout instanceof ColumnLayout) -// return hHint; - } - return SWT.DEFAULT; - } - - public static int computeMinimumWidth(Control c, boolean changed) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); -// if (layout instanceof ILayoutExtension) -// return ((ILayoutExtension) layout).computeMinimumWidth( -// (Composite) c, changed); - } - return c.computeSize(FormUtil.getWidthHint(5, c), SWT.DEFAULT, changed).x; - } - - public static int computeMaximumWidth(Control c, boolean changed) { - if (c instanceof Composite) { - Layout layout = ((Composite) c).getLayout(); -// if (layout instanceof ILayoutExtension) -// return ((ILayoutExtension) layout).computeMaximumWidth( -// (Composite) c, changed); - } - return c.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed).x; - } - -// public static Form getForm(Control c) { -// Composite parent = c.getParent(); -// while (parent != null) { -// if (parent instanceof Form) { -// return (Form) parent; -// } -// parent = parent.getParent(); -// } -// return null; -// } - -// RAP [rh] FormUtil#createAlphaMashImage unnecessary -// public static Image createAlphaMashImage(Device device, Image srcImage) { -// Rectangle bounds = srcImage.getBounds(); -// int alpha = 0; -// int calpha = 0; -// ImageData data = srcImage.getImageData(); -// // Create a new image with alpha values alternating -// // between fully transparent (0) and fully opaque (255). -// // This image will show the background through the -// // transparent pixels. -// for (int i = 0; i < bounds.height; i++) { -// // scan line -// alpha = calpha; -// for (int j = 0; j < bounds.width; j++) { -// // column -// data.setAlpha(j, i, alpha); -// alpha = alpha == 255 ? 0 : 255; -// } -// calpha = calpha == 255 ? 0 : 255; -// } -// return new Image(device, data); -// } - - public static boolean mnemonicMatch(String text, char key) { - char mnemonic = findMnemonic(text); - if (mnemonic == '\0') - return false; - return Character.toUpperCase(key) == Character.toUpperCase(mnemonic); - } - - private static char findMnemonic(String string) { - int index = 0; - int length = string.length(); - do { - while (index < length && string.charAt(index) != '&') - index++; - if (++index >= length) - return '\0'; - if (string.charAt(index) != '&') - return string.charAt(index); - index++; - } while (index < length); - return '\0'; - } - - public static void setFocusScrollingEnabled(Control c, boolean enabled) { - ScrolledComposite scomp = null; - - if (c instanceof ScrolledComposite) - scomp = (ScrolledComposite)c; - else - scomp = getScrolledComposite(c); - if (scomp!=null) - scomp.setData(FormUtil.FOCUS_SCROLLING, enabled?null:Boolean.FALSE); - } - - // RAP [rh] FormUtil#setAntialias unnecessary -// public static void setAntialias(GC gc, int style) { -// if (!gc.getAdvanced()) { -// gc.setAdvanced(true); -// if (!gc.getAdvanced()) -// return; -// } -// gc.setAntialias(style); -// } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java deleted file mode 100644 index cf0e5d35e..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IFormColors.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -/** - * A place to hold all the color constants used in the forms package. - * - * @since 1.0 - */ - -public interface IFormColors { - /** - * A prefix for all the keys. - */ - String PREFIX = "org.eclipse.ui.forms."; //$NON-NLS-1$ - /** - * Key for the form title foreground color. - */ - String TITLE = PREFIX + "TITLE"; //$NON-NLS-1$ - - /** - * A prefix for the header color constants. - */ - String H_PREFIX = PREFIX + "H_"; //$NON-NLS-1$ - /* - * A prefix for the section title bar color constants. - */ - String TB_PREFIX = PREFIX + "TB_"; //$NON-NLS-1$ - /** - * Key for the form header background gradient ending color. - */ - String H_GRADIENT_END = H_PREFIX + "GRADIENT_END"; //$NON-NLS-1$ - - /** - * Key for the form header background gradient starting color. - * - */ - String H_GRADIENT_START = H_PREFIX + "GRADIENT_START"; //$NON-NLS-1$ - /** - * Key for the form header bottom keyline 1 color. - * - */ - String H_BOTTOM_KEYLINE1 = H_PREFIX + "BOTTOM_KEYLINE1"; //$NON-NLS-1$ - /** - * Key for the form header bottom keyline 2 color. - * - */ - String H_BOTTOM_KEYLINE2 = H_PREFIX + "BOTTOM_KEYLINE2"; //$NON-NLS-1$ - /** - * Key for the form header light hover color. - * - */ - String H_HOVER_LIGHT = H_PREFIX + "H_HOVER_LIGHT"; //$NON-NLS-1$ - /** - * Key for the form header full hover color. - * - */ - String H_HOVER_FULL = H_PREFIX + "H_HOVER_FULL"; //$NON-NLS-1$ - - /** - * Key for the tree/table border color. - */ - String BORDER = PREFIX + "BORDER"; //$NON-NLS-1$ - - /** - * Key for the section separator color. - */ - String SEPARATOR = PREFIX + "SEPARATOR"; //$NON-NLS-1$ - - /** - * Key for the section title bar background. - */ - String TB_BG = TB_PREFIX + "BG"; //$NON-NLS-1$ - - /** - * Key for the section title bar foreground. - */ - String TB_FG = TB_PREFIX + "FG"; //$NON-NLS-1$ - - /** - * Key for the section title bar gradient. - * @deprecated Since 3.3, this color is not used any more. The - * tool bar gradient is created starting from {@link #TB_BG} to - * the section background color. - */ - String TB_GBG = TB_BG; - - /** - * Key for the section title bar border. - */ - String TB_BORDER = TB_PREFIX + "BORDER"; //$NON-NLS-1$ - - /** - * Key for the section toggle color. Since 3.1, this color is used for all - * section styles. - */ - String TB_TOGGLE = TB_PREFIX + "TOGGLE"; //$NON-NLS-1$ - - /** - * Key for the section toggle hover color. - * - */ - String TB_TOGGLE_HOVER = TB_PREFIX + "TOGGLE_HOVER"; //$NON-NLS-1$ -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java deleted file mode 100644 index 954cc0372..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IFormPart.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -/** - * Classes that implement this interface can be added to the managed form and - * take part in the form life cycle. The part is initialized with the form and - * will be asked to accept focus. The part can receive form input and can elect - * to do something according to it (for example, select an object that matches - * the input). - *

- * The form part has two 'out of sync' states in respect to the model(s) that - * feed the form: dirty and stale. When a part is dirty, it - * means that the user interacted with it and now its widgets contain state that - * is newer than the model. In order to sync up with the model, 'commit' needs - * to be called. In contrast, the model can change 'under' the form (as a result - * of some actions outside the form), resulting in data in the model being - * 'newer' than the content presented in the form. A 'stale' form part is - * brought in sync with the model by calling 'refresh'. The part is responsible - * for notifying the form when one of these states change in the part. The form - * reserves the right to handle this notification in the most appropriate way - * for the situation (for example, if the form is in a page of the multi-page - * editor, it may do nothing for stale parts if the page is currently not - * showing). - *

- * When the form is disposed, each registered part is disposed as well. Parts - * are responsible for releasing any system resources they created and for - * removing themselves as listeners from all event providers. - * - * @see IManagedForm - * @since 1.0 - * - */ -public interface IFormPart { - /** - * Initializes the part. - * - * @param form - * the managed form that manages the part - */ - void initialize(IManagedForm form); - - /** - * Disposes the part allowing it to release allocated resources. - */ - void dispose(); - - /** - * Returns true if the part has been modified with respect to the data - * loaded from the model. - * - * @return true if the part has been modified with respect to the data - * loaded from the model - */ - boolean isDirty(); - - /** - * If part is displaying information loaded from a model, this method - * instructs it to commit the new (modified) data back into the model. - * - * @param onSave - * indicates if commit is called during 'save' operation or for - * some other reason (for example, if form is contained in a - * wizard or a multi-page editor and the user is about to leave - * the page). - */ - void commit(boolean onSave); - - /** - * Notifies the part that an object has been set as overall form's input. - * The part can elect to react by revealing or selecting the object, or do - * nothing if not applicable. - * - * @return true if the part has selected and revealed the - * input object, false otherwise. - */ - boolean setFormInput(Object input); - - /** - * Instructs form part to transfer focus to the widget that should has focus - * in that part. The method can do nothing (if it has no widgets capable of - * accepting focus). - */ - void setFocus(); - - /** - * Tests whether the form part is stale and needs refreshing. Parts can - * receive notification from models that will make their content stale, but - * may need to delay refreshing to improve performance (for example, there - * is no need to immediately refresh a part on a form that is current on a - * hidden page). - *

- * It is important to differentiate 'stale' and 'dirty' states. Part is - * 'dirty' if user interacted with its editable widgets and changed the - * values. In contrast, part is 'stale' when the data it presents in the - * widgets has been changed in the model without direct user interaction. - * - * @return true if the part needs refreshing, - * false otherwise. - */ - boolean isStale(); - - /** - * Refreshes the part completely from the information freshly obtained from - * the model. The method will not be called if the part is not stale. - * Otherwise, the part is responsible for clearing the 'stale' flag after - * refreshing itself. - */ - void refresh(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java deleted file mode 100644 index 490d3a303..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IManagedForm.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.swt.custom.ScrolledComposite; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ScrolledForm; - -/** - * Managed form wraps a form widget and adds life cycle methods for form parts. - * A form part is a portion of the form that participates in form life cycle - * events. - *

- * There is no 1/1 mapping between widgets and form parts. A widget like Section - * can be a part by itself, but a number of widgets can gather around one form - * part. - *

- * This interface should not be extended or implemented. New form instances - * should be created using ManagedForm. - * - * @see ManagedForm - * @since 1.0 - * @noimplement This interface is not intended to be implemented by clients. - * @noextend This interface is not intended to be extended by clients. - */ -public interface IManagedForm { - /** - * Initializes the form by looping through the managed parts and - * initializing them. Has no effect if already called once. - */ - public void initialize(); - - /** - * Returns the toolkit used by this form. - * - * @return the toolkit - */ - public FormToolkit getToolkit(); - - /** - * Returns the form widget managed by this form. - * - * @return the form widget - */ - public ScrolledComposite getForm(); - - /** - * Reflows the form as a result of the layout change. - * - * @param changed - * if true, discard cached layout information - */ - public void reflow(boolean changed); - - /** - * A part can use this method to notify other parts that implement - * IPartSelectionListener about selection changes. - * - * @param part - * the part that broadcasts the selection - * @param selection - * the selection in the part - */ - public void fireSelectionChanged(IFormPart part, ISelection selection); - - /** - * Returns all the parts currently managed by this form. - * - * @return the managed parts - */ - IFormPart[] getParts(); - - /** - * Adds the new part to the form. - * - * @param part - * the part to add - */ - void addPart(IFormPart part); - - /** - * Removes the part from the form. - * - * @param part - * the part to remove - */ - void removePart(IFormPart part); - - /** - * Sets the input of this page to the provided object. - * - * @param input - * the new page input - * @return true if the form contains this object, - * false otherwise. - */ - boolean setInput(Object input); - - /** - * Returns the current page input. - * - * @return page input object or null if not applicable. - */ - Object getInput(); - - /** - * Tests if form is dirty. A managed form is dirty if at least one managed - * part is dirty. - * - * @return true if at least one managed part is dirty, - * false otherwise. - */ - boolean isDirty(); - - /** - * Notifies the form that the dirty state of one of its parts has changed. - * The global dirty state of the form can be obtained by calling 'isDirty'. - * - * @see #isDirty - */ - void dirtyStateChanged(); - - /** - * Commits the dirty form. All pending changes in the widgets are flushed - * into the model. - * - * @param onSave - */ - void commit(boolean onSave); - - /** - * Tests if form is stale. A managed form is stale if at least one managed - * part is stale. This can happen when the underlying model changes, - * resulting in the presentation of the part being out of sync with the - * model and needing refreshing. - * - * @return true if the form is stale, false - * otherwise. - */ - boolean isStale(); - - /** - * Notifies the form that the stale state of one of its parts has changed. - * The global stale state of the form can be obtained by calling 'isStale'. - */ - void staleStateChanged(); - - /** - * Refreshes the form by refreshing every part that is stale. - */ - void refresh(); - - /** - * Sets the container that owns this form. Depending on the context, the - * container may be wizard, editor page, editor etc. - * - * @param container - * the container of this form - */ - void setContainer(Object container); - - /** - * Returns the container of this form. - * - * @return the form container - */ - Object getContainer(); - - /** - * Returns the message manager that will keep track of messages in this - * form. - * - * @return the message manager instance - */ -// IMessageManager getMessageManager(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java deleted file mode 100644 index 0f557d41f..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/IPartSelectionListener.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import org.eclipse.jface.viewers.ISelection; - -/** - * Form parts can implement this interface if they want to be - * notified when another part on the same form changes selection - * state. - * - * @see IFormPart - * @since 1.0 - */ -public interface IPartSelectionListener { - /** - * Called when the provided part has changed selection state. - * - * @param part - * the selection source - * @param selection - * the new selection - */ - public void selectionChanged(IFormPart part, ISelection selection); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java deleted file mode 100644 index 4140465a1..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/ManagedForm.java +++ /dev/null @@ -1,323 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms; - -import java.util.Vector; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.widgets.Composite; -//import org.eclipse.ui.forms.widgets.FormToolkit; -//import org.eclipse.ui.forms.widgets.ScrolledForm; - -/** - * Managed form wraps a form widget and adds life cycle methods for form parts. - * A form part is a portion of the form that participates in form life cycle - * events. - *

- * There is requirement for 1/1 mapping between widgets and form parts. A widget - * like Section can be a part by itself, but a number of widgets can join around - * one form part. - *

- * Note to developers: this class is left public to allow its use beyond the - * original intention (inside a multi-page editor's page). You should limit the - * use of this class to make new instances inside a form container (wizard page, - * dialog etc.). Clients that need access to the class should not do it - * directly. Instead, they should do it through IManagedForm interface as much - * as possible. - * - * @since 1.0 - */ -public class ManagedForm implements IManagedForm { - private Object input; - - private ScrolledComposite form; - - private FormToolkit toolkit; - - private Object container; - - private boolean ownsToolkit; - - private boolean initialized; - - private Vector parts = new Vector(); - - /** - * Creates a managed form in the provided parent. Form toolkit and widget - * will be created and owned by this object. - * - * @param parent - * the parent widget - */ - public ManagedForm(Composite parent) { - toolkit = new FormToolkit(parent.getDisplay()); - ownsToolkit = true; - form = toolkit.createScrolledForm(parent); - - } - - /** - * Creates a managed form that will use the provided toolkit and - * - * @param toolkit - * @param form - */ - public ManagedForm(FormToolkit toolkit, ScrolledComposite form) { - this.form = form; - this.toolkit = toolkit; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#addPart(org.eclipse.ui.forms.IFormPart) - */ - public void addPart(IFormPart part) { - parts.add(part); - part.initialize(this); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#removePart(org.eclipse.ui.forms.IFormPart) - */ - public void removePart(IFormPart part) { - parts.remove(part); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getParts() - */ - public IFormPart[] getParts() { - return (IFormPart[]) parts.toArray(new IFormPart[parts.size()]); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getToolkit() - */ - public FormToolkit getToolkit() { - return toolkit; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getForm() - */ - public ScrolledComposite getForm() { - return form; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#reflow(boolean) - */ - public void reflow(boolean changed) { -// form.reflow(changed); - } - - /** - * A part can use this method to notify other parts that implement - * IPartSelectionListener about selection changes. - * - * @param part - * the part that broadcasts the selection - * @param selection - * the selection in the part - * @see IPartSelectionListener - */ - public void fireSelectionChanged(IFormPart part, ISelection selection) { - for (int i = 0; i < parts.size(); i++) { - IFormPart cpart = (IFormPart) parts.get(i); - if (part.equals(cpart)) - continue; -// if (cpart instanceof IPartSelectionListener) { -// ((IPartSelectionListener) cpart).selectionChanged(part, -// selection); -// } - } - } - - /** - * Initializes the form by looping through the managed parts and - * initializing them. Has no effect if already called once. - */ - public void initialize() { - if (initialized) - return; - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - part.initialize(this); - } - initialized = true; - } - - /** - * Disposes all the parts in this form. - */ - public void dispose() { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - part.dispose(); - } - if (ownsToolkit) { - toolkit.dispose(); - } - } - - /** - * Refreshes the form by refreshes all the stale parts. Since 3.1, this - * method is performed on a UI thread when called from another thread so it - * is not needed to wrap the call in Display.syncExec or - * asyncExec. - */ - public void refresh() { - Thread t = Thread.currentThread(); - Thread dt = toolkit.getColors().getDisplay().getThread(); - if (t.equals(dt)) - doRefresh(); - else { - toolkit.getColors().getDisplay().asyncExec(new Runnable() { - public void run() { - doRefresh(); - } - }); - } - } - - private void doRefresh() { - int nrefreshed = 0; - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isStale()) { - part.refresh(); - nrefreshed++; - } - } -// if (nrefreshed > 0) -// form.reflow(true); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#commit(boolean) - */ - public void commit(boolean onSave) { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isDirty()) - part.commit(onSave); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#setInput(java.lang.Object) - */ - public boolean setInput(Object input) { - boolean pageResult = false; - - this.input = input; - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - boolean result = part.setFormInput(input); - if (result) - pageResult = true; - } - return pageResult; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getInput() - */ - public Object getInput() { - return input; - } - - /** - * Transfers the focus to the first form part. - */ - public void setFocus() { - if (parts.size() > 0) { - IFormPart part = (IFormPart) parts.get(0); - part.setFocus(); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#isDirty() - */ - public boolean isDirty() { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isDirty()) - return true; - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#isStale() - */ - public boolean isStale() { - for (int i = 0; i < parts.size(); i++) { - IFormPart part = (IFormPart) parts.get(i); - if (part.isStale()) - return true; - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#dirtyStateChanged() - */ - public void dirtyStateChanged() { - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#staleStateChanged() - */ - public void staleStateChanged() { - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#getContainer() - */ - public Object getContainer() { - return container; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.forms.IManagedForm#setContainer(java.lang.Object) - */ - public void setContainer(Object container) { - this.container = container; - } - - /* (non-Javadoc) - * @see org.eclipse.ui.forms.IManagedForm#getMessageManager() - */ -// public IMessageManager getMessageManager() { -// return form.getMessageManager(); -// } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java deleted file mode 100644 index 484dae842..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormEditor.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms.editor; - -import org.argeo.cms.ui.eclipse.forms.FormToolkit; -import org.eclipse.jface.dialogs.IPageChangeProvider; - -/** - * This class forms a base of multi-page form editors that typically use one or - * more pages with forms and one page for raw source of the editor input. - *

- * Pages are added 'lazily' i.e. adding a page reserves a tab for it but does - * not cause the page control to be created. Page control is created when an - * attempt is made to select the page in question. This allows editors with - * several tabs and complex pages to open quickly. - *

- * Subclasses should extend this class and implement addPages - * method. One of the two addPage methods should be called to - * contribute pages to the editor. One adds complete (standalone) editors as - * nested tabs. These editors will be created right away and will be hooked so - * that key bindings, selection service etc. is compatible with the one for the - * standalone case. The other method adds classes that implement - * IFormPage interface. These pages will be created lazily and - * they will share the common key binding and selection service. Since 3.1, - * FormEditor is a page change provider. It allows listeners to attach to it and - * get notified when pages are changed. This new API in JFace allows dynamic - * help to update on page changes. - * - * @since 1.0 - */ -// RAP [if] As RAP is still using workbench 3.4, the implementation of -// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code -// with the adoption of workbench > 3.5 -//public abstract class FormEditor extends MultiPageEditorPart { -public abstract class FormEditor implements - IPageChangeProvider { - private FormToolkit formToolkit; - - -public FormToolkit getToolkit() { - return formToolkit; - } - -public void editorDirtyStateChanged() { - -} - -public FormPage getActivePageInstance() { - return null; -} - - // RAP [if] As RAP is still using workbench 3.4, the implementation of -// IPageChangeProvider is missing from MultiPageEditorPart. Remove this code -// with the adoption of workbench > 3.5 -// private ListenerList pageListeners = new ListenerList(); -// -// /* -// * (non-Javadoc) -// * -// * @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) -// */ -// public void addPageChangedListener(IPageChangedListener listener) { -// pageListeners.add(listener); -// } -// -// /* -// * (non-Javadoc) -// * -// * @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener) -// */ -// public void removePageChangedListener(IPageChangedListener listener) { -// pageListeners.remove(listener); -// } -// -// private void firePageChanged(final PageChangedEvent event) { -// Object[] listeners = pageListeners.getListeners(); -// for (int i = 0; i < listeners.length; ++i) { -// final IPageChangedListener l = (IPageChangedListener) listeners[i]; -// SafeRunnable.run(new SafeRunnable() { -// public void run() { -// l.pageChanged(event); -// } -// }); -// } -// } -// RAPEND [if] -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java deleted file mode 100644 index a788412db..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/FormPage.java +++ /dev/null @@ -1,276 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms.editor; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.argeo.cms.ui.eclipse.forms.ManagedForm; -import org.eclipse.swt.custom.BusyIndicator; -import org.eclipse.swt.custom.ScrolledComposite; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -/** - * A base class that all pages that should be added to FormEditor must subclass. - * Form page has an instance of PageForm that extends managed form. Subclasses - * should override method 'createFormContent(ManagedForm)' to fill the form with - * content. Note that page itself can be loaded lazily (on first open). - * Consequently, the call to create the form content can come after the editor - * has been opened for a while (in fact, it is possible to open and close the - * editor and never create the form because no attempt has been made to show the - * page). - * - * @since 1.0 - */ -public class FormPage implements IFormPage { - private FormEditor editor; - private PageForm mform; - private int index; - private String id; - - private String partName; - - - - public void setPartName(String partName) { - this.partName = partName; - } - private static class PageForm extends ManagedForm { - public PageForm(FormPage page, ScrolledComposite form) { - super(page.getEditor().getToolkit(), form); - setContainer(page); - } - - public FormPage getPage() { - return (FormPage)getContainer(); - } - public void dirtyStateChanged() { - getPage().getEditor().editorDirtyStateChanged(); - } - public void staleStateChanged() { - if (getPage().isActive()) - refresh(); - } - } - /** - * A constructor that creates the page and initializes it with the editor. - * - * @param editor - * the parent editor - * @param id - * the unique identifier - * @param title - * the page title - */ - public FormPage(FormEditor editor, String id, String title) { - this(id, title); - initialize(editor); - } - /** - * The constructor. The parent editor need to be passed in the - * initialize method if this constructor is used. - * - * @param id - * a unique page identifier - * @param title - * a user-friendly page title - */ - public FormPage(String id, String title) { - this.id = id; - setPartName(title); - } - /** - * Initializes the form page. - * - * @see IEditorPart#init - */ -// public void init(IEditorSite site, IEditorInput input) { -// setSite(site); -// setInput(input); -// } - /** - * Primes the form page with the parent editor instance. - * - * @param editor - * the parent editor - */ - public void initialize(FormEditor editor) { - this.editor = editor; - } - /** - * Returns the parent editor. - * - * @return parent editor instance - */ - public FormEditor getEditor() { - return editor; - } - /** - * Returns the managed form owned by this page. - * - * @return the managed form - */ - public IManagedForm getManagedForm() { - return mform; - } - /** - * Implements the required method by refreshing the form when set active. - * Subclasses must call super when overriding this method. - */ - public void setActive(boolean active) { - if (active) { - // We are switching to this page - refresh it - // if needed. - if (mform != null) - mform.refresh(); - } - } - /** - * Tests if the page is active by asking the parent editor if this page is - * the currently active page. - * - * @return true if the page is currently active, - * false otherwise. - */ - public boolean isActive() { - return this.equals(editor.getActivePageInstance()); - } - /** - * Creates the part control by creating the managed form using the parent - * editor's toolkit. Subclasses should override - * createFormContent(IManagedForm) to populate the form with - * content. - * - * @param parent - * the page parent composite - */ - public void createPartControl(Composite parent) { - ScrolledComposite form = editor.getToolkit().createScrolledForm(parent); - mform = new PageForm(this, form); - BusyIndicator.showWhile(parent.getDisplay(), new Runnable() { - public void run() { - createFormContent(mform); - } - }); - } - /** - * Subclasses should override this method to create content in the form - * hosted in this page. - * - * @param managedForm - * the form hosted in this page. - */ - protected void createFormContent(IManagedForm managedForm) { - } - /** - * Returns the form page control. - * - * @return managed form's control - */ - public Control getPartControl() { - return mform != null ? mform.getForm() : null; - } - /** - * Disposes the managed form. - */ - public void dispose() { - if (mform != null) - mform.dispose(); - } - /** - * Returns the unique identifier that can be used to reference this page. - * - * @return the unique page identifier - */ - public String getId() { - return id; - } - /** - * Returns null- form page has no title image. Subclasses - * may override. - * - * @return null - */ - public Image getTitleImage() { - return null; - } - /** - * Sets the focus by delegating to the managed form. - */ - public void setFocus() { - if (mform != null) - mform.setFocus(); - } - /** - * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) - */ -// public void doSave(IProgressMonitor monitor) { -// if (mform != null) -// mform.commit(true); -// } - /** - * @see org.eclipse.ui.ISaveablePart#doSaveAs() - */ - public void doSaveAs() { - } - /** - * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() - */ - public boolean isSaveAsAllowed() { - return false; - } - /** - * Implemented by testing if the managed form is dirty. - * - * @return true if the managed form is dirty, - * false otherwise. - * - * @see org.eclipse.ui.ISaveablePart#isDirty() - */ - public boolean isDirty() { - return mform != null ? mform.isDirty() : false; - } - /** - * Preserves the page index. - * - * @param index - * the assigned page index - */ - public void setIndex(int index) { - this.index = index; - } - /** - * Returns the saved page index. - * - * @return the page index - */ - public int getIndex() { - return index; - } - /** - * Form pages are not editors. - * - * @return false - */ - public boolean isEditor() { - return false; - } - /** - * Attempts to select and reveal the given object by passing the request to - * the managed form. - * - * @param object - * the object to select and reveal in the page if possible. - * @return true if the page has been successfully selected - * and revealed by one of the managed form parts, false - * otherwise. - */ - public boolean selectReveal(Object object) { - if (mform != null) - return mform.setInput(object); - return false; - } - /** - * By default, editor will be allowed to flip the page. - * @return true - */ - public boolean canLeaveThePage() { - return true; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java deleted file mode 100644 index eb08cb59d..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/eclipse/forms/editor/IFormPage.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.argeo.cms.ui.eclipse.forms.editor; -import org.argeo.cms.ui.eclipse.forms.IManagedForm; -import org.eclipse.swt.widgets.Control; -/** - * Interface that all GUI pages need to implement in order - * to be added to FormEditor part. The interface makes - * several assumptions: - *

    - *
  • The form page has a managed form
  • - *
  • The form page has a unique id
  • - *
  • The form page can be GUI but can also wrap a complete - * editor class (in that case, it should return true - * from isEditor() method).
  • - *
  • The form page is lazy i.e. understands that - * its part control will be created at the last possible - * moment.
  • . - *
- *

Existing editors can be wrapped by implementing - * this interface. In this case, 'isEditor' should return true. - * A common editor to wrap in TextEditor that is - * often added to show the raw source code of the file open into - * the multi-page editor. - * - * @since 1.0 - */ -public interface IFormPage { - /** - * @param editor - * the form editor that this page belongs to - */ - void initialize(FormEditor editor); - /** - * Returns the editor this page belongs to. - * - * @return the form editor - */ - FormEditor getEditor(); - /** - * Returns the managed form of this page, unless this is a source page. - * - * @return the managed form or null if this is a source page. - */ - IManagedForm getManagedForm(); - /** - * Indicates whether the page has become the active in the editor. Classes - * that implement this interface may use this method to commit the page (on - * false) or lazily create and/or populate the content on - * true. - * - * @param active - * true if page should be visible, false - * otherwise. - */ - void setActive(boolean active); - /** - * Returns true if page is currently active, false if not. - * - * @return true for active page. - */ - boolean isActive(); - /** - * Tests if the content of the page is in a state that allows the - * editor to flip to another page. Typically, pages that contain - * raw source with syntax errors should not allow editors to - * leave them until errors are corrected. - * @return true if the editor can flip to another page, - * false otherwise. - */ - boolean canLeaveThePage(); - /** - * Returns the control associated with this page. - * - * @return the control of this page if created or null if the - * page has not been shown yet. - */ - Control getPartControl(); - /** - * Page must have a unique id that can be used to show it without knowing - * its relative position in the editor. - * - * @return the unique page identifier - */ - String getId(); - /** - * Returns the position of the page in the editor. - * - * @return the zero-based index of the page in the editor. - */ - int getIndex(); - /** - * Sets the position of the page in the editor. - * - * @param index - * the zero-based index of the page in the editor. - */ - void setIndex(int index); - /** - * Tests whether this page wraps a complete editor that - * can be registered on its own, or represents a page - * that cannot exist outside the multi-page editor context. - * - * @return true if the page wraps an editor, - * false if this is a form page. - */ - boolean isEditor(); - /** - * A hint to bring the provided object into focus. If the object is in a - * tree or table control, select it. If it is shown on a scrollable page, - * ensure that it is visible. If the object is not presented in - * the page, false should be returned to allow another - * page to try. - * - * @param object - * object to select and reveal - * @return true if the request was successful, false - * otherwise. - */ - boolean selectReveal(Object object); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableLink.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableLink.java deleted file mode 100644 index 3c1e8cda5..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableLink.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.argeo.cms.ui.forms; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable String that displays a browsable link when read-only */ -public class EditableLink extends EditablePropertyString implements - SwtEditablePart { - private static final long serialVersionUID = 5055000749992803591L; - - private String type; - private String message; - private boolean readOnly; - - public EditableLink(Composite parent, int style, Node node, - String propertyName, String type, String message) - throws RepositoryException { - super(parent, style, node, propertyName, message); - this.message = message; - this.type = type; - - readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - if (node.hasProperty(propertyName)) { - this.setStyle(FormStyle.propertyText.style()); - this.setText(node.getProperty(propertyName).getString()); - } else { - this.setStyle(FormStyle.propertyMessage.style()); - this.setText(""); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (EclipseUiUtils.isEmpty(text)) - lbl.setText(message); - else if (readOnly) - setLinkValue(lbl, text); - else - // if canEdit() we put only the value with no link - // to avoid glitches of the edition life cycle - lbl.setText(text); - } else if (child instanceof Text) { - Text txt = (Text) child; - if (EclipseUiUtils.isEmpty(text)) { - txt.setText(""); - txt.setMessage(message); - } else - txt.setText(text); - } - } - - private void setLinkValue(Label lbl, String text) { - if (FormStyle.email.style().equals(type)) - lbl.setText(FormUtils.getMailLink(text)); - else if (FormStyle.phone.style().equals(type)) - lbl.setText(FormUtils.getPhoneLink(text)); - else if (FormStyle.website.style().equals(type)) - lbl.setText(FormUtils.getUrlLink(text)); - else if (FormStyle.facebook.style().equals(type) - || FormStyle.instagram.style().equals(type) - || FormStyle.linkedIn.style().equals(type) - || FormStyle.twitter.style().equals(type)) - lbl.setText(FormUtils.getUrlLink(text)); - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java deleted file mode 100644 index ff8270046..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditableMultiStringProperty.java +++ /dev/null @@ -1,261 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -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; - -/** Display, add or remove values from a list in a CMS context */ -public class EditableMultiStringProperty extends StyledControl implements SwtEditablePart { - private static final long serialVersionUID = -7044614381252178595L; - - private String propertyName; - private String message; - // TODO implement the ability to provide a list of possible values -// private String[] possibleValues; - private boolean canEdit; - private SelectionListener removeValueSL; - private List values; - - // TODO manage within the CSS - private int rowSpacing = 5; - private int rowMarging = 0; - private int oneValueMargingRight = 5; - private int btnWidth = 16; - private int btnHeight = 16; - private int btnHorizontalIndent = 3; - - public EditableMultiStringProperty(Composite parent, int style, Node node, String propertyName, List values, - String[] possibleValues, String addValueMsg, SelectionListener removeValueSelectionListener) - throws RepositoryException { - super(parent, style, node, true); - - this.propertyName = propertyName; - this.values = values; -// this.possibleValues = new String[] { "Un", "Deux", "Trois" }; - this.message = addValueMsg; - this.canEdit = removeValueSelectionListener != null; - this.removeValueSL = removeValueSelectionListener; - } - - public List getValues() { - return values; - } - - public void setValues(List values) { - this.values = values; - } - - // Row layout items do not need explicit layout data - protected void setControlLayoutData(Control control) { - } - - /** To be overridden */ - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsSwtUtils.fillWidth()); - } - - @Override - public Control getControl() { - return super.getControl(); - } - - @Override - protected Control createControl(Composite box, String style) { - Composite row = new Composite(box, SWT.NO_FOCUS); - row.setLayoutData(EclipseUiUtils.fillAll()); - - RowLayout rl = new RowLayout(SWT.HORIZONTAL); - rl.wrap = true; - rl.spacing = rowSpacing; - rl.marginRight = rl.marginLeft = rl.marginBottom = rl.marginTop = rowMarging; - row.setLayout(rl); - - if (values != null) { - for (final String value : values) { - if (canEdit) - createRemovableValue(row, SWT.SINGLE, value); - else - createValueLabel(row, SWT.SINGLE, value); - } - } - - if (!canEdit) - return row; - else if (isEditing()) - return createText(row, style); - else - return createLabel(row, style); - } - - /** - * Override to provide specific layout for the existing values, typically adding - * a pound (#) char for tags or anchor info for browsable links. We assume the - * parent composite already has a layout and it is the caller responsibility to - * apply corresponding layout data - */ - protected Label createValueLabel(Composite parent, int style, String value) { - Label label = new Label(parent, style); - label.setText("#" + value); - CmsSwtUtils.markup(label); - CmsSwtUtils.style(label, FormStyle.propertyText.style()); - return label; - } - - private Composite createRemovableValue(Composite parent, int style, String value) { - Composite valCmp = new Composite(parent, SWT.NO_FOCUS); - GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); - gl.marginRight = oneValueMargingRight; - valCmp.setLayout(gl); - - createValueLabel(valCmp, SWT.WRAP, value); - - Button deleteBtn = new Button(valCmp, SWT.FLAT); - deleteBtn.setData(FormConstants.LINKED_VALUE, value); - deleteBtn.addSelectionListener(removeValueSL); - CmsSwtUtils.style(deleteBtn, FormStyle.delete.style() + FormStyle.BUTTON_SUFFIX); - GridData gd = new GridData(); - gd.heightHint = btnHeight; - gd.widthHint = btnWidth; - gd.horizontalIndent = btnHorizontalIndent; - deleteBtn.setLayoutData(gd); - - return valCmp; - } - - protected Text createText(Composite box, String style) { - final Text text = new Text(box, getStyle()); - // The "add new value" text is not meant to change, so we can set it on - // creation - text.setMessage(message); - CmsSwtUtils.style(text, style); - text.setFocus(); - - text.addTraverseListener(new TraverseListener() { - private static final long serialVersionUID = 1L; - - public void keyTraversed(TraverseEvent e) { - if (e.keyCode == SWT.CR) { - addValue(text); - e.doit = false; - } - } - }); - - // The OK button does not work with the focusOut listener - // because focus out is called before the OK button is pressed - - // // we must call layout() now so that the row data can compute the - // height - // // of the other controls. - // text.getParent().layout(); - // int height = text.getSize().y; - // - // Button okBtn = new Button(box, SWT.BORDER | SWT.PUSH | SWT.BOTTOM); - // okBtn.setText("OK"); - // RowData rd = new RowData(SWT.DEFAULT, height - 2); - // okBtn.setLayoutData(rd); - // - // okBtn.addSelectionListener(new SelectionAdapter() { - // private static final long serialVersionUID = 2780819012423622369L; - // - // @Override - // public void widgetSelected(SelectionEvent e) { - // addValue(text); - // } - // }); - - return text; - } - - /** Performs the real addition, overwrite to make further sanity checks */ - protected void addValue(Text text) { - String value = text.getText(); - String errMsg = null; - - if (EclipseUiUtils.isEmpty(value)) - return; - - if (values.contains(value)) - errMsg = "Dupplicated value: " + value + ", please correct and try again"; - if (errMsg != null) - MessageDialog.openError(this.getShell(), "Addition not allowed", errMsg); - else { - values.add(value); - Composite newCmp = createRemovableValue(text.getParent(), SWT.SINGLE, value); - newCmp.moveAbove(text); - text.setText(""); - newCmp.getParent().layout(); - } - } - - protected Label createLabel(Composite box, String style) { - if (canEdit) { - Label lbl = new Label(box, getStyle()); - lbl.setText(message); - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - return null; - } - - protected void clear(boolean deep) { - Control child = getControl(); - if (deep) - super.clear(deep); - else { - child.getParent().dispose(); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (canEdit) - lbl.setText(text); - else - lbl.setText(""); - } else if (child instanceof Text) { - Text txt = (Text) child; - txt.setText(text); - } - } - - public synchronized void startEditing() { - CmsSwtUtils.style(getControl(), FormStyle.propertyText.style()); -// getControl().setData(STYLE, FormStyle.propertyText.style()); - super.startEditing(); - } - - public synchronized void stopEditing() { - CmsSwtUtils.style(getControl(), FormStyle.propertyMessage.style()); -// getControl().setData(STYLE, FormStyle.propertyMessage.style()); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java deleted file mode 100644 index 641f916f2..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyDate.java +++ /dev/null @@ -1,298 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.text.DateFormat; -import java.util.Calendar; -import java.util.GregorianCalendar; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -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.Control; -import org.eclipse.swt.widgets.DateTime; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** CMS form part to display and edit a date */ -public class EditablePropertyDate extends StyledControl implements SwtEditablePart { - private static final long serialVersionUID = 2500215515778162468L; - - // Context - private String propertyName; - private String message; - private DateFormat dateFormat; - - // UI Objects - private Text dateTxt; - private Button openCalBtn; - - // TODO manage within the CSS - private int fieldBtnSpacing = 5; - - /** - * - * @param parent - * @param style - * @param node - * @param propertyName - * @param message - * @param dateFormat provide a {@link DateFormat} as contract to be able to - * read/write dates as strings - * @throws RepositoryException - */ - public EditablePropertyDate(Composite parent, int style, Node node, String propertyName, String message, - DateFormat dateFormat) throws RepositoryException { - super(parent, style, node, false); - - this.propertyName = propertyName; - this.message = message; - this.dateFormat = dateFormat; - - if (node.hasProperty(propertyName)) { - this.setStyle(FormStyle.propertyText.style()); - this.setText(dateFormat.format(node.getProperty(propertyName).getDate().getTime())); - } else { - this.setStyle(FormStyle.propertyMessage.style()); - this.setText(message); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (EclipseUiUtils.isEmpty(text)) - lbl.setText(message); - else - lbl.setText(text); - } else if (child instanceof Text) { - Text txt = (Text) child; - if (EclipseUiUtils.isEmpty(text)) { - txt.setText(""); - } else - txt.setText(text); - } - } - - public synchronized void startEditing() { - // if (dateTxt != null && !dateTxt.isDisposed()) - CmsSwtUtils.style(getControl(), FormStyle.propertyText); -// getControl().setData(STYLE, FormStyle.propertyText.style()); - super.startEditing(); - } - - public synchronized void stopEditing() { - if (EclipseUiUtils.isEmpty(dateTxt.getText())) - CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); -// getControl().setData(STYLE, FormStyle.propertyMessage.style()); - else - CmsSwtUtils.style(getControl(), FormStyle.propertyText); -// getControl().setData(STYLE, FormStyle.propertyText.style()); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) { - return createCustomEditableControl(box, style); - } else - return createLabel(box, style); - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle() | SWT.WRAP); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - private Control createCustomEditableControl(Composite box, String style) { - box.setLayoutData(CmsSwtUtils.fillWidth()); - Composite dateComposite = new Composite(box, SWT.NONE); - GridLayout gl = EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false)); - gl.horizontalSpacing = fieldBtnSpacing; - dateComposite.setLayout(gl); - dateTxt = new Text(dateComposite, SWT.BORDER); - CmsSwtUtils.style(dateTxt, style); - dateTxt.setLayoutData(new GridData(120, SWT.DEFAULT)); - dateTxt.setToolTipText( - "Enter a date with form \"" + FormUtils.DEFAULT_SHORT_DATE_FORMAT + "\" or use the calendar"); - openCalBtn = new Button(dateComposite, SWT.FLAT); - CmsSwtUtils.style(openCalBtn, FormStyle.calendar.style() + FormStyle.BUTTON_SUFFIX); - GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false); - gd.heightHint = 17; - openCalBtn.setLayoutData(gd); - // openCalBtn.setImage(PeopleRapImages.CALENDAR_BTN); - - openCalBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 1L; - - public void widgetSelected(SelectionEvent event) { - CalendarPopup popup = new CalendarPopup(dateTxt); - popup.open(); - } - }); - - // dateTxt.addFocusListener(new FocusListener() { - // private static final long serialVersionUID = 1L; - // - // @Override - // public void focusLost(FocusEvent event) { - // String newVal = dateTxt.getText(); - // // Enable reset of the field - // if (FormUtils.notNull(newVal)) - // calendar = null; - // else { - // try { - // Calendar newCal = parseDate(newVal); - // // DateText.this.setText(newCal); - // calendar = newCal; - // } catch (ParseException pe) { - // // Silent. Manage error popup? - // if (calendar != null) - // EditablePropertyDate.this.setText(calendar); - // } - // } - // } - // - // @Override - // public void focusGained(FocusEvent event) { - // } - // }); - return dateTxt; - } - - protected void clear(boolean deep) { - Control child = getControl(); - if (deep || child instanceof Label) - super.clear(deep); - else { - child.getParent().dispose(); - } - } - - /** Enable setting a custom tooltip on the underlying text */ - @Deprecated - public void setToolTipText(String toolTipText) { - dateTxt.setToolTipText(toolTipText); - } - - @Deprecated - /** Enable setting a custom message on the underlying text */ - public void setMessage(String message) { - dateTxt.setMessage(message); - } - - @Deprecated - public void setText(Calendar cal) { - String newValueStr = ""; - if (cal != null) - newValueStr = dateFormat.format(cal.getTime()); - if (!newValueStr.equals(dateTxt.getText())) - dateTxt.setText(newValueStr); - } - - // UTILITIES TO MANAGE THE CALENDAR POPUP - // TODO manage the popup shell in a cleaner way - private class CalendarPopup extends Shell { - private static final long serialVersionUID = 1L; - private DateTime dateTimeCtl; - - public CalendarPopup(Control source) { - super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - populate(); - // Add border and shadow style - CmsSwtUtils.markup(CalendarPopup.this); - CmsSwtUtils.style(CalendarPopup.this, FormStyle.popupCalendar.style()); - pack(); - layout(); - setLocation(source.toDisplay((source.getLocation().x - 2), (source.getSize().y) + 3)); - - addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - close(); - dispose(); - } - }); - open(); - } - - private void setProperty() { - // Direct set does not seems to work. investigate - // cal.set(dateTimeCtl.getYear(), dateTimeCtl.getMonth(), - // dateTimeCtl.getDay(), 12, 0); - Calendar cal = new GregorianCalendar(); - cal.set(Calendar.YEAR, dateTimeCtl.getYear()); - cal.set(Calendar.MONTH, dateTimeCtl.getMonth()); - cal.set(Calendar.DAY_OF_MONTH, dateTimeCtl.getDay()); - String dateStr = dateFormat.format(cal.getTime()); - dateTxt.setText(dateStr); - } - - protected void populate() { - setLayout(EclipseUiUtils.noSpaceGridLayout()); - - dateTimeCtl = new DateTime(this, SWT.CALENDAR); - dateTimeCtl.setLayoutData(EclipseUiUtils.fillAll()); - - Calendar calendar = FormUtils.parseDate(dateFormat, dateTxt.getText()); - - if (calendar != null) - dateTimeCtl.setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH)); - - dateTimeCtl.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -8414377364434281112L; - - @Override - public void widgetSelected(SelectionEvent e) { - setProperty(); - } - }); - - dateTimeCtl.addMouseListener(new MouseListener() { - private static final long serialVersionUID = 1L; - - @Override - public void mouseUp(MouseEvent e) { - } - - @Override - public void mouseDown(MouseEvent e) { - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - setProperty(); - close(); - dispose(); - } - }); - } - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java deleted file mode 100644 index f2575e1f9..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/EditablePropertyString.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.argeo.cms.ui.forms; - -import static org.argeo.cms.ui.forms.FormStyle.propertyMessage; -import static org.argeo.cms.ui.forms.FormStyle.propertyText; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.EditableText; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable String in a CMS context */ -public class EditablePropertyString extends EditableText implements SwtEditablePart { - private static final long serialVersionUID = 5055000749992803591L; - - private String propertyName; - private String message; - - // encode the '&' character in rap - private final static String AMPERSAND = "&"; - private final static String AMPERSAND_REGEX = "&(?![#a-zA-Z0-9]+;)"; - - public EditablePropertyString(Composite parent, int style, Node node, String propertyName, String message) - throws RepositoryException { - super(parent, style, node, true); - //setUseTextAsLabel(true); - this.propertyName = propertyName; - this.message = message; - - if (node.hasProperty(propertyName)) { - this.setStyle(propertyText.style()); - this.setText(node.getProperty(propertyName).getString()); - } else { - this.setStyle(propertyMessage.style()); - this.setText(message + " "); - } - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) { - Label lbl = (Label) child; - if (EclipseUiUtils.isEmpty(text)) - lbl.setText(message + " "); - else - // TODO enhance this - lbl.setText(text.replaceAll(AMPERSAND_REGEX, AMPERSAND)); - } else if (child instanceof Text) { - Text txt = (Text) child; - if (EclipseUiUtils.isEmpty(text)) { - txt.setText(""); - txt.setMessage(message + " "); - } else - txt.setText(text.replaceAll("
", "\n")); - } - } - - public synchronized void startEditing() { - CmsSwtUtils.style(getControl(), FormStyle.propertyText); - super.startEditing(); - } - - public synchronized void stopEditing() { - if (EclipseUiUtils.isEmpty(((Text) getControl()).getText())) - CmsSwtUtils.style(getControl(), FormStyle.propertyMessage); - else - CmsSwtUtils.style(getControl(), FormStyle.propertyText); - super.stopEditing(); - } - - public String getPropertyName() { - return propertyName; - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormConstants.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormConstants.java deleted file mode 100644 index fe9f7e7d7..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormConstants.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.argeo.cms.ui.forms; - -/** Constants used in the various CMS Forms */ -public interface FormConstants { - // DATAKEYS - public final static String LINKED_VALUE = "LinkedValue"; -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java deleted file mode 100644 index a75c19150..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormEditorHeader.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.util.Observable; -import java.util.Observer; - -import javax.jcr.Node; - -import org.argeo.api.cms.ux.CmsEditable; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; - -/** Add life cycle management abilities to an editable form page */ -public class FormEditorHeader implements SelectionListener, Observer { - private static final long serialVersionUID = 7392898696542484282L; - - // private final Node context; - private final CmsEditable cmsEditable; - private Button publishBtn; - - // Should we provide here the ability to switch from read only to edition - // mode? - // private Button editBtn; - // private boolean readOnly; - - // TODO add information about the current node status, typically if it is - // dirty or not - - private Composite parent; - private Composite display; - private Object layoutData; - - public FormEditorHeader(Composite parent, int style, Node context, - CmsEditable cmsEditable) { - this.cmsEditable = cmsEditable; - this.parent = parent; - // readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - // this.context = context; - if (this.cmsEditable instanceof Observable) - ((Observable) this.cmsEditable).addObserver(this); - refresh(); - } - - public void setLayoutData(Object layoutData) { - this.layoutData = layoutData; - if (display != null && !display.isDisposed()) - display.setLayoutData(layoutData); - } - - protected void refresh() { - if (display != null && !display.isDisposed()) - display.dispose(); - - display = new Composite(parent, SWT.NONE); - display.setLayoutData(layoutData); - - CmsSwtUtils.style(display, FormStyle.header.style()); - display.setBackgroundMode(SWT.INHERIT_FORCE); - - display.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - publishBtn = createSimpleBtn(display, getPublishButtonLabel()); - display.moveAbove(null); - parent.layout(); - } - - private Button createSimpleBtn(Composite parent, String label) { - Button button = new Button(parent, SWT.FLAT | SWT.PUSH); - button.setText(label); - CmsSwtUtils.style(button, FormStyle.header.style()); - button.addSelectionListener(this); - return button; - } - - private String getPublishButtonLabel() { - // Rather check if the current node differs from what has been - // previously committed - // For the time being, we always reach here, the underlying CmsEditable - // is always editing. - if (cmsEditable.isEditing()) - return " Publish "; - else - return " Edit "; - } - - @Override - public void widgetSelected(SelectionEvent e) { - if (e.getSource() == publishBtn) { - // For the time being, the underlying CmsEditable - // is always editing when we reach this point - if (cmsEditable.isEditing()) { - // we always leave the node in a check outed state - cmsEditable.stopEditing(); - cmsEditable.startEditing(); - } else { - cmsEditable.startEditing(); - } - } - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - - @Override - public void update(Observable o, Object arg) { - if (o == cmsEditable) { - refresh(); - } - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java deleted file mode 100644 index 1888055fc..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormPageViewer.java +++ /dev/null @@ -1,608 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.io.IOException; -import java.io.InputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.Value; -import javax.jcr.ValueFormatException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.api.cms.ux.CmsEditable; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.viewers.AbstractPageViewer; -import org.argeo.cms.ui.viewers.Section; -import org.argeo.cms.ui.viewers.SectionPart; -import org.argeo.cms.ui.widgets.EditableImage; -import org.argeo.cms.ui.widgets.Img; -import org.argeo.cms.ui.widgets.StyledControl; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.rap.fileupload.FileDetails; -import org.eclipse.rap.fileupload.FileUploadEvent; -import org.eclipse.rap.fileupload.FileUploadHandler; -import org.eclipse.rap.fileupload.FileUploadListener; -import org.eclipse.rap.fileupload.FileUploadReceiver; -import org.eclipse.rap.rwt.service.ServerPushSession; -import org.eclipse.rap.rwt.widgets.FileUpload; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.FormAttachment; -import org.eclipse.swt.layout.FormData; -import org.eclipse.swt.layout.FormLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -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; - -/** Manage life cycle of a form page that is linked to a given node */ -public class FormPageViewer extends AbstractPageViewer { - private final static CmsLog log = CmsLog.getLog(FormPageViewer.class); - private static final long serialVersionUID = 5277789504209413500L; - - private final Section mainSection; - - // TODO manage within the CSS - private Integer labelColWidth = null; - private int rowLayoutHSpacing = 8; - - // Context cached in the viewer - // The reference to translate from text to calendar and reverse - private DateFormat dateFormat = new SimpleDateFormat(FormUtils.DEFAULT_SHORT_DATE_FORMAT); - private CmsImageManager imageManager; - private FileUploadListener fileUploadListener; - - public FormPageViewer(Section mainSection, int style, CmsEditable cmsEditable) throws RepositoryException { - super(mainSection, style, cmsEditable); - this.mainSection = mainSection; - - if (getCmsEditable().canEdit()) { - fileUploadListener = new FUL(); - } - } - - @Override - protected void prepare(SwtEditablePart part, Object caretPosition) { - if (part instanceof Img) { - ((Img) part).setFileUploadListener(fileUploadListener); - } - } - - /** To be overridden.Save the edited part. */ - protected void save(SwtEditablePart part) throws RepositoryException { - Node node = null; - if (part instanceof EditableMultiStringProperty) { - EditableMultiStringProperty ept = (EditableMultiStringProperty) part; - // SWT : View - List values = ept.getValues(); - // JCR : Model - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (values.isEmpty()) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - node.setProperty(propName, values.toArray(new String[0])); - } - // => Viewer : Controller - } else if (part instanceof EditablePropertyString) { - EditablePropertyString ept = (EditablePropertyString) part; - // SWT : View - String txt = ((Text) ept.getControl()).getText(); - // JCR : Model - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (EclipseUiUtils.isEmpty(txt)) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - setPropertySilently(node, propName, txt); - // node.setProperty(propName, txt); - } - // node.getSession().save(); - // => Viewer : Controller - } else if (part instanceof EditablePropertyDate) { - EditablePropertyDate ept = (EditablePropertyDate) part; - Calendar cal = FormUtils.parseDate(dateFormat, ((Text) ept.getControl()).getText()); - node = ept.getNode(); - String propName = ept.getPropertyName(); - if (cal == null) { - if (node.hasProperty(propName)) - node.getProperty(propName).remove(); - } else { - node.setProperty(propName, cal); - } - // node.getSession().save(); - // => Viewer : Controller - } - // TODO: make this configurable, sometimes we do not want to save the - // current session at this stage - if (node != null && node.getSession().hasPendingChanges()) { - JcrUtils.updateLastModified(node, true); - node.getSession().save(); - } - } - - @Override - protected void updateContent(SwtEditablePart part) throws RepositoryException { - if (part instanceof EditableMultiStringProperty) { - EditableMultiStringProperty ept = (EditableMultiStringProperty) part; - // SWT : View - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - List valStrings = new ArrayList(); - if (node.hasProperty(propName)) { - Value[] values = node.getProperty(propName).getValues(); - for (Value val : values) - valStrings.add(val.getString()); - } - ept.setValues(valStrings); - } else if (part instanceof EditablePropertyString) { - // || part instanceof EditableLink - EditablePropertyString ept = (EditablePropertyString) part; - // JCR : Model - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - if (node.hasProperty(propName)) { - String value = node.getProperty(propName).getString(); - ept.setText(value); - } else - ept.setText(""); - // => Viewer : Controller - } else if (part instanceof EditablePropertyDate) { - EditablePropertyDate ept = (EditablePropertyDate) part; - // JCR : Model - Node node = ept.getNode(); - String propName = ept.getPropertyName(); - if (node.hasProperty(propName)) - ept.setText(dateFormat.format(node.getProperty(propName).getDate().getTime())); - else - ept.setText(""); - } else if (part instanceof SectionPart) { - SectionPart sectionPart = (SectionPart) part; - Node partNode = sectionPart.getNode(); - // use control AFTER setting style, since it may have been reset - if (part instanceof EditableImage) { - EditableImage editableImage = (EditableImage) part; - imageManager().load(partNode, part.getControl(), editableImage.getPreferredImageSize()); - } - } - } - - // FILE UPLOAD LISTENER - protected class FUL implements FileUploadListener { - - public FUL() { - } - - public void uploadProgress(FileUploadEvent event) { - // TODO Monitor upload progress - } - - public void uploadFailed(FileUploadEvent event) { - throw new IllegalStateException("Upload failed " + event, event.getException()); - } - - public void uploadFinished(FileUploadEvent event) { - for (FileDetails file : event.getFileDetails()) { - if (log.isDebugEnabled()) - log.debug("Received: " + file.getFileName()); - } - mainSection.getDisplay().syncExec(new Runnable() { - @Override - public void run() { - saveEdit(); - } - }); - FileUploadHandler uploadHandler = (FileUploadHandler) event.getSource(); - uploadHandler.dispose(); - } - } - - // FOCUS OUT LISTENER - protected FocusListener createFocusListener() { - return new FocusOutListener(); - } - - private class FocusOutListener implements FocusListener { - private static final long serialVersionUID = -6069205786732354186L; - - @Override - public void focusLost(FocusEvent event) { - saveEdit(); - } - - @Override - public void focusGained(FocusEvent event) { - // does nothing; - } - } - - // MOUSE LISTENER - @Override - protected MouseListener createMouseListener() { - return new ML(); - } - - private class ML extends MouseAdapter { - private static final long serialVersionUID = 8526890859876770905L; - - @Override - public void mouseDoubleClick(MouseEvent e) { - if (e.button == 1) { - Control source = (Control) e.getSource(); - if (getCmsEditable().canEdit()) { - if (getCmsEditable().isEditing() && !(getEdited() instanceof Img)) { - if (source == mainSection) - return; - SwtEditablePart part = findDataParent(source); - upload(part); - } else { - getCmsEditable().startEditing(); - } - } - } - } - - @Override - public void mouseDown(MouseEvent e) { - if (getCmsEditable().isEditing()) { - if (e.button == 1) { - Control source = (Control) e.getSource(); - SwtEditablePart composite = findDataParent(source); - Point point = new Point(e.x, e.y); - if (!(composite instanceof Img)) - edit(composite, source.toDisplay(point)); - } else if (e.button == 3) { - // EditablePart composite = findDataParent((Control) e - // .getSource()); - // if (styledTools != null) - // styledTools.show(composite, new Point(e.x, e.y)); - } - } - } - - protected synchronized void upload(SwtEditablePart part) { - if (part instanceof SectionPart) { - if (part instanceof Img) { - if (getEdited() == part) - return; - edit(part, null); - layout(part.getControl()); - } - } - } - } - - @Override - public Control getControl() { - return mainSection; - } - - protected CmsImageManager imageManager() { - if (imageManager == null) - imageManager = (CmsImageManager) CmsSwtUtils.getCmsView(mainSection).getImageManager(); - return imageManager; - } - - // LOCAL UI HELPERS - protected Section createSectionIfNeeded(Composite body, Node node) throws RepositoryException { - Section section = null; - if (node != null) { - section = new Section(body, SWT.NO_FOCUS, node); - section.setLayoutData(CmsSwtUtils.fillWidth()); - section.setLayout(CmsSwtUtils.noSpaceGridLayout()); - } - return section; - } - - protected void createSimpleLT(Composite bodyRow, Node node, String propName, String label, String msg) - throws RepositoryException { - if (getCmsEditable().canEdit() || node.hasProperty(propName)) { - createPropertyLbl(bodyRow, label); - EditablePropertyString eps = new EditablePropertyString(bodyRow, SWT.WRAP | SWT.LEFT, node, propName, msg); - eps.setMouseListener(getMouseListener()); - eps.setFocusListener(getFocusListener()); - eps.setLayoutData(CmsSwtUtils.fillWidth()); - } - } - - protected void createMultiStringLT(Composite bodyRow, Node node, String propName, String label, String msg) - throws RepositoryException { - boolean canEdit = getCmsEditable().canEdit(); - if (canEdit || node.hasProperty(propName)) { - createPropertyLbl(bodyRow, label); - - List valueStrings = new ArrayList(); - - if (node.hasProperty(propName)) { - Value[] values = node.getProperty(propName).getValues(); - for (Value value : values) - valueStrings.add(value.getString()); - } - - // TODO use a drop down to display possible values to the end user - EditableMultiStringProperty emsp = new EditableMultiStringProperty(bodyRow, SWT.SINGLE | SWT.LEAD, node, - propName, valueStrings, new String[] { "Implement this" }, msg, - canEdit ? getRemoveValueSelListener() : null); - addListeners(emsp); - // emsp.setMouseListener(getMouseListener()); - emsp.setStyle(FormStyle.propertyMessage.style()); - emsp.setLayoutData(CmsSwtUtils.fillWidth()); - } - } - - protected Label createPropertyLbl(Composite parent, String value) { - return createPropertyLbl(parent, value, SWT.NONE); - } - - protected Label createPropertyLbl(Composite parent, String value, int vAlign) { - // boolean isSmall = CmsView.getCmsView(parent).getUxContext().isSmall(); - Label label = new Label(parent, SWT.LEAD | SWT.WRAP); - label.setText(value + " "); - CmsSwtUtils.style(label, FormStyle.propertyLabel.style()); - GridData gd = new GridData(SWT.LEAD, vAlign, false, false); - if (labelColWidth != null) - gd.widthHint = labelColWidth; - label.setLayoutData(gd); - return label; - } - - protected Label newStyledLabel(Composite parent, String style, String value) { - Label label = new Label(parent, SWT.NONE); - label.setText(value); - CmsSwtUtils.style(label, style); - return label; - } - - protected Composite createRowLayoutComposite(Composite parent) throws RepositoryException { - Composite bodyRow = new Composite(parent, SWT.NO_FOCUS); - bodyRow.setLayoutData(CmsSwtUtils.fillWidth()); - RowLayout rl = new RowLayout(SWT.WRAP); - rl.type = SWT.HORIZONTAL; - rl.spacing = rowLayoutHSpacing; - rl.marginHeight = rl.marginWidth = 0; - rl.marginTop = rl.marginBottom = rl.marginLeft = rl.marginRight = 0; - bodyRow.setLayout(rl); - return bodyRow; - } - - protected Composite createAddImgComposite(final Section section, Composite parent, final Node parentNode) - throws RepositoryException { - - Composite body = new Composite(parent, SWT.NO_FOCUS); - body.setLayout(new GridLayout()); - - FormFileUploadReceiver receiver = new FormFileUploadReceiver(section, parentNode, null); - final FileUploadHandler currentUploadHandler = new FileUploadHandler(receiver); - if (fileUploadListener != null) - currentUploadHandler.addUploadListener(fileUploadListener); - - // Button creation - final FileUpload fileUpload = new FileUpload(body, SWT.BORDER); - fileUpload.setText("Import an image"); - fileUpload.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - fileUpload.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 4869523412991968759L; - - @Override - public void widgetSelected(SelectionEvent e) { - ServerPushSession pushSession = new ServerPushSession(); - pushSession.start(); - String uploadURL = currentUploadHandler.getUploadUrl(); - fileUpload.submit(uploadURL); - } - }); - - return body; - } - - protected class FormFileUploadReceiver extends FileUploadReceiver { - - private Node context; - private Section section; - private String name; - - public FormFileUploadReceiver(Section section, Node context, String name) { - this.context = context; - this.section = section; - this.name = name; - } - - @Override - public void receive(InputStream stream, FileDetails details) throws IOException { - - if (name == null) - name = details.getFileName(); - - // TODO clean image name more carefully - String cleanedName = name.replaceAll("[^a-zA-Z0-9-.]", ""); - // We add a unique prefix to workaround the cache issue: when - // deleting and re-adding a new image with same name, the end user - // browser will use the cache and the image will remain unchanged - // for a while - cleanedName = System.currentTimeMillis() % 100000 + "_" + cleanedName; - - imageManager().uploadImage(context, context, cleanedName, stream, details.getContentType()); - // TODO clean refresh strategy - section.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - try { - FormPageViewer.this.refresh(section); - section.layout(); - section.getParent().layout(); - } catch (RepositoryException re) { - throw new JcrException("Unable to refresh " + "image section for " + context, re); - } - } - }); - } - } - - protected void addListeners(StyledControl control) { - control.setMouseListener(getMouseListener()); - control.setFocusListener(getFocusListener()); - } - - protected Img createImgComposite(Composite parent, Node node, Point preferredSize) throws RepositoryException { - Img img = new Img(parent, SWT.NONE, node, new Cms2DSize(preferredSize.x, preferredSize.y)) { - private static final long serialVersionUID = 1297900641952417540L; - - @Override - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - } - - @Override - protected void setControlLayoutData(Control control) { - control.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - } - }; - img.setLayoutData(CmsSwtUtils.grabWidth(SWT.CENTER, SWT.DEFAULT)); - updateContent(img); - addListeners(img); - return img; - } - - protected Composite addDeleteAbility(final Section section, final Node sessionNode, int topWeight, - int rightWeight) { - Composite comp = new Composite(section, SWT.NONE); - comp.setLayoutData(CmsSwtUtils.fillAll()); - comp.setLayout(new FormLayout()); - - // The body to be populated - Composite body = new Composite(comp, SWT.NO_FOCUS); - body.setLayoutData(EclipseUiUtils.fillFormData()); - - if (getCmsEditable().canEdit()) { - // the delete button - Button deleteBtn = new Button(comp, SWT.FLAT); - CmsSwtUtils.style(deleteBtn, FormStyle.deleteOverlay.style()); - FormData formData = new FormData(); - formData.right = new FormAttachment(rightWeight, 0); - formData.top = new FormAttachment(topWeight, 0); - deleteBtn.setLayoutData(formData); - deleteBtn.moveAbove(body); - - deleteBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = 4304223543657238462L; - - @Override - public void widgetSelected(SelectionEvent e) { - super.widgetSelected(e); - if (MessageDialog.openConfirm(section.getShell(), "Confirm deletion", - "Are you really you want to remove this?")) { - Session session; - try { - session = sessionNode.getSession(); - Section parSection = section.getParentSection(); - sessionNode.remove(); - session.save(); - refresh(parSection); - layout(parSection); - } catch (RepositoryException re) { - throw new JcrException("Unable to delete " + sessionNode, re); - } - - } - - } - }); - } - return body; - } - -// // LOCAL HELPERS FOR NODE MANAGEMENT -// private Node getOrCreateNode(Node parent, String nodeName, String nodeType) throws RepositoryException { -// Node node = null; -// if (getCmsEditable().canEdit() && !parent.hasNode(nodeName)) { -// node = JcrUtils.mkdirs(parent, nodeName, nodeType); -// parent.getSession().save(); -// } -// -// if (getCmsEditable().canEdit() || parent.hasNode(nodeName)) -// node = parent.getNode(nodeName); -// -// return node; -// } - - private SelectionListener getRemoveValueSelListener() { - return new SelectionAdapter() { - private static final long serialVersionUID = 9022259089907445195L; - - @Override - public void widgetSelected(SelectionEvent e) { - Object source = e.getSource(); - if (source instanceof Button) { - Button btn = (Button) source; - Object obj = btn.getData(FormConstants.LINKED_VALUE); - SwtEditablePart ep = findDataParent(btn); - if (ep != null && ep instanceof EditableMultiStringProperty) { - EditableMultiStringProperty emsp = (EditableMultiStringProperty) ep; - List values = emsp.getValues(); - if (values.contains(obj)) { - values.remove(values.indexOf(obj)); - emsp.setValues(values); - try { - save(emsp); - // TODO workaround to force refresh - edit(emsp, 0); - cancelEdit(); - } catch (RepositoryException e1) { - throw new JcrException("Unable to remove value " + obj, e1); - } - layout(emsp); - } - } - } - } - }; - } - - protected void setPropertySilently(Node node, String propName, String value) throws RepositoryException { - try { - // TODO Clean this: - // Format strings to replace \n - value = value.replaceAll("\n", "
"); - // Do not make the update if validation fails - try { - MarkupValidatorCopy.getInstance().validate(value); - } catch (Exception e) { - log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node - + ", String cannot be validated - " + e.getMessage()); - return; - } - // TODO check if the newly created property is of the correct type, - // otherwise the property will be silently created with a STRING - // property type. - node.setProperty(propName, value); - } catch (ValueFormatException vfe) { - log.warn("Cannot set [" + value + "] on prop " + propName + "of " + node + " - " + vfe.getMessage()); - } - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormStyle.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormStyle.java deleted file mode 100644 index 709ecd024..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormStyle.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.argeo.cms.ui.forms; - -import org.argeo.api.cms.ux.CmsStyle; - -/** Syles used */ -public enum FormStyle implements CmsStyle { - // Main - form, title, - // main part - header, headerBtn, headerCombo, section, sectionHeader, - // Property fields - propertyLabel, propertyText, propertyMessage, errorMessage, - // Date - popupCalendar, - // Buttons - starred, unstarred, starOverlay, editOverlay, deleteOverlay, updateOverlay, deleteOverlaySmall, calendar, delete, - // Contacts - email, address, phone, website, - // Social Media - facebook, twitter, linkedIn, instagram; - - @Override - public String getClassPrefix() { - return "argeo-form"; - } - - // TODO clean button style management - public final static String BUTTON_SUFFIX = "_btn"; -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormUtils.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormUtils.java deleted file mode 100644 index ef49c8ae5..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/FormUtils.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.text.DateFormat; -import java.text.ParseException; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.eclipse.jface.fieldassist.ControlDecoration; -import org.eclipse.jface.fieldassist.FieldDecorationRegistry; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Utilitary methods to ease implementation of CMS forms */ -public class FormUtils { - private final static CmsLog log = CmsLog.getLog(FormUtils.class); - - public final static String DEFAULT_SHORT_DATE_FORMAT = "dd/MM/yyyy"; - - /** Best effort to convert a String to a calendar. Fails silently */ - public static Calendar parseDate(DateFormat dateFormat, String calStr) { - Calendar cal = null; - if (EclipseUiUtils.notEmpty(calStr)) { - try { - Date date = dateFormat.parse(calStr); - cal = new GregorianCalendar(); - cal.setTime(date); - } catch (ParseException pe) { - // Silent - log.warn("Unable to parse date: " + calStr + " - msg: " - + pe.getMessage()); - } - } - return cal; - } - - /** Add a double click listener on tables that display a JCR node list */ - public static void addCanonicalDoubleClickListener(final TableViewer v) { - v.addDoubleClickListener(new IDoubleClickListener() { - - @Override - public void doubleClick(DoubleClickEvent event) { - CmsView cmsView = CmsUiUtils.getCmsView(); - Node node = (Node) ((IStructuredSelection) event.getSelection()) - .getFirstElement(); - try { - cmsView.navigateTo(node.getPath()); - } catch (RepositoryException e) { - throw new CmsException("Unable to get path for node " - + node + " before calling navigateTo(path)", e); - } - } - }); - } - - // MANAGE ERROR DECORATION - - public static ControlDecoration addDecoration(final Text text) { - final ControlDecoration dynDecoration = new ControlDecoration(text, - SWT.LEFT); - Image icon = getDecorationImage(FieldDecorationRegistry.DEC_ERROR); - dynDecoration.setImage(icon); - dynDecoration.setMarginWidth(3); - dynDecoration.hide(); - return dynDecoration; - } - - public static void refreshDecoration(Text text, ControlDecoration deco, - boolean isValid, boolean clean) { - if (isValid || clean) { - text.setBackground(null); - deco.hide(); - } else { - text.setBackground(new Color(text.getDisplay(), 250, 200, 150)); - deco.show(); - } - } - - public static Image getDecorationImage(String image) { - FieldDecorationRegistry registry = FieldDecorationRegistry.getDefault(); - return registry.getFieldDecoration(image).getImage(); - } - - public static void addCompulsoryDecoration(Label label) { - final ControlDecoration dynDecoration = new ControlDecoration(label, - SWT.RIGHT | SWT.TOP); - Image icon = getDecorationImage(FieldDecorationRegistry.DEC_REQUIRED); - dynDecoration.setImage(icon); - dynDecoration.setMarginWidth(3); - } - - // TODO the read only generation of read only links for various contact type - // should be factorised in the cms Utils. - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able phone number - */ - public static String getPhoneLink(String value) { - return getPhoneLink(value, value); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able phone number - * - * @param value - * @param label - * a potentially distinct label - * @return - */ - public static String getPhoneLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - builder.append("").append(label) - .append(""); - return builder.toString(); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able mail - */ - public static String getMailLink(String value) { - return getMailLink(value, value); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able mail - * - * @param value - * @param label - * a potentially distinct label - * @return - */ - public static String getMailLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - value = replaceAmpersand(value); - builder.append("").append(label).append(""); - return builder.toString(); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able link - */ - public static String getUrlLink(String value) { - return getUrlLink(value, value); - } - - /** - * Creates the read-only HTML snippet to display in a label with styling - * enabled in order to provide a click-able link - */ - public static String getUrlLink(String value, String label) { - StringBuilder builder = new StringBuilder(); - value = replaceAmpersand(value); - label = replaceAmpersand(label); - if (!(value.startsWith("http://") || value.startsWith("https://"))) - value = "http://" + value; - builder.append("" + label + ""); - return builder.toString(); - } - - private static String AMPERSAND = "&"; - - /** - * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to - * avoid SAXParseException while rendering HTML with RWT - */ - public static String replaceAmpersand(String value) { - value = value.replaceAll("&(?![#a-zA-Z0-9]+;)", AMPERSAND); - return value; - } - - // Prevents instantiation - private FormUtils() { - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java deleted file mode 100644 index 3f588d1ea..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/MarkupValidatorCopy.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.argeo.cms.ui.forms; - -import java.io.StringReader; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.eclipse.rap.rwt.SingletonUtil; -import org.eclipse.swt.widgets.Widget; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.helpers.DefaultHandler; - -/** - * Copy of RAP v2.3 since it is in an internal package. - */ -class MarkupValidatorCopy { - - // Used by Eclipse Scout project - public static final String MARKUP_VALIDATION_DISABLED = "org.eclipse.rap.rwt.markupValidationDisabled"; - - private static final String DTD = createDTD(); - private static final Map SUPPORTED_ELEMENTS = createSupportedElementsMap(); - private final SAXParser saxParser; - - public static MarkupValidatorCopy getInstance() { - return SingletonUtil.getSessionInstance(MarkupValidatorCopy.class); - } - - public MarkupValidatorCopy() { - saxParser = createSAXParser(); - } - - public void validate(String text) { - StringBuilder markup = new StringBuilder(); - markup.append(DTD); - markup.append(""); - markup.append(text); - markup.append(""); - InputSource inputSource = new InputSource(new StringReader(markup.toString())); - try { - saxParser.parse(inputSource, new MarkupHandler()); - } catch (RuntimeException exception) { - throw exception; - } catch (Exception exception) { - throw new IllegalArgumentException("Failed to parse markup text", exception); - } - } - - public static boolean isValidationDisabledFor(Widget widget) { - return Boolean.TRUE.equals(widget.getData(MARKUP_VALIDATION_DISABLED)); - } - - private static SAXParser createSAXParser() { - SAXParser result = null; - SAXParserFactory parserFactory = SAXParserFactory.newInstance(); - try { - result = parserFactory.newSAXParser(); - } catch (Exception exception) { - throw new RuntimeException("Failed to create SAX parser", exception); - } - return result; - } - - private static String createDTD() { - StringBuilder result = new StringBuilder(); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append(""); - result.append("]>"); - return result.toString(); - } - - private static Map createSupportedElementsMap() { - Map result = new HashMap(); - result.put("html", new String[0]); - result.put("br", new String[0]); - result.put("b", new String[] { "style" }); - result.put("strong", new String[] { "style" }); - result.put("i", new String[] { "style" }); - result.put("em", new String[] { "style" }); - result.put("sub", new String[] { "style" }); - result.put("sup", new String[] { "style" }); - result.put("big", new String[] { "style" }); - result.put("small", new String[] { "style" }); - result.put("del", new String[] { "style" }); - result.put("ins", new String[] { "style" }); - result.put("code", new String[] { "style" }); - result.put("samp", new String[] { "style" }); - result.put("kbd", new String[] { "style" }); - result.put("var", new String[] { "style" }); - result.put("cite", new String[] { "style" }); - result.put("dfn", new String[] { "style" }); - result.put("q", new String[] { "style" }); - result.put("abbr", new String[] { "style", "title" }); - result.put("span", new String[] { "style" }); - result.put("img", new String[] { "style", "src", "width", "height", "title", "alt" }); - result.put("a", new String[] { "style", "href", "target", "title" }); - return result; - } - - private static class MarkupHandler extends DefaultHandler { - - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) { - checkSupportedElements(name, attributes); - checkSupportedAttributes(name, attributes); - checkMandatoryAttributes(name, attributes); - } - - private static void checkSupportedElements(String elementName, Attributes attributes) { - if (!SUPPORTED_ELEMENTS.containsKey(elementName)) { - throw new IllegalArgumentException("Unsupported element in markup text: " + elementName); - } - } - - private static void checkSupportedAttributes(String elementName, Attributes attributes) { - if (attributes.getLength() > 0) { - List supportedAttributes = Arrays.asList(SUPPORTED_ELEMENTS.get(elementName)); - int index = 0; - String attributeName = attributes.getQName(index); - while (attributeName != null) { - if (!supportedAttributes.contains(attributeName)) { - String message = "Unsupported attribute \"{0}\" for element \"{1}\" in markup text"; - message = MessageFormat.format(message, new Object[] { attributeName, elementName }); - throw new IllegalArgumentException(message); - } - index++; - attributeName = attributes.getQName(index); - } - } - } - - private static void checkMandatoryAttributes(String elementName, Attributes attributes) { - checkIntAttribute(elementName, attributes, "img", "width"); - checkIntAttribute(elementName, attributes, "img", "height"); - } - - private static void checkIntAttribute(String elementName, Attributes attributes, String checkedElementName, - String checkedAttributeName) { - if (checkedElementName.equals(elementName)) { - String attribute = attributes.getValue(checkedAttributeName); - try { - Integer.parseInt(attribute); - } catch (NumberFormatException exception) { - String message = "Mandatory attribute \"{0}\" for element \"{1}\" is missing or not a valid integer"; - Object[] arguments = new Object[] { checkedAttributeName, checkedElementName }; - message = MessageFormat.format(message, arguments); - throw new IllegalArgumentException(message); - } - } - } - - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/package-info.java deleted file mode 100644 index 5f954c1c4..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/forms/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS forms, based on SWT/JFace. */ -package org.argeo.cms.ui.forms; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java deleted file mode 100644 index d9c1c1221..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java +++ /dev/null @@ -1,524 +0,0 @@ -package org.argeo.cms.ui.fs; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.DirectoryStream; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.spi.FileSystemProvider; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.Session; - -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider; -import org.argeo.eclipse.ui.fs.FsTableViewer; -import org.argeo.eclipse.ui.fs.FsUiConstants; -import org.argeo.eclipse.ui.fs.FsUiUtils; -import org.argeo.eclipse.ui.fs.NioFileLabelProvider; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowData; -import org.eclipse.swt.layout.RowLayout; -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.Table; -import org.eclipse.swt.widgets.Text; - -/** - * Default CMS browser composite: a sashForm layout with bookmarks at the left - * hand side, a simple table in the middle and an overview at right hand side. - */ -public class CmsFsBrowser extends Composite { - // private final static Log log = LogFactory.getLog(CmsFsBrowser.class); - private static final long serialVersionUID = -40347919096946585L; - - private final FileSystemProvider nodeFileSystemProvider; - private final Node currentBaseContext; - - // UI Parts for the browser - private Composite leftPannelCmp; - private Composite filterCmp; - private Text filterTxt; - private FsTableViewer directoryDisplayViewer; - private Composite rightPannelCmp; - - private FsContextMenu contextMenu; - - // Local context (this composite is state full) - private Path initialPath; - private Path currDisplayedFolder; - private Path currSelected; - - // local variables (to be cleaned) - private int bookmarkColWith = 500; - - /* - * WARNING: unfinalised implementation of the mechanism to retrieve base - * paths - */ - - private final static String NODE_PREFIX = "node://"; - - private String getCurrentHomePath() { - Session session = null; - try { - Repository repo = currentBaseContext.getSession().getRepository(); - session = CurrentUser.tryAs(() -> repo.login()); - String homepath = CmsJcrUtils.getUserHome(session).getPath(); - return homepath; - } catch (Exception e) { - throw new CmsException("Cannot retrieve Current User Home Path", e); - } finally { - JcrUtils.logoutQuietly(session); - } - } - - protected Path[] getMyFilesPath() { - // return Paths.get(System.getProperty("user.dir")); - String currHomeUriStr = NODE_PREFIX + getCurrentHomePath(); - try { - URI uri = new URI(currHomeUriStr); - FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); - if (fileSystem == null) { - PrivilegedExceptionAction pea = new PrivilegedExceptionAction() { - @Override - public FileSystem run() throws Exception { - return nodeFileSystemProvider.newFileSystem(uri, null); - } - - }; - fileSystem = CurrentUser.tryAs(pea); - } - Path[] paths = { fileSystem.getPath(getCurrentHomePath()), fileSystem.getPath("/") }; - return paths; - } catch (URISyntaxException | PrivilegedActionException e) { - throw new RuntimeException("unable to initialise home file system for " + currHomeUriStr, e); - } - } - - private Path[] getMyGroupsFilesPath() { - // TODO - Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp") }; - return paths; - } - - private Path[] getMyBookmarks() { - // TODO - Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp"), Paths.get("/opt") }; - return paths; - } - - /* End of warning */ - - public CmsFsBrowser(Composite parent, int style, Node context, FileSystemProvider fileSystemProvider) { - super(parent, style); - this.nodeFileSystemProvider = fileSystemProvider; - this.currentBaseContext = context; - - this.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - SashForm form = new SashForm(this, SWT.HORIZONTAL); - - leftPannelCmp = new Composite(form, SWT.NO_FOCUS); - // Bookmarks are still static - populateBookmarks(leftPannelCmp); - - Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS); - createDisplay(centerCmp); - - rightPannelCmp = new Composite(form, SWT.NO_FOCUS); - - form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - form.setWeights(new int[] { 15, 40, 20 }); - } - - void refresh() { - modifyFilter(false); - // also refresh bookmarks and groups - } - - private void createDisplay(final Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // top filter - filterCmp = new Composite(parent, SWT.NO_FOCUS); - filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); - addFilterPanel(filterCmp); - - // Main display - directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); - List colDefs = new ArrayList<>(); - colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 250)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), - "Last modified", 400)); - final Table table = directoryDisplayViewer.configureDefaultTable(colDefs); - table.setLayoutData(EclipseUiUtils.fillAll()); - - // table.addKeyListener(new KeyListener() { - // private static final long serialVersionUID = -8083424284436715709L; - // - // @Override - // public void keyReleased(KeyEvent e) { - // } - // - // @Override - // public void keyPressed(KeyEvent e) { - // if (log.isDebugEnabled()) - // log.debug("Key event received: " + e.keyCode); - // IStructuredSelection selection = (IStructuredSelection) - // directoryDisplayViewer.getSelection(); - // Path selected = null; - // if (!selection.isEmpty()) - // selected = ((Path) selection.getFirstElement()); - // if (e.keyCode == SWT.CR) { - // if (!Files.isDirectory(selected)) - // return; - // if (selected != null) { - // currDisplayedFolder = selected; - // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); - // } - // } else if (e.keyCode == SWT.BS) { - // currDisplayedFolder = currDisplayedFolder.getParent(); - // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); - // directoryDisplayViewer.getTable().setFocus(); - // } - // } - // }); - - directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (selection.isEmpty()) - setSelected(null); - else - selected = ((Path) selection.getFirstElement()); - if (selected != null) { - // TODO manage multiple selection - setSelected(selected); - } - } - }); - - directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { - @Override - public void doubleClick(DoubleClickEvent event) { - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (!selection.isEmpty()) - selected = ((Path) selection.getFirstElement()); - if (selected != null) { - if (!Files.isDirectory(selected)) - return; - setInput(selected); - } - } - }); - - // The context menu - contextMenu = new FsContextMenu(this); - - table.addMouseListener(new MouseAdapter() { - private static final long serialVersionUID = 6737579410648595940L; - - @Override - public void mouseDown(MouseEvent e) { - if (e.button == 3) { - // contextMenu.setCurrFolderPath(currDisplayedFolder); - contextMenu.show(table, new Point(e.x, e.y), currDisplayedFolder); - } - } - }); - } - - private void addPathElementBtn(Path path) { - Button elemBtn = new Button(filterCmp, SWT.PUSH); - String nameStr; - if (path.toString().equals("/")) - nameStr = "[jcr:root]"; - else - nameStr = path.getFileName().toString(); - elemBtn.setText(nameStr + " >> "); - CmsSwtUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); - elemBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -4103695476023480651L; - - @Override - public void widgetSelected(SelectionEvent e) { - setInput(path); - } - }); - } - - public void setInput(Path path) { - if (path.equals(currDisplayedFolder)) - return; - currDisplayedFolder = path; - - Path diff = initialPath.relativize(currDisplayedFolder); - - for (Control child : filterCmp.getChildren()) - if (!child.equals(filterTxt)) - child.dispose(); - - addPathElementBtn(initialPath); - Path currTarget = initialPath; - if (!diff.toString().equals("")) - for (Path pathElem : diff) { - currTarget = currTarget.resolve(pathElem); - addPathElementBtn(currTarget); - } - - filterTxt.setText(""); - filterTxt.moveBelow(null); - setSelected(null); - filterCmp.getParent().layout(true, true); - } - - private void setSelected(Path path) { - currSelected = path; - setOverviewInput(path); - } - - public Viewer getViewer() { - return directoryDisplayViewer; - } - - private void populateBookmarks(Composite parent) { - CmsSwtUtils.clear(parent); - parent.setLayout(new GridLayout()); - ISelectionChangedListener selList = new BookmarksSelChangeListener(); - - FsTableViewer homeViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - Table table = homeViewer.configureDefaultSingleColumnTable(bookmarkColWith); - GridData gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - homeViewer.addSelectionChangedListener(selList); - homeViewer.setPathsInput(getMyFilesPath()); - - appendTitle(parent, "Shared files"); - FsTableViewer groupsViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - table = groupsViewer.configureDefaultSingleColumnTable(bookmarkColWith); - gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - groupsViewer.addSelectionChangedListener(selList); - groupsViewer.setPathsInput(getMyGroupsFilesPath()); - - appendTitle(parent, "My bookmarks"); - FsTableViewer bookmarksViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - table = bookmarksViewer.configureDefaultSingleColumnTable(bookmarkColWith); - gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - bookmarksViewer.addSelectionChangedListener(selList); - bookmarksViewer.setPathsInput(getMyBookmarks()); - } - - /** - * Recreates the content of the box that displays information about the - * current selected Path. - */ - private void setOverviewInput(Path path) { - try { - EclipseUiUtils.clear(rightPannelCmp); - rightPannelCmp.setLayout(new GridLayout()); - if (path != null) { - // if (isImg(context)) { - // EditableImage image = new Img(parent, RIGHT, context, - // imageWidth); - // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, - // true, false, - // 2, 1)); - // } - - Label contextL = new Label(rightPannelCmp, SWT.NONE); - contextL.setText(path.getFileName().toString()); - contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp)); - addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString()); - // addProperty(rightPannelCmp, "Owner", - // Files.getOwner(path).getName()); - if (Files.isDirectory(path)) { - addProperty(rightPannelCmp, "Type", "Folder"); - } else { - String mimeType = Files.probeContentType(path); - if (EclipseUiUtils.isEmpty(mimeType)) - mimeType = "Unknown"; - addProperty(rightPannelCmp, "Type", mimeType); - addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); - } - } - rightPannelCmp.layout(true, true); - } catch (IOException e) { - throw new CmsException("Cannot display details for " + path.toString(), e); - } - } - - private void addFilterPanel(Composite parent) { - RowLayout rl = new RowLayout(SWT.HORIZONTAL); - rl.wrap = true; - parent.setLayout(rl); - // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, - // false))); - - filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); - filterTxt.setMessage("Search current folder"); - filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT)); - filterTxt.addModifyListener(new ModifyListener() { - private static final long serialVersionUID = 1L; - - public void modifyText(ModifyEvent event) { - modifyFilter(false); - } - }); - filterTxt.addKeyListener(new KeyListener() { - private static final long serialVersionUID = 2533535233583035527L; - - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - // boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; - // // boolean altPressed = (e.stateMask & SWT.ALT) != 0; - // FilterEntitiesVirtualTable currTable = null; - // if (currEdited != null) { - // FilterEntitiesVirtualTable table = - // browserCols.get(currEdited); - // if (table != null && !table.isDisposed()) - // currTable = table; - // } - // - // if (e.keyCode == SWT.ARROW_DOWN) - // currTable.setFocus(); - // else if (e.keyCode == SWT.BS) { - // if (filterTxt.getText().equals("") - // && !(currEdited.getNameCount() == 1 || - // currEdited.equals(initialPath))) { - // Path oldEdited = currEdited; - // Path parentPath = currEdited.getParent(); - // setEdited(parentPath); - // if (browserCols.containsKey(parentPath)) - // browserCols.get(parentPath).setSelected(oldEdited); - // filterTxt.setFocus(); - // e.doit = false; - // } - // } else if (e.keyCode == SWT.TAB && !shiftPressed) { - // Path uniqueChild = getOnlyChild(currEdited, - // filterTxt.getText()); - // if (uniqueChild != null) { - // // Highlight the unique chosen child - // currTable.setSelected(uniqueChild); - // setEdited(uniqueChild); - // } - // filterTxt.setFocus(); - // e.doit = false; - // } - } - }); - } - - private Path getOnlyChild(Path parent, String filter) { - try (DirectoryStream stream = Files.newDirectoryStream(currDisplayedFolder, filter + "*")) { - Path uniqueChild = null; - boolean moreThanOne = false; - loop: for (Path entry : stream) { - if (uniqueChild == null) { - uniqueChild = entry; - } else { - moreThanOne = true; - break loop; - } - } - if (!moreThanOne) - return uniqueChild; - return null; - } catch (IOException ioe) { - throw new CmsException( - "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, - ioe); - } - } - - private void modifyFilter(boolean fromOutside) { - if (!fromOutside) - if (currDisplayedFolder != null) { - String filter = filterTxt.getText() + "*"; - directoryDisplayViewer.setInput(currDisplayedFolder, filter); - } - } - - private class BookmarksSelChangeListener implements ISelectionChangedListener { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - if (selection.isEmpty()) - return; - else { - Path newSelected = (Path) selection.getFirstElement(); - if (newSelected.equals(currDisplayedFolder) && newSelected.equals(initialPath)) - return; - initialPath = newSelected; - setInput(newSelected); - } - } - } - - // Simplify UI implementation - private void addProperty(Composite parent, String propName, String value) { - Label contextL = new Label(parent, SWT.NONE); - contextL.setText(propName + ": " + value); - } - - private Label appendTitle(Composite parent, String value) { - Label titleLbl = new Label(parent, SWT.NONE); - titleLbl.setText(value); - titleLbl.setFont(EclipseUiUtils.getBoldFont(parent)); - GridData gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 5; - gd.verticalIndent = 5; - titleLbl.setLayoutData(gd); - return titleLbl; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FileDrop.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FileDrop.java deleted file mode 100644 index e875b5a3d..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FileDrop.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.ui.fs; - -import java.io.IOException; -import java.io.InputStream; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.specific.FileDropAdapter; -import org.eclipse.swt.dnd.DND; -import org.eclipse.swt.dnd.DropTarget; -import org.eclipse.swt.dnd.DropTargetEvent; -import org.eclipse.swt.widgets.Control; - -/** Allows a control to receive file drops. */ -public class FileDrop { - private final static CmsLog log = CmsLog.getLog(FileDrop.class); - - public void createDropTarget(Control control) { - FileDropAdapter fileDropAdapter = new FileDropAdapter() { - @Override - protected void processUpload(InputStream in, String fileName, String contentType) throws IOException { - if (log.isDebugEnabled()) - log.debug("Process upload of " + fileName + " (" + contentType + ")"); - processFileUpload(in, fileName, contentType); - } - }; - DropTarget dropTarget = new DropTarget(control, DND.DROP_MOVE | DND.DROP_COPY); - fileDropAdapter.prepareDropTarget(control, dropTarget); - } - - public void handleFileDrop(Control control, DropTargetEvent event) { - } - - /** Executed in UI thread */ - protected void processFileUpload(InputStream in, String fileName, String contentType) throws IOException { - - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java deleted file mode 100644 index 1fb3c2a05..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java +++ /dev/null @@ -1,383 +0,0 @@ -package org.argeo.cms.ui.fs; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Method; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.commons.io.IOUtils; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.SingleValue; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** Generic popup context menu to manage NIO Path in a Viewer. */ -public class FsContextMenu extends Shell { - private static final long serialVersionUID = -9120261153509855795L; - - private final static CmsLog log = CmsLog.getLog(FsContextMenu.class); - - // Default known actions - public final static String ACTION_ID_CREATE_FOLDER = "createFolder"; - public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder"; - public final static String ACTION_ID_SHARE_FOLDER = "shareFolder"; - public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder"; - public final static String ACTION_ID_DELETE = "delete"; - public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles"; - public final static String ACTION_ID_OPEN = "open"; - - // Local context - private final CmsFsBrowser browser; - // private final Viewer viewer; - private final static String KEY_ACTION_ID = "actionId"; - private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER, - ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_UPLOAD_FILE, - ACTION_ID_OPEN }; - private Map actionButtons = new HashMap(); - - private Path currFolderPath; - - public FsContextMenu(CmsFsBrowser browser) { // Viewer viewer, Display - // display) { - super(browser.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - this.browser = browser; - setLayout(EclipseUiUtils.noSpaceGridLayout()); - - Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER); - boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - CmsSwtUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX); - createContextMenu(boxCmp); - - addShellListener(new ActionsShellListener()); - } - - protected void createContextMenu(Composite boxCmp) { - ActionsSelListener asl = new ActionsSelListener(); - for (String actionId : DEFAULT_ACTIONS) { - Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD); - btn.setText(getLabel(actionId)); - btn.setLayoutData(EclipseUiUtils.fillWidth()); - CmsSwtUtils.markup(btn); - CmsSwtUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX); - btn.setData(KEY_ACTION_ID, actionId); - btn.addSelectionListener(asl); - actionButtons.put(actionId, btn); - } - } - - protected String getLabel(String actionId) { - switch (actionId) { - case ACTION_ID_CREATE_FOLDER: - return "Create Folder"; - case ACTION_ID_BOOKMARK_FOLDER: - return "Bookmark Folder"; - case ACTION_ID_SHARE_FOLDER: - return "Share Folder"; - case ACTION_ID_DOWNLOAD_FOLDER: - return "Download as zip archive"; - case ACTION_ID_DELETE: - return "Delete"; - case ACTION_ID_UPLOAD_FILE: - return "Upload Files"; - case ACTION_ID_OPEN: - return "Open"; - default: - throw new IllegalArgumentException("Unknown action ID " + actionId); - } - } - - protected void aboutToShow(Control source, Point location) { - IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); - boolean emptySel = true; - boolean multiSel = false; - boolean isFolder = true; - if (selection != null && !selection.isEmpty()) { - emptySel = false; - multiSel = selection.size() > 1; - if (!multiSel && selection.getFirstElement() instanceof Path) { - isFolder = Files.isDirectory((Path) selection.getFirstElement()); - } - } - if (emptySel) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } else if (multiSel) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } else if (isFolder) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER); - } else { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_OPEN, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } - } - - private void setVisible(boolean visible, String... buttonIds) { - for (String id : buttonIds) { - Button button = actionButtons.get(id); - button.setVisible(visible); - GridData gd = (GridData) button.getLayoutData(); - gd.heightHint = visible ? SWT.DEFAULT : 0; - } - } - - public void show(Control source, Point location, Path currFolderPath) { - if (isVisible()) - setVisible(false); - // TODO find a better way to retrieve the parent path (cannot be deduced - // from table content because it will fail on an empty folder) - this.currFolderPath = currFolderPath; - aboutToShow(source, location); - pack(); - layout(); - if (source instanceof Control) - setLocation(((Control) source).toDisplay(location.x, location.y)); - open(); - } - - class StyleButton extends Label { - private static final long serialVersionUID = 7731102609123946115L; - - public StyleButton(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - - } - - // class ActionsMouseListener extends MouseAdapter { - // private static final long serialVersionUID = -1041871937815812149L; - // - // @Override - // public void mouseDown(MouseEvent e) { - // Object eventSource = e.getSource(); - // if (e.button == 1) { - // if (eventSource instanceof Button) { - // Button pressedBtn = (Button) eventSource; - // String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); - // switch (actionId) { - // case ACTION_ID_CREATE_FOLDER: - // createFolder(); - // break; - // case ACTION_ID_DELETE: - // deleteItems(); - // break; - // default: - // throw new IllegalArgumentException("Unimplemented action " + actionId); - // // case ACTION_ID_SHARE_FOLDER: - // // return "Share Folder"; - // // case ACTION_ID_DOWNLOAD_FOLDER: - // // return "Download as zip archive"; - // // case ACTION_ID_UPLOAD_FILE: - // // return "Upload Files"; - // // case ACTION_ID_OPEN: - // // return "Open"; - // } - // } - // } - // viewer.getControl().setFocus(); - // // setVisible(false); - // } - // } - - class ActionsSelListener extends SelectionAdapter { - private static final long serialVersionUID = -1041871937815812149L; - - @Override - public void widgetSelected(SelectionEvent e) { - Object eventSource = e.getSource(); - if (eventSource instanceof Button) { - Button pressedBtn = (Button) eventSource; - String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); - switch (actionId) { - case ACTION_ID_CREATE_FOLDER: - createFolder(); - break; - case ACTION_ID_DELETE: - deleteItems(); - break; - case ACTION_ID_OPEN: - openFile(); - break; - case ACTION_ID_UPLOAD_FILE: - uploadFiles(); - break; - default: - throw new IllegalArgumentException("Unimplemented action " + actionId); - // case ACTION_ID_SHARE_FOLDER: - // return "Share Folder"; - // case ACTION_ID_DOWNLOAD_FOLDER: - // return "Download as zip archive"; - // case ACTION_ID_OPEN: - // return "Open"; - } - } - browser.setFocus(); - // viewer.getControl().setFocus(); - // setVisible(false); - - } - } - - class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter { - private static final long serialVersionUID = -5092341449523150827L; - - @Override - public void shellDeactivated(ShellEvent e) { - setVisible(false); - } - } - - private void openFile() { - log.warn("Implement single sourced, workbench independant \"Open File\" action"); - } - - private void deleteItems() { - IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); - if (selection.isEmpty()) - return; - - StringBuilder builder = new StringBuilder(); - @SuppressWarnings("unchecked") - Iterator iterator = selection.iterator(); - List paths = new ArrayList<>(); - - while (iterator.hasNext()) { - Path path = (Path) iterator.next(); - builder.append(path.getFileName() + ", "); - paths.add(path); - } - String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) - + ". Are you sure?"; - if (MessageDialog.openConfirm(this, "Confirm deletion", msg)) { - for (Path path : paths) { - try { - // Might have already been deleted if we are in a tree - Files.deleteIfExists(path); - } catch (IOException e) { - throw new CmsException("Cannot delete path " + path, e); - } - } - browser.refresh(); - } - } - - private void createFolder() { - String msg = "Please provide a name."; - String name = SingleValue.ask("Create folder", msg); - // TODO enhance check of name validity - if (EclipseUiUtils.notEmpty(name)) { - try { - Path child = currFolderPath.resolve(name); - if (Files.exists(child)) - throw new CmsException("An item with name " + name + " already exists at " - + currFolderPath.toString() + ", cannot create"); - else - Files.createDirectories(child); - browser.refresh(); - } catch (IOException e) { - throw new CmsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e); - } - } - } - - private void uploadFiles() { - try { - FileDialog dialog = new FileDialog(browser.getShell(), SWT.MULTI); - dialog.setText("Choose one or more files to upload"); - - if (EclipseUiUtils.notEmpty(dialog.open())) { - String[] names = dialog.getFileNames(); - // Workaround small differences between RAP and RCP - // 1. returned names are absolute path on RAP and - // relative in RCP - // 2. in RCP we must use getFilterPath that does not - // exists on RAP - Method filterMethod = null; - Path parPath = null; - try { - filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath"); - String filterPath = (String) filterMethod.invoke(dialog); - parPath = Paths.get(filterPath); - } catch (NoSuchMethodException nsme) { // RAP - } - if (names.length == 0) - return; - else { - loop: for (String name : names) { - Path tmpPath = Paths.get(name); - if (parPath != null) - tmpPath = parPath.resolve(tmpPath); - if (Files.exists(tmpPath)) { - URI uri = tmpPath.toUri(); - String uriStr = uri.toString(); - - if (Files.isDirectory(tmpPath)) { - MessageDialog.openError(browser.getShell(), "Unimplemented directory import", - "Upload of directories in the system is not yet implemented"); - continue loop; - } - Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString()); - InputStream in = null; - try { - in = new ByteArrayInputStream(Files.readAllBytes(tmpPath)); - Files.copy(in, targetPath); - Files.delete(tmpPath); - } finally { - IOUtils.closeQuietly(in); - } - if (log.isDebugEnabled()) - log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString()); - } else { - String msg = "Cannot copy tmp file from " + tmpPath.toString(); - if (parPath != null) - msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS"; - MessageDialog.openError(browser.getShell(), "Missing file", msg); - continue loop; - } - } - browser.refresh(); - } - } - } catch (Exception e) { - e.printStackTrace(); - MessageDialog.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath); - } - } - - public void setCurrFolderPath(Path currFolderPath) { - this.currFolderPath = currFolderPath; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsStyles.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsStyles.java deleted file mode 100644 index 9ae319282..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsStyles.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.fs; - -/** FS Ui specific CSS styles */ -public interface FsStyles { - String BREAD_CRUMB_BTN = "breadCrumb_btn"; - String CONTEXT_MENU_BOX = "contextMenu_box"; - String BUTTON_SUFFIX = "_btn"; -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/package-info.java deleted file mode 100644 index 6a6c27286..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace file system components. */ -package org.argeo.cms.ui.fs; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java deleted file mode 100644 index e10da3aed..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/Activator.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.ui.internal; - -import org.argeo.api.cms.CmsState; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.util.tracker.ServiceTracker; - -public class Activator implements BundleActivator { - - // avoid dependency to RWT OSGi - private final static String CONTEXT_NAME_PROP = "contextName"; - - private static ServiceTracker nodeState; - - // @Override - public void start(BundleContext bc) throws Exception { - // UI -// bc.registerService(ApplicationConfiguration.class, new MaintenanceUi(), -// LangUtils.dico(CONTEXT_NAME_PROP, "system")); -// bc.registerService(ApplicationConfiguration.class, new UserUi(), LangUtils.dico(CONTEXT_NAME_PROP, "user")); - - nodeState = new ServiceTracker<>(bc, CmsState.class, null); - nodeState.open(); - } - - @Override - public void stop(BundleContext context) throws Exception { - if (nodeState != null) { - nodeState.close(); - nodeState = null; - } - } - - public static CmsState getNodeState() { - return nodeState.getService(); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java deleted file mode 100644 index ea0abdf5d..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.argeo.cms.ui.internal; - -import java.util.ArrayList; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsException; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -@Deprecated -class JcrContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -1333678161322488674L; - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - if (newInput == null) - return; - if (!(newInput instanceof Node)) - throw new CmsException("Input " + newInput + " must be a node"); - } - - @Override - public Object[] getElements(Object inputElement) { - try { - Node node = (Node) inputElement; - ArrayList arr = new ArrayList(); - NodeIterator nit = node.getNodes(); - while (nit.hasNext()) { - arr.add(nit.nextNode()); - } - return arr.toArray(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } - - @Override - public Object[] getChildren(Object parentElement) { - try { - Node node = (Node) parentElement; - ArrayList arr = new ArrayList(); - NodeIterator nit = node.getNodes(); - while (nit.hasNext()) { - arr.add(nit.nextNode()); - } - return arr.toArray(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } - - @Override - public Object getParent(Object element) { - try { - Node node = (Node) element; - if (node.getName().equals("")) - return null; - else - return node.getParent(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } - - @Override - public boolean hasChildren(Object element) { - try { - Node node = (Node) element; - return node.hasNodes(); - } catch (RepositoryException e) { - throw new CmsException("Cannot get elements", e); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java deleted file mode 100644 index 60bb42b46..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrFileUploadReceiver.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.argeo.cms.ui.internal; - -import static javax.jcr.nodetype.NodeType.NT_FILE; - -import java.io.IOException; -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.FilenameUtils; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.cms.ui.widgets.Img; -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.fileupload.FileDetails; -import org.eclipse.rap.fileupload.FileUploadReceiver; - -public class JcrFileUploadReceiver extends FileUploadReceiver { - private Img img; - private final Node parentNode; - private final String nodeName; - private final CmsImageManager imageManager; - - /** If nodeName is null, use the uploaded file name */ - public JcrFileUploadReceiver(Img img, Node parentNode, String nodeName, CmsImageManager imageManager) { - super(); - this.img = img; - this.parentNode = parentNode; - this.nodeName = nodeName; - this.imageManager = imageManager; - } - - @Override - public void receive(InputStream stream, FileDetails details) throws IOException { - try { - String fileName = nodeName != null ? nodeName : details.getFileName(); - String contentType = details.getContentType(); - if (isImage(details.getFileName(), contentType)) { - imageManager.uploadImage(img.getNode(),parentNode, fileName, stream, contentType); - return; - } - - Node fileNode; - if (parentNode.hasNode(fileName)) { - fileNode = parentNode.getNode(fileName); - if (!fileNode.isNodeType(NT_FILE)) - fileNode.remove(); - } - fileNode = JcrUtils.copyStreamAsFile(parentNode, fileName, stream); - - if (contentType != null) { - fileNode.addMixin(NodeType.MIX_MIMETYPE); - fileNode.setProperty(Property.JCR_MIMETYPE, contentType); - } - processNewFile(fileNode); - fileNode.getSession().save(); - } catch (RepositoryException e) { - throw new JcrException("Cannot receive " + details, e); - } - } - - protected Boolean isImage(String fileName, String contentType) { - String ext = FilenameUtils.getExtension(fileName); - return ext != null && (ext.equals("png") || ext.equalsIgnoreCase("jpg")); - } - - protected void processNewFile(Node node) { - - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java deleted file mode 100644 index 5e938d818..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/SimpleEditableImage.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.argeo.cms.ui.internal; - -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.ui.widgets.EditableImage; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Text; - -/** NOT working yet. */ -public class SimpleEditableImage extends EditableImage { - private static final long serialVersionUID = -5689145523114022890L; - - private String src; - private Cms2DSize imageSize; - - public SimpleEditableImage(Composite parent, int swtStyle) { - super(parent, swtStyle); - // load(getControl()); - getParent().layout(); - } - - public SimpleEditableImage(Composite parent, int swtStyle, String src, Cms2DSize imageSize) { - super(parent, swtStyle); - this.src = src; - this.imageSize = imageSize; - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) { - return createText(box, style); - } else { - return createLabel(box, style); - } - } - - protected String createImgTag() throws RepositoryException { - String imgTag; - if (src != null) - imgTag = CmsUiUtils.img(src, imageSize); - else - imgTag = CmsUiUtils.noImg(imageSize != null ? imageSize : NO_IMAGE_SIZE); - return imgTag; - } - - protected Text createText(Composite box, String style) { - Text text = new Text(box, getStyle()); - CmsSwtUtils.style(text, style); - return text; - } - - public String getSrc() { - return src; - } - - public void setSrc(String src) { - this.src = src; - } - - public Cms2DSize getImageSize() { - return imageSize; - } - - public void setImageSize(Cms2DSize imageSize) { - this.imageSize = imageSize; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java deleted file mode 100644 index 380634118..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/DefaultRepositoryRegister.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.Collections; -import java.util.Map; -import java.util.Observable; -import java.util.TreeMap; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; - -public class DefaultRepositoryRegister extends Observable implements RepositoryRegister { - /** Key for a JCR repository alias */ - private final static String CN = CmsConstants.CN; - /** Key for a JCR repository URI */ - // public final static String JCR_REPOSITORY_URI = "argeo.jcr.repository.uri"; - private final static CmsLog log = CmsLog.getLog(DefaultRepositoryRegister.class); - - /** Read only map which will be directly exposed. */ - private Map repositories = Collections.unmodifiableMap(new TreeMap()); - - @SuppressWarnings("rawtypes") - public synchronized Repository getRepository(Map parameters) throws RepositoryException { - if (!parameters.containsKey(CN)) - throw new RepositoryException("Parameter " + CN + " has to be defined."); - String alias = parameters.get(CN).toString(); - if (!repositories.containsKey(alias)) - throw new RepositoryException("No repository registered with alias " + alias); - - return repositories.get(alias); - } - - /** Access to the read-only map */ - public synchronized Map getRepositories() { - return repositories; - } - - /** Registers a service, typically called when OSGi services are bound. */ - @SuppressWarnings("rawtypes") - public synchronized void register(Repository repository, Map properties) { - String alias; - if (properties == null || !properties.containsKey(CN)) { - log.warn("Cannot register a repository if no " + CN + " property is specified."); - return; - } - alias = properties.get(CN).toString(); - Map map = new TreeMap(repositories); - map.put(alias, repository); - repositories = Collections.unmodifiableMap(map); - setChanged(); - notifyObservers(alias); - } - - /** Unregisters a service, typically called when OSGi services are unbound. */ - @SuppressWarnings("rawtypes") - public synchronized void unregister(Repository repository, Map properties) { - // TODO: also check bean name? - if (properties == null || !properties.containsKey(CN)) { - log.warn("Cannot unregister a repository without property " + CN); - return; - } - - String alias = properties.get(CN).toString(); - Map map = new TreeMap(repositories); - if (map.remove(alias) == null) { - log.warn("No repository was registered with alias " + alias); - return; - } - repositories = Collections.unmodifiableMap(map); - setChanged(); - notifyObservers(alias); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java deleted file mode 100644 index 0f7ee7735..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/FullVersioningTreeContentProvider.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; -import javax.jcr.version.Version; -import javax.jcr.version.VersionHistory; -import javax.jcr.version.VersionIterator; -import javax.jcr.version.VersionManager; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** - * Display some version information of a JCR full versionable node in a tree - * like structure - */ -public class FullVersioningTreeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = 8691772509491211112L; - - /** - * Sends back the first level of the Tree. input element must be a single - * node object - */ - public Object[] getElements(Object inputElement) { - try { - Node rootNode = (Node) inputElement; - String curPath = rootNode.getPath(); - VersionManager vm = rootNode.getSession().getWorkspace() - .getVersionManager(); - - VersionHistory vh = vm.getVersionHistory(curPath); - List result = new ArrayList(); - VersionIterator vi = vh.getAllLinearVersions(); - - while (vi.hasNext()) { - result.add(vi.nextVersion()); - } - return result.toArray(); - } catch (RepositoryException re) { - throw new EclipseUiException( - "Unexpected error while getting version elements", re); - } - } - - public Object[] getChildren(Object parentElement) { - try { - if (parentElement instanceof Version) { - List tmp = new ArrayList(); - tmp.add(((Version) parentElement).getFrozenNode()); - return tmp.toArray(); - } - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while getting child " - + "node for version element", re); - } - return null; - } - - public Object getParent(Object element) { - try { - // this will not work in a simpleVersionning environment, parent is - // not a node. - if (element instanceof Node - && ((Node) element).isNodeType(NodeType.NT_FROZEN_NODE)) { - Node node = (Node) element; - return node.getParent(); - } else - return null; - } catch (RepositoryException e) { - return null; - } - } - - public boolean hasChildren(Object element) { - try { - if (element instanceof Version) - return true; - else if (element instanceof Node) - return ((Node) element).hasNodes(); - else - return false; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check children of " + element, e); - } - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java deleted file mode 100644 index b36acc368..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrBrowserUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; - -import org.argeo.cms.ui.jcr.model.RepositoriesElem; -import org.argeo.cms.ui.jcr.model.RepositoryElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; - -/** Useful methods to manage the JCR Browser */ -public class JcrBrowserUtils { - - public static String getPropertyTypeAsString(Property prop) { - try { - return PropertyType.nameFromValue(prop.getType()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check type for " + prop, e); - } - } - - /** Insure that the UI component is not stale, refresh if needed */ - public static void forceRefreshIfNeeded(TreeParent element) { - Node curNode = null; - - boolean doRefresh = false; - - try { - if (element instanceof SingleJcrNodeElem) { - curNode = ((SingleJcrNodeElem) element).getNode(); - } else if (element instanceof WorkspaceElem) { - curNode = ((WorkspaceElem) element).getRootNode(); - } - - if (curNode != null && element.getChildren().length != curNode.getNodes().getSize()) - doRefresh = true; - else if (element instanceof RepositoryElem) { - RepositoryElem rn = (RepositoryElem) element; - if (rn.isConnected()) { - String[] wkpNames = rn.getAccessibleWorkspaceNames(); - if (element.getChildren().length != wkpNames.length) - doRefresh = true; - } - } else if (element instanceof RepositoriesElem) { - doRefresh = true; - // Always force refresh for RepositoriesElem : the condition - // below does not take remote repository into account and it is - // not trivial to do so. - - // RepositoriesElem rn = (RepositoriesElem) element; - // if (element.getChildren().length != - // rn.getRepositoryRegister() - // .getRepositories().size()) - // doRefresh = true; - } - if (doRefresh) { - element.clearChildren(); - element.getChildren(); - } - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while synchronising the UI with the JCR repository", re); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java deleted file mode 100644 index 1707681b4..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrDClickListener.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.Node; - -import org.argeo.cms.ui.jcr.model.RepositoryElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TreeViewer; - -/** Centralizes the management of double click on a NodeTreeViewer */ -public class JcrDClickListener implements IDoubleClickListener { - // private final static Log log = LogFactory - // .getLog(GenericNodeDoubleClickListener.class); - - private TreeViewer nodeViewer; - - // private JcrFileProvider jfp; - // private FileHandler fileHandler; - - public JcrDClickListener(TreeViewer nodeViewer) { - this.nodeViewer = nodeViewer; - // jfp = new JcrFileProvider(); - // Commented out. see https://www.argeo.org/bugzilla/show_bug.cgi?id=188 - // fileHandler = null; - // fileHandler = new FileHandler(jfp); - } - - public void doubleClick(DoubleClickEvent event) { - if (event.getSelection() == null || event.getSelection().isEmpty()) - return; - Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement(); - if (obj instanceof RepositoryElem) { - RepositoryElem rpNode = (RepositoryElem) obj; - if (rpNode.isConnected()) { - rpNode.logout(); - } else { - rpNode.login(); - } - nodeViewer.refresh(obj); - } else if (obj instanceof WorkspaceElem) { - WorkspaceElem wn = (WorkspaceElem) obj; - if (wn.isConnected()) - wn.logout(); - else - wn.login(); - nodeViewer.refresh(obj); - } else if (obj instanceof SingleJcrNodeElem) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) obj; - Node node = sjn.getNode(); - openNode(node); - } - } - - protected void openNode(Node node) { - // TODO implement generic behaviour - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrImages.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrImages.java deleted file mode 100644 index d1d1e31ef..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrImages.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import org.argeo.cms.ui.theme.CmsImages; -import org.eclipse.swt.graphics.Image; - -/** Shared icons. */ -public class JcrImages { - public final static Image NODE = CmsImages.createIcon("node.gif"); - public final static Image FOLDER = CmsImages.createIcon("folder.gif"); - public final static Image FILE = CmsImages.createIcon("file.gif"); - public final static Image BINARY = CmsImages.createIcon("binary.png"); - public final static Image HOME = CmsImages.createIcon("person-logged-in.png"); - public final static Image SORT = CmsImages.createIcon("sort.gif"); - public final static Image REMOVE = CmsImages.createIcon("remove.gif"); - - public final static Image REPOSITORIES = CmsImages.createIcon("repositories.gif"); - public final static Image REPOSITORY_DISCONNECTED = CmsImages.createIcon("repository_disconnected.gif"); - public final static Image REPOSITORY_CONNECTED = CmsImages.createIcon("repository_connected.gif"); - public final static Image REMOTE_DISCONNECTED = CmsImages.createIcon("remote_disconnected.gif"); - public final static Image REMOTE_CONNECTED = CmsImages.createIcon("remote_connected.gif"); - public final static Image WORKSPACE_DISCONNECTED = CmsImages.createIcon("workspace_disconnected.png"); - public final static Image WORKSPACE_CONNECTED = CmsImages.createIcon("workspace_connected.png"); - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java deleted file mode 100644 index cc8479f78..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/JcrTreeContentProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** - * Implementation of the {@code ITreeContentProvider} in order to display a - * single JCR node and its children in a tree like structure - */ -public class JcrTreeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -2128326504754297297L; - // private Node rootNode; - private JcrItemsComparator itemComparator = new JcrItemsComparator(); - - /** - * Sends back the first level of the Tree. input element must be a single node - * object - */ - public Object[] getElements(Object inputElement) { - Node rootNode = (Node) inputElement; - return childrenNodes(rootNode); - } - - public Object[] getChildren(Object parentElement) { - return childrenNodes((Node) parentElement); - } - - public Object getParent(Object element) { - try { - Node node = (Node) element; - if (!node.getPath().equals("/")) - return node.getParent(); - else - return null; - } catch (RepositoryException e) { - return null; - } - } - - public boolean hasChildren(Object element) { - try { - return ((Node) element).hasNodes(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check children existence on " + element, e); - } - } - - protected Object[] childrenNodes(Node parentNode) { - try { - List children = new ArrayList(); - NodeIterator nit = parentNode.getNodes(); - while (nit.hasNext()) { - Node node = nit.nextNode(); -// if (node.getName().startsWith("rep:") || node.getName().startsWith("jcr:") -// || node.getName().startsWith("nt:")) -// continue nodes; - children.add(node); - } - Node[] arr = children.toArray(new Node[0]); - Arrays.sort(arr, itemComparator); - return arr; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot list children of " + parentNode, e); - } - } - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java deleted file mode 100644 index 0625cc872..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeContentProvider.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.ui.jcr.model.RepositoriesElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ux.widgets.TreeParent; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** - * Implementation of the {@code ITreeContentProvider} to display multiple - * repository environment in a tree like structure - */ -public class NodeContentProvider implements ITreeContentProvider { - private static final long serialVersionUID = -4083809398848374403L; - final private RepositoryRegister repositoryRegister; - final private RepositoryFactory repositoryFactory; - - // Current user session on the default workspace of the argeo Node - final private Session userSession; - final private Keyring keyring; - private boolean sortChildren; - - // Reference for cleaning - private SingleJcrNodeElem homeNode = null; - private RepositoriesElem repositoriesNode = null; - - // Utils - private TreeBrowserComparator itemComparator = new TreeBrowserComparator(); - - public NodeContentProvider(Session userSession, Keyring keyring, - RepositoryRegister repositoryRegister, - RepositoryFactory repositoryFactory, Boolean sortChildren) { - this.userSession = userSession; - this.keyring = keyring; - this.repositoryRegister = repositoryRegister; - this.repositoryFactory = repositoryFactory; - this.sortChildren = sortChildren; - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - if (newInput == null)// dispose - return; - - if (userSession != null) { - Node userHome = CmsJcrUtils.getUserHome(userSession); - if (userHome != null) { - // TODO : find a way to dynamically get alias for the node - if (homeNode != null) - homeNode.dispose(); - homeNode = new SingleJcrNodeElem(null, userHome, - userSession.getUserID(), CmsConstants.EGO_REPOSITORY); - } - } - if (repositoryRegister != null) { - if (repositoriesNode != null) - repositoriesNode.dispose(); - repositoriesNode = new RepositoriesElem("Repositories", - repositoryRegister, repositoryFactory, null, userSession, - keyring); - } - } - - /** - * Sends back the first level of the Tree. Independent from inputElement - * that can be null - */ - public Object[] getElements(Object inputElement) { - List objs = new ArrayList(); - if (homeNode != null) - objs.add(homeNode); - if (repositoriesNode != null) - objs.add(repositoriesNode); - return objs.toArray(); - } - - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof TreeParent) { - if (sortChildren) { - Object[] tmpArr = ((TreeParent) parentElement).getChildren(); - if (tmpArr == null) - return new Object[0]; - TreeParent[] arr = new TreeParent[tmpArr.length]; - for (int i = 0; i < tmpArr.length; i++) - arr[i] = (TreeParent) tmpArr[i]; - Arrays.sort(arr, itemComparator); - return arr; - } else - return ((TreeParent) parentElement).getChildren(); - } else - return new Object[0]; - } - - /** - * Sets whether the content provider should order the children nodes or not. - * It is user duty to call a full refresh of the tree after changing this - * parameter. - */ - public void setSortChildren(boolean sortChildren) { - this.sortChildren = sortChildren; - } - - public Object getParent(Object element) { - if (element instanceof TreeParent) { - return ((TreeParent) element).getParent(); - } else - return null; - } - - public boolean hasChildren(Object element) { - if (element instanceof RepositoriesElem) { - RepositoryRegister rr = ((RepositoriesElem) element) - .getRepositoryRegister(); - return rr.getRepositories().size() > 0; - } else if (element instanceof TreeParent) { - TreeParent tp = (TreeParent) element; - return tp.hasChildren(); - } - return false; - } - - public void dispose() { - if (homeNode != null) - homeNode.dispose(); - if (repositoriesNode != null) { - // logs out open sessions - // see https://bugzilla.argeo.org/show_bug.cgi?id=23 - repositoriesNode.dispose(); - } - } - - /** - * Specific comparator for this view. See specification here: - * https://www.argeo.org/bugzilla/show_bug.cgi?id=139 - */ - private class TreeBrowserComparator implements Comparator { - - public int category(TreeParent element) { - if (element instanceof SingleJcrNodeElem) { - Node node = ((SingleJcrNodeElem) element).getNode(); - try { - if (node.isNodeType(NodeType.NT_FOLDER)) - return 5; - } catch (RepositoryException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - return 10; - } - - public int compare(TreeParent o1, TreeParent o2) { - int cat1 = category(o1); - int cat2 = category(o2); - - if (cat1 != cat2) { - return cat1 - cat2; - } - return o1.getName().compareTo(o2.getName()); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java deleted file mode 100644 index a5751c083..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/NodeLabelProvider.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.NamespaceException; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.ui.jcr.model.RemoteRepositoryElem; -import org.argeo.cms.ui.jcr.model.RepositoriesElem; -import org.argeo.cms.ui.jcr.model.RepositoryElem; -import org.argeo.cms.ui.jcr.model.SingleJcrNodeElem; -import org.argeo.cms.ui.jcr.model.WorkspaceElem; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** Provides reasonable defaults for know JCR types. */ -public class NodeLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -3662051696443321843L; - - private final static CmsLog log = CmsLog.getLog(NodeLabelProvider.class); - - public String getText(Object element) { - try { - if (element instanceof SingleJcrNodeElem) { - SingleJcrNodeElem sjn = (SingleJcrNodeElem) element; - return getText(sjn.getNode()); - } else if (element instanceof Node) { - return getText((Node) element); - } else - return super.getText(element); - } catch (RepositoryException e) { - throw new EclipseUiException("Unexpected JCR error while getting node name."); - } - } - - protected String getText(Node node) throws RepositoryException { - String label = node.getName(); - StringBuffer mixins = new StringBuffer(""); - for (NodeType type : node.getMixinNodeTypes()) - mixins.append(' ').append(type.getName()); - - return label + " [" + node.getPrimaryNodeType().getName() + mixins + "]"; - } - - @Override - public Image getImage(Object element) { - if (element instanceof RemoteRepositoryElem) { - if (((RemoteRepositoryElem) element).isConnected()) - return JcrImages.REMOTE_CONNECTED; - else - return JcrImages.REMOTE_DISCONNECTED; - } else if (element instanceof RepositoryElem) { - if (((RepositoryElem) element).isConnected()) - return JcrImages.REPOSITORY_CONNECTED; - else - return JcrImages.REPOSITORY_DISCONNECTED; - } else if (element instanceof WorkspaceElem) { - if (((WorkspaceElem) element).isConnected()) - return JcrImages.WORKSPACE_CONNECTED; - else - return JcrImages.WORKSPACE_DISCONNECTED; - } else if (element instanceof RepositoriesElem) { - return JcrImages.REPOSITORIES; - } else if (element instanceof SingleJcrNodeElem) { - Node nodeElem = ((SingleJcrNodeElem) element).getNode(); - return getImage(nodeElem); - - // if (element instanceof Node) { - // return getImage((Node) element); - // } else if (element instanceof WrappedNode) { - // return getImage(((WrappedNode) element).getNode()); - // } else if (element instanceof NodesWrapper) { - // return getImage(((NodesWrapper) element).getNode()); - // } - } - // try { - // return super.getImage(); - // } catch (RepositoryException e) { - // return null; - // } - return super.getImage(element); - } - - protected Image getImage(Node node) { - try { - if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FILE)) - return JcrImages.FILE; - else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_FOLDER)) - return JcrImages.FOLDER; - else if (node.getPrimaryNodeType().isNodeType(NodeType.NT_RESOURCE)) - return JcrImages.BINARY; - try { - // TODO check workspace type? - if (node.getDepth() == 1 && node.hasProperty(Property.JCR_ID)) - return JcrImages.HOME; - - // optimizes -// if (node.hasProperty(LdapAttrs.uid.property()) && node.isNodeType(NodeTypes.NODE_USER_HOME)) -// return JcrImages.HOME; - } catch (NamespaceException e) { - // node namespace is not registered in this repo - } - return JcrImages.NODE; - } catch (RepositoryException e) { - log.warn("Error while retrieving type for " + node + " in order to display corresponding image"); - e.printStackTrace(); - return null; - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java deleted file mode 100644 index 444350aeb..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/OsgiRepositoryRegister.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Repository; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; - -public class OsgiRepositoryRegister extends DefaultRepositoryRegister { - private final static BundleContext bc = FrameworkUtil.getBundle(OsgiRepositoryRegister.class).getBundleContext(); - private final ServiceTracker repositoryTracker; - - public OsgiRepositoryRegister() { - repositoryTracker = new ServiceTracker(bc, Repository.class, null) { - - @Override - public Repository addingService(ServiceReference reference) { - - Repository repository = super.addingService(reference); - Map props = new HashMap<>(); - for (String key : reference.getPropertyKeys()) { - props.put(key, reference.getProperty(key)); - } - register(repository, props); - return repository; - } - - @Override - public void removedService(ServiceReference reference, Repository service) { - Map props = new HashMap<>(); - for (String key : reference.getPropertyKeys()) { - props.put(key, reference.getProperty(key)); - } - unregister(service, props); - super.removedService(reference, service); - } - - }; - } - - public void init() { - repositoryTracker.open(); - } - - public void destroy() { - repositoryTracker.close(); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java deleted file mode 100644 index fd544bbd8..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertiesContentProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.Set; -import java.util.TreeSet; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.util.JcrItemsComparator; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** Simple content provider that displays all properties of a given Node */ -public class PropertiesContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = 5227554668841613078L; - private JcrItemsComparator itemComparator = new JcrItemsComparator(); - - public void dispose() { - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - public Object[] getElements(Object inputElement) { - try { - if (inputElement instanceof Node) { - Set props = new TreeSet(itemComparator); - PropertyIterator pit = ((Node) inputElement).getProperties(); - while (pit.hasNext()) - props.add(pit.nextProperty()); - return props.toArray(); - } - return new Object[] {}; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get element for " - + inputElement, e); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java deleted file mode 100644 index 37b90f7ee..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/PropertyLabelProvider.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; - -import javax.jcr.Property; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.ViewerCell; - -/** Default basic label provider for a given JCR Node's properties */ -public class PropertyLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -5405794508731390147L; - - // To be able to change column order easily - public static final int COLUMN_PROPERTY = 0; - public static final int COLUMN_VALUE = 1; - public static final int COLUMN_TYPE = 2; - public static final int COLUMN_ATTRIBUTES = 3; - - // Utils - protected DateFormat timeFormatter = new SimpleDateFormat(CmsUiConstants.DATE_TIME_FORMAT); - - public void update(ViewerCell cell) { - Object element = cell.getElement(); - cell.setText(getColumnText(element, cell.getColumnIndex())); - } - - public String getColumnText(Object element, int columnIndex) { - try { - if (element instanceof Property) { - Property prop = (Property) element; - if (prop.isMultiple()) { - switch (columnIndex) { - case COLUMN_PROPERTY: - return prop.getName(); - case COLUMN_VALUE: - // Corresponding values are listed on children - return ""; - case COLUMN_TYPE: - return JcrBrowserUtils.getPropertyTypeAsString(prop); - case COLUMN_ATTRIBUTES: - return JcrUtils.getPropertyDefinitionAsString(prop); - } - } else { - switch (columnIndex) { - case COLUMN_PROPERTY: - return prop.getName(); - case COLUMN_VALUE: - return formatValueAsString(prop.getValue()); - case COLUMN_TYPE: - return JcrBrowserUtils.getPropertyTypeAsString(prop); - case COLUMN_ATTRIBUTES: - return JcrUtils.getPropertyDefinitionAsString(prop); - } - } - } else if (element instanceof Value) { - Value val = (Value) element; - switch (columnIndex) { - case COLUMN_PROPERTY: - // Nothing to show - return ""; - case COLUMN_VALUE: - return formatValueAsString(val); - case COLUMN_TYPE: - // listed on the parent - return ""; - case COLUMN_ATTRIBUTES: - // Corresponding attributes are listed on the parent - return ""; - } - } - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot retrieve prop value on " + element, re); - } - return null; - } - - private String formatValueAsString(Value value) { - // TODO enhance this method - try { - String strValue; - - if (value.getType() == PropertyType.BINARY) - strValue = ""; - else if (value.getType() == PropertyType.DATE) - strValue = timeFormatter.format(value.getDate().getTime()); - else - strValue = value.getString(); - return strValue; - } catch (RepositoryException e) { - throw new EclipseUiException("unexpected error while formatting value", e); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java deleted file mode 100644 index 802c75619..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/RepositoryRegister.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import java.util.Map; - -import javax.jcr.Repository; -import javax.jcr.RepositoryFactory; - -/** Allows to register repositories by name. */ -public interface RepositoryRegister extends RepositoryFactory { - /** - * The registered {@link Repository} as a read-only map. Note that this - * method should be called for each access in order to be sure to be up to - * date in case repositories have registered/unregistered - */ - public Map getRepositories(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java deleted file mode 100644 index 37dfe2b8f..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/VersionLabelProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.argeo.cms.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.version.Version; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; - -/** - * Simple wrapping of the ColumnLabelProvider class to provide text display in - * order to build a tree for version. The getText() method does not assume that - * {@link Version} extends {@link Node} class to respect JCR 2.0 specification - * - */ -public class VersionLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = 5270739851193688238L; - - public String getText(Object element) { - try { - if (element instanceof Version) { - Version version = (Version) element; - return version.getName(); - } else if (element instanceof Node) { - return ((Node) element).getName(); - } - } catch (RepositoryException re) { - throw new EclipseUiException( - "Unexpected error while getting element name", re); - } - return super.getText(element); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java deleted file mode 100644 index d33b33f63..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/MaintainedRepositoryElem.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.Repository; - -import org.argeo.cms.ux.widgets.TreeParent; - -/** Wrap a MaintainedRepository */ -public class MaintainedRepositoryElem extends RepositoryElem { - - public MaintainedRepositoryElem(String alias, Repository repository, TreeParent parent) { - super(alias, repository, parent); - // if (!(repository instanceof MaintainedRepository)) { - // throw new ArgeoException("Repository " + alias - // + " is not a maintained repository"); - // } - } - - // protected MaintainedRepository getMaintainedRepository() { - // return (MaintainedRepository) getRepository(); - // } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java deleted file mode 100644 index 908d1b135..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RemoteRepositoryElem.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import java.util.Arrays; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; -import javax.jcr.SimpleCredentials; - -import org.argeo.cms.ArgeoNames; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; - -/** Root of a remote repository */ -public class RemoteRepositoryElem extends RepositoryElem { - private final Keyring keyring; - /** - * A session of the logged in user on the default workspace of the node - * repository. - */ - private final Session userSession; - private final String remoteNodePath; - - private final RepositoryFactory repositoryFactory; - private final String uri; - - public RemoteRepositoryElem(String alias, RepositoryFactory repositoryFactory, String uri, TreeParent parent, - Session userSession, Keyring keyring, String remoteNodePath) { - super(alias, null, parent); - this.repositoryFactory = repositoryFactory; - this.uri = uri; - this.keyring = keyring; - this.userSession = userSession; - this.remoteNodePath = remoteNodePath; - } - - @Override - protected Session repositoryLogin(String workspaceName) throws RepositoryException { - Node remoteRepository = userSession.getNode(remoteNodePath); - String userID = remoteRepository.getProperty(ArgeoNames.ARGEO_USER_ID).getString(); - if (userID.trim().equals("")) { - return getRepository().login(workspaceName); - } else { - String pwdPath = remoteRepository.getPath() + '/' + ArgeoNames.ARGEO_PASSWORD; - char[] password = keyring.getAsChars(pwdPath); - try { - SimpleCredentials credentials = new SimpleCredentials(userID, password); - return getRepository().login(credentials, workspaceName); - } finally { - Arrays.fill(password, 0, password.length, ' '); - } - } - } - - @Override - public Repository getRepository() { - if (repository == null) - repository = CmsJcrUtils.getRepositoryByUri(repositoryFactory, uri); - return super.getRepository(); - } - - public void remove() { - try { - Node remoteNode = userSession.getNode(remoteNodePath); - remoteNode.remove(); - remoteNode.getSession().save(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot remove " + remoteNodePath, e); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java deleted file mode 100644 index 742800b0b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoriesElem.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.RepositoryFactory; -import javax.jcr.Session; - -import org.argeo.cms.ArgeoNames; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.security.Keyring; -import org.argeo.cms.ui.jcr.RepositoryRegister; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.dialogs.ErrorFeedback; - -/** - * UI Tree component that implements the Argeo abstraction of a - * {@link RepositoryFactory} that enable a user to "mount" various repositories - * in a single Tree like View. It is usually meant to be at the root of the UI - * Tree and thus {@link getParent()} method will return null. - * - * The {@link RepositoryFactory} is injected at instantiation time and must be - * use get or register new {@link Repository} objects upon which a reference is - * kept here. - */ - -public class RepositoriesElem extends TreeParent implements ArgeoNames { - private final RepositoryRegister repositoryRegister; - private final RepositoryFactory repositoryFactory; - - /** - * A session of the logged in user on the default workspace of the node - * repository. - */ - private final Session userSession; - private final Keyring keyring; - - public RepositoriesElem(String name, RepositoryRegister repositoryRegister, RepositoryFactory repositoryFactory, - TreeParent parent, Session userSession, Keyring keyring) { - super(name); - this.repositoryRegister = repositoryRegister; - this.repositoryFactory = repositoryFactory; - this.userSession = userSession; - this.keyring = keyring; - } - - /** - * Override normal behavior to initialize the various repositories only at - * request time - */ - @Override - public synchronized Object[] getChildren() { - if (isLoaded()) { - return super.getChildren(); - } else { - // initialize current object - Map refRepos = repositoryRegister.getRepositories(); - for (String name : refRepos.keySet()) { - Repository repository = refRepos.get(name); - // if (repository instanceof MaintainedRepository) - // super.addChild(new MaintainedRepositoryElem(name, - // repository, this)); - // else - super.addChild(new RepositoryElem(name, repository, this)); - } - - // remote - if (keyring != null) { - try { - addRemoteRepositories(keyring); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot browse remote repositories", e); - } - } - return super.getChildren(); - } - } - - protected void addRemoteRepositories(Keyring jcrKeyring) throws RepositoryException { - Node userHome = CmsJcrUtils.getUserHome(userSession); - if (userHome != null && userHome.hasNode(ARGEO_REMOTE)) { - NodeIterator it = userHome.getNode(ARGEO_REMOTE).getNodes(); - while (it.hasNext()) { - Node remoteNode = it.nextNode(); - String uri = remoteNode.getProperty(ARGEO_URI).getString(); - try { - RemoteRepositoryElem remoteRepositoryNode = new RemoteRepositoryElem(remoteNode.getName(), - repositoryFactory, uri, this, userSession, jcrKeyring, remoteNode.getPath()); - super.addChild(remoteRepositoryNode); - } catch (Exception e) { - ErrorFeedback.show("Cannot add remote repository " + remoteNode, e); - } - } - } - } - - public void registerNewRepository(String alias, Repository repository) { - // TODO: implement this - // Create a new RepositoryNode Object - // add it - // super.addChild(new RepositoriesNode(...)); - } - - /** Returns the {@link RepositoryRegister} wrapped by this object. */ - public RepositoryRegister getRepositoryRegister() { - return repositoryRegister; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java deleted file mode 100644 index 296c36922..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/RepositoryElem.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; - -/** - * UI Tree component that wraps a JCR {@link Repository}. It also keeps a - * reference to its parent Tree Ui component; typically the unique - * {@link RepositoriesElem} object of the current view to enable bi-directionnal - * browsing in the tree. - */ - -public class RepositoryElem extends TreeParent { - private String alias; - protected Repository repository; - private Session defaultSession = null; - - /** Create a new repository with distinct name and alias */ - public RepositoryElem(String alias, Repository repository, TreeParent parent) { - super(alias); - this.repository = repository; - setParent(parent); - this.alias = alias; - } - - public void login() { - try { - defaultSession = repositoryLogin(CmsConstants.SYS_WORKSPACE); - String[] wkpNames = defaultSession.getWorkspace().getAccessibleWorkspaceNames(); - for (String wkpName : wkpNames) { - if (wkpName.equals(defaultSession.getWorkspace().getName())) - addChild(new WorkspaceElem(this, wkpName, defaultSession)); - else - addChild(new WorkspaceElem(this, wkpName)); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot connect to repository " + alias, e); - } - } - - public synchronized void logout() { - for (Object child : getChildren()) { - if (child instanceof WorkspaceElem) - ((WorkspaceElem) child).logout(); - } - clearChildren(); - JcrUtils.logoutQuietly(defaultSession); - defaultSession = null; - } - - /** - * Actual call to the {@link Repository#login(javax.jcr.Credentials, String)} - * method. To be overridden. - */ - protected Session repositoryLogin(String workspaceName) throws RepositoryException { - return repository.login(workspaceName); - } - - public String[] getAccessibleWorkspaceNames() { - try { - return defaultSession.getWorkspace().getAccessibleWorkspaceNames(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot retrieve workspace names", e); - } - } - - public void createWorkspace(String workspaceName) { - if (!isConnected()) - login(); - try { - defaultSession.getWorkspace().createWorkspace(workspaceName); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot create workspace", e); - } - } - - /** returns the {@link Repository} referenced by the current UI Node */ - public Repository getRepository() { - return repository; - } - - public String getAlias() { - return alias; - } - - public Boolean isConnected() { - if (defaultSession != null && defaultSession.isLive()) - return true; - else - return false; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java deleted file mode 100644 index a2584a5e8..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/SingleJcrNodeElem.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.Workspace; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; - -/** - * UI Tree component. Wraps a node of a JCR {@link Workspace}. It also keeps a - * reference to its parent node that can either be a {@link WorkspaceElem}, a - * {@link SingleJcrNodeElem} or null if the node is "mounted" as the root of the - * UI tree. - */ -public class SingleJcrNodeElem extends TreeParent { - - private final Node node; - private String alias = null; - - /** Creates a new UiNode in the UI Tree */ - public SingleJcrNodeElem(TreeParent parent, Node node, String name) { - super(name); - setParent(parent); - this.node = node; - } - - /** - * Creates a new UiNode in the UI Tree, keeping a reference to the alias of - * the corresponding repository in the current UI environment. It is useful - * to be able to mount nodes as roots of the UI tree. - */ - public SingleJcrNodeElem(TreeParent parent, Node node, String name, String alias) { - super(name); - setParent(parent); - this.node = node; - this.alias = alias; - } - - /** Returns the node wrapped by the current UI object */ - public Node getNode() { - return node; - } - - protected String getRepositoryAlias() { - return alias; - } - - /** - * Overrides normal behaviour to initialise children only when first - * requested - */ - @Override - public synchronized Object[] getChildren() { - if (isLoaded()) { - return super.getChildren(); - } else { - // initialize current object - try { - NodeIterator ni = node.getNodes(); - while (ni.hasNext()) { - Node curNode = ni.nextNode(); - addChild(new SingleJcrNodeElem(this, curNode, curNode.getName())); - } - return super.getChildren(); - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot initialize SingleJcrNode children", re); - } - } - } - - @Override - public boolean hasChildren() { - try { - if (node.getSession().isLive()) - return node.hasNodes(); - else - return false; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot check children node existence", re); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java deleted file mode 100644 index 2d786669f..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/WorkspaceElem.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.argeo.cms.ui.jcr.model; - -import javax.jcr.AccessDeniedException; -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -// import javax.jcr.Workspace; -import javax.jcr.Workspace; - -import org.argeo.cms.ux.widgets.TreeParent; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; - -/** - * UI Tree component. Wraps the root node of a JCR {@link Workspace}. It also - * keeps a reference to its parent {@link RepositoryElem}, to be able to - * retrieve alias of the current used repository - */ -public class WorkspaceElem extends TreeParent { - private Session session = null; - - public WorkspaceElem(RepositoryElem parent, String name) { - this(parent, name, null); - } - - public WorkspaceElem(RepositoryElem parent, String name, Session session) { - super(name); - this.session = session; - setParent(parent); - } - - public synchronized Session getSession() { - return session; - } - - public synchronized Node getRootNode() { - try { - if (session != null) - return session.getRootNode(); - else - return null; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get root node of workspace " + getName(), e); - } - } - - public synchronized void login() { - try { - session = ((RepositoryElem) getParent()).repositoryLogin(getName()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot connect to repository " + getName(), e); - } - } - - public Boolean isConnected() { - if (session != null && session.isLive()) - return true; - else - return false; - } - - @Override - public synchronized void dispose() { - logout(); - super.dispose(); - } - - /** Logouts the session, does not nothing if there is no live session. */ - public synchronized void logout() { - clearChildren(); - JcrUtils.logoutQuietly(session); - session = null; - } - - @Override - public synchronized boolean hasChildren() { - try { - if (isConnected()) - try { - return session.getRootNode().hasNodes(); - } catch (AccessDeniedException e) { - // current user may not have access to the root node - return false; - } - else - return false; - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while checking children node existence", re); - } - } - - /** Override normal behaviour to initialize display of the workspace */ - @Override - public synchronized Object[] getChildren() { - if (isLoaded()) { - return super.getChildren(); - } else { - // initialize current object - try { - Node rootNode; - if (session == null) - return null; - else - rootNode = session.getRootNode(); - NodeIterator ni = rootNode.getNodes(); - while (ni.hasNext()) { - Node node = ni.nextNode(); - addChild(new SingleJcrNodeElem(this, node, node.getName())); - } - return super.getChildren(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot initialize WorkspaceNode UI object." + getName(), e); - } - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/package-info.java deleted file mode 100644 index 8f5474449..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/model/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Model for SWT/JFace JCR components. */ -package org.argeo.cms.ui.jcr.model; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/package-info.java deleted file mode 100644 index 26ae330b5..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/jcr/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace JCR components. */ -package org.argeo.cms.ui.jcr; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/package-info.java deleted file mode 100644 index 82fdee796..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** SWT/JFace components for Argeo CMS. */ -package org.argeo.cms.ui; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsLink.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsLink.java deleted file mode 100644 index e91f9ba48..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsLink.java +++ /dev/null @@ -1,282 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsStyle; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.jcr.JcrException; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.service.ResourceManager; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.osgi.framework.BundleContext; - -/** A link to an internal or external location. */ -public class CmsLink implements CmsUiProvider { - private final static CmsLog log = CmsLog.getLog(CmsLink.class); - private BundleContext bundleContext; - - private String label; - private String style; - private String target; - private String image; - private boolean openNew = false; - private MouseListener mouseListener; - - private int horizontalAlignment = SWT.CENTER; - private int verticalAlignment = SWT.CENTER; - - private String loggedInLabel = null; - private String loggedInTarget = null; - - // internal - // private Boolean isUrl = false; - private Integer imageWidth, imageHeight; - - public CmsLink() { - super(); - } - - public CmsLink(String label, String target) { - this(label, target, (String) null); - } - - public CmsLink(String label, String target, CmsStyle style) { - this(label, target, style != null ? style.style() : null); - } - - public CmsLink(String label, String target, String style) { - super(); - this.label = label; - this.target = target; - this.style = style; - init(); - } - - public void init() { - if (image != null) { - ImageData image = loadImage(); - if (imageHeight == null && imageWidth == null) { - imageWidth = image.width; - imageHeight = image.height; - } else if (imageHeight == null) { - imageHeight = (imageWidth * image.height) / image.width; - } else if (imageWidth == null) { - imageWidth = (imageHeight * image.width) / image.height; - } - } - } - - /** @return {@link Composite} with a single {@link Label} child. */ - @Override - public Control createUi(final Composite parent, Node context) { -// if (image != null && (imageWidth == null || imageHeight == null)) { -// throw new CmsException("Image is not properly configured." -// + " Make sure bundleContext property is set and init() method has been called."); -// } - - Composite comp = new Composite(parent, SWT.NONE); - comp.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - Label link = new Label(comp, SWT.NONE); - CmsSwtUtils.markup(link); - GridData layoutData = new GridData(horizontalAlignment, verticalAlignment, false, false); - if (image != null) { - if (imageHeight != null) - layoutData.heightHint = imageHeight; - if (label == null) - if (imageWidth != null) - layoutData.widthHint = imageWidth; - } - - link.setLayoutData(layoutData); - CmsSwtUtils.style(comp, style != null ? style : getDefaultStyle()); - CmsSwtUtils.style(link, style != null ? style : getDefaultStyle()); - - // label - StringBuilder labelText = new StringBuilder(); - if (loggedInTarget != null && isLoggedIn()) { - labelText.append(""); - } else if (target != null) { - labelText.append(""); - } - if (image != null) { - registerImageIfNeeded(); - String imageLocation = RWT.getResourceManager().getLocation(image); - labelText.append(""); - - } - - if (loggedInLabel != null && isLoggedIn()) { - labelText.append(' ').append(loggedInLabel); - } else if (label != null) { - labelText.append(' ').append(label); - } - - if ((loggedInTarget != null && isLoggedIn()) || target != null) - labelText.append(""); - - link.setText(labelText.toString()); - - if (mouseListener != null) - link.addMouseListener(mouseListener); - - return comp; - } - - private void registerImageIfNeeded() { - ResourceManager resourceManager = RWT.getResourceManager(); - if (!resourceManager.isRegistered(image)) { - URL res = getImageUrl(); - try (InputStream inputStream = res.openStream()) { - resourceManager.register(image, inputStream); - if (log.isTraceEnabled()) - log.trace("Registered image " + image); - } catch (IOException e) { - throw new RuntimeException("Cannot load image " + image, e); - } - } - } - - private ImageData loadImage() { - URL url = getImageUrl(); - ImageData result = null; - try (InputStream inputStream = url.openStream()) { - result = new ImageData(inputStream); - if (log.isTraceEnabled()) - log.trace("Loaded image " + image); - } catch (IOException e) { - throw new RuntimeException("Cannot load image " + image, e); - } - return result; - } - - private URL getImageUrl() { - URL url; - try { - // pure URL - url = new URL(image); - } catch (MalformedURLException e1) { - url = bundleContext.getBundle().getResource(image); - } - - if (url == null) - throw new IllegalStateException("No image " + image + " available."); - - return url; - } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public void setLabel(String label) { - this.label = label; - } - - public void setStyle(String style) { - this.style = style; - } - - /** @deprecated Use {@link #setStyle(String)} instead. */ - @Deprecated - public void setCustom(String custom) { - this.style = custom; - } - - public void setTarget(String target) { - this.target = target; - // try { - // new URL(target); - // isUrl = true; - // } catch (MalformedURLException e1) { - // isUrl = false; - // } - } - - public void setImage(String image) { - this.image = image; - } - - public void setLoggedInLabel(String loggedInLabel) { - this.loggedInLabel = loggedInLabel; - } - - public void setLoggedInTarget(String loggedInTarget) { - this.loggedInTarget = loggedInTarget; - } - - public void setMouseListener(MouseListener mouseListener) { - this.mouseListener = mouseListener; - } - - public void setvAlign(String vAlign) { - if ("bottom".equals(vAlign)) { - verticalAlignment = SWT.BOTTOM; - } else if ("top".equals(vAlign)) { - verticalAlignment = SWT.TOP; - } else if ("center".equals(vAlign)) { - verticalAlignment = SWT.CENTER; - } else { - throw new IllegalArgumentException( - "Unsupported vertical alignment " + vAlign + " (must be: top, bottom or center)"); - } - } - - protected boolean isLoggedIn() { - return !CurrentUser.isAnonymous(); - } - - public void setImageWidth(Integer imageWidth) { - this.imageWidth = imageWidth; - } - - public void setImageHeight(Integer imageHeight) { - this.imageHeight = imageHeight; - } - - public void setOpenNew(boolean openNew) { - this.openNew = openNew; - } - - protected String getDefaultStyle() { - return SimpleStyle.link.name(); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsPane.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsPane.java deleted file mode 100644 index fc0c82146..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsPane.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.argeo.cms.ui.util; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Composite; - -/** The main pane of a CMS display, with QA and support areas. */ -public class CmsPane { - - private Composite mainArea; - private Composite qaArea; - private Composite supportArea; - - public CmsPane(Composite parent, int style) { - parent.setLayout(CmsSwtUtils.noSpaceGridLayout()); - -// qaArea = new Composite(parent, SWT.NONE); -// qaArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); -// RowLayout qaLayout = new RowLayout(); -// qaLayout.spacing = 0; -// qaArea.setLayout(qaLayout); - - mainArea = new Composite(parent, SWT.NONE); - mainArea.setLayout(new GridLayout()); - mainArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - -// supportArea = new Composite(parent, SWT.NONE); -// supportArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); -// RowLayout supportLayout = new RowLayout(); -// supportLayout.spacing = 0; -// supportArea.setLayout(supportLayout); - } - - public Composite getMainArea() { - return mainArea; - } - - public Composite getQaArea() { - return qaArea; - } - - public Composite getSupportArea() { - return supportArea; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java deleted file mode 100644 index 178fbcf24..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/CmsUiUtils.java +++ /dev/null @@ -1,203 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.servlet.http.HttpServletRequest; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.service.ResourceManager; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.ImageData; -import org.eclipse.swt.layout.RowData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Table; - -/** Static utilities for the CMS framework. */ -public class CmsUiUtils { - // private final static Log log = LogFactory.getLog(CmsUiUtils.class); - - /* - * CMS VIEW - */ - - /** - * The CMS view related to this display, or null if none is available from this - * call. - * - * @deprecated Use {@link CmsSwtUtils#getCmsView(Composite)} instead. - */ - @Deprecated - public static CmsView getCmsView() { -// return UiContext.getData(CmsView.class.getName()); - return CmsSwtUtils.getCmsView(Display.getCurrent().getActiveShell()); - } - - public static StringBuilder getServerBaseUrl(HttpServletRequest request) { - try { - URL url = new URL(request.getRequestURL().toString()); - StringBuilder buf = new StringBuilder(); - buf.append(url.getProtocol()).append("://").append(url.getHost()); - if (url.getPort() != -1) - buf.append(':').append(url.getPort()); - return buf; - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Cannot extract server base URL from " + request.getRequestURL(), e); - } - } - - // - public static String getDataUrl(Node node, HttpServletRequest request) { - try { - StringBuilder buf = getServerBaseUrl(request); - buf.append(getDataPath(node)); - return new URL(buf.toString()).toString(); - } catch (MalformedURLException e) { - throw new IllegalArgumentException("Cannot build data URL for " + node, e); - } - } - - /** A path in the node repository */ - public static String getDataPath(Node node) { - return getDataPath(CmsConstants.EGO_REPOSITORY, node); - } - - public static String getDataPath(String cn, Node node) { - return CmsJcrUtils.getDataPath(cn, node); - } - - /** Clean reserved URL characters for use in HTTP links. */ - public static String getDataPathForUrl(Node node) { - return CmsSwtUtils.cleanPathForUrl(getDataPath(node)); - } - - /** @deprecated Use rowData16px() instead. GridData should not be reused. */ - @Deprecated - public static RowData ROW_DATA_16px = new RowData(16, 16); - - - - /* - * FORM LAYOUT - */ - - - - @Deprecated - public static void setItemHeight(Table table, int height) { - table.setData(CmsUiConstants.ITEM_HEIGHT, height); - } - - // - // JCR - // - public static Node getOrAddEmptyFile(Node parent, Enum child) throws RepositoryException { - if (has(parent, child)) - return child(parent, child); - return JcrUtils.copyBytesAsFile(parent, child.name(), new byte[0]); - } - - public static Node child(Node parent, Enum en) throws RepositoryException { - return parent.getNode(en.name()); - } - - public static Boolean has(Node parent, Enum en) throws RepositoryException { - return parent.hasNode(en.name()); - } - - public static Node getOrAdd(Node parent, Enum en) throws RepositoryException { - return getOrAdd(parent, en, null); - } - - public static Node getOrAdd(Node parent, Enum en, String primaryType) throws RepositoryException { - if (has(parent, en)) - return child(parent, en); - else if (primaryType == null) - return parent.addNode(en.name()); - else - return parent.addNode(en.name(), primaryType); - } - - // IMAGES - - public static String img(Node fileNode, String width, String height) { - return img(null, fileNode, width, height); - } - - public static String img(String serverBase, Node fileNode, String width, String height) { -// String src = (serverBase != null ? serverBase : "") + NodeUtils.getDataPath(fileNode); - String src; - src = (serverBase != null ? serverBase : "") + getDataPathForUrl(fileNode); - return imgBuilder(src, width, height).append("/>").toString(); - } - - public static String img(String src, String width, String height) { - return imgBuilder(src, width, height).append("/>").toString(); - } - - public static String img(String src, Cms2DSize size) { - return img(src, Integer.toString(size.getWidth()), Integer.toString(size.getHeight())); - } - - public static StringBuilder imgBuilder(String src, String width, String height) { - return new StringBuilder(64).append(" { - private final static CmsLog log = CmsLog.getLog(DefaultImageManager.class); -// private MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap(); - - public Boolean load(Node node, Control control, Cms2DSize preferredSize) { - Cms2DSize imageSize = getImageSize(node); - Cms2DSize size; - String imgTag = null; - if (preferredSize == null || imageSize.getWidth() == 0 || imageSize.getHeight() == 0 - || (preferredSize.getWidth() == 0 && preferredSize.getHeight() == 0)) { - if (imageSize.getWidth() != 0 && imageSize.getHeight() != 0) { - // actual image size if completely known - size = imageSize; - } else { - // no image if not completely known - size = resizeTo(NO_IMAGE_SIZE, preferredSize != null ? preferredSize : imageSize); - imgTag = CmsUiUtils.noImg(size); - } - - } else if (preferredSize.getWidth() != 0 && preferredSize.getHeight() != 0) { - // given size if completely provided - size = preferredSize; - } else { - // at this stage : - // image is completely known - assert imageSize.getWidth() != 0 && imageSize.getHeight() != 0; - // one and only one of the dimension as been specified - assert preferredSize.getWidth() == 0 || preferredSize.getHeight() == 0; - size = resizeTo(imageSize, preferredSize); - } - - boolean loaded = false; - if (control == null) - return loaded; - - if (control instanceof Label) { - if (imgTag == null) { - // IMAGE RETRIEVED HERE - imgTag = getImageTag(node, size); - // - if (imgTag == null) - imgTag = CmsUiUtils.noImg(size); - else - loaded = true; - } - - Label lbl = (Label) control; - lbl.setText(imgTag); - // lbl.setSize(size); - } else if (control instanceof FileUpload) { - FileUpload lbl = (FileUpload) control; - lbl.setImage(CmsUiUtils.noImage(size)); - lbl.setSize(new Point(size.getWidth(), size.getHeight())); - return loaded; - } else - loaded = false; - - return loaded; - } - - private Cms2DSize resizeTo(Cms2DSize orig, Cms2DSize constraints) { - if (constraints.getWidth() != 0 && constraints.getHeight() != 0) { - return constraints; - } else if (constraints.getWidth() == 0 && constraints.getHeight() == 0) { - return orig; - } else if (constraints.getHeight() == 0) {// force width - return new Cms2DSize(constraints.getWidth(), - scale(orig.getHeight(), orig.getWidth(), constraints.getWidth())); - } else if (constraints.getWidth() == 0) {// force height - return new Cms2DSize(scale(orig.getWidth(), orig.getHeight(), constraints.getHeight()), - constraints.getHeight()); - } - throw new IllegalArgumentException("Cannot resize " + orig + " to " + constraints); - } - - private int scale(int origDimension, int otherDimension, int otherConstraint) { - return Math.round(origDimension * divide(otherConstraint, otherDimension)); - } - - private float divide(int a, int b) { - return ((float) a) / ((float) b); - } - - public Cms2DSize getImageSize(Node node) { - // TODO optimise - Image image = getSwtImage(node); - return new Cms2DSize(image.getBounds().width, image.getBounds().height); - } - - /** @return null if not available */ - @Override - public String getImageTag(Node node) { - return getImageTag(node, getImageSize(node)); - } - - private String getImageTag(Node node, Cms2DSize size) { - StringBuilder buf = getImageTagBuilder(node, size); - if (buf == null) - return null; - return buf.append("/>").toString(); - } - - /** @return null if not available */ - @Override - public StringBuilder getImageTagBuilder(Node node, Cms2DSize size) { - return getImageTagBuilder(node, Integer.toString(size.getWidth()), Integer.toString(size.getHeight())); - } - - /** @return null if not available */ - private StringBuilder getImageTagBuilder(Node node, String width, String height) { - String url = getImageUrl(node); - if (url == null) - return null; - return CmsUiUtils.imgBuilder(url, width, height); - } - - /** @return null if not available */ - @Override - public String getImageUrl(Node node) { - return CmsUiUtils.getDataPathForUrl(node); - } - - protected String getResourceName(Node node) { - try { - String workspace = node.getSession().getWorkspace().getName(); - if (node.hasNode(JCR_CONTENT)) - return workspace + '_' + node.getNode(JCR_CONTENT).getIdentifier(); - else - return workspace + '_' + node.getIdentifier(); - } catch (RepositoryException e) { - throw new JcrException(e); - } - } - - public Binary getImageBinary(Node node) { - try { - if (node.isNodeType(NT_FILE)) { - return node.getNode(JCR_CONTENT).getProperty(JCR_DATA).getBinary(); - } else { - return null; - } - } catch (RepositoryException e) { - throw new JcrException(e); - } - } - - public Image getSwtImage(Node node) { - InputStream inputStream = null; - Binary binary = getImageBinary(node); - if (binary == null) - return null; - try { - inputStream = binary.getStream(); - return new Image(Display.getCurrent(), inputStream); - } catch (RepositoryException e) { - throw new JcrException(e); - } finally { - IOUtils.closeQuietly(inputStream); - JcrUtils.closeQuietly(binary); - } - } - - @Override - public String uploadImage(Node context, Node parentNode, String fileName, InputStream in, String contentType) { - InputStream inputStream = null; - try { - String previousResourceName = null; - if (parentNode.hasNode(fileName)) { - Node node = parentNode.getNode(fileName); - previousResourceName = getResourceName(node); - if (node.hasNode(JCR_CONTENT)) { - node.getNode(JCR_CONTENT).remove(); - node.addNode(JCR_CONTENT, NT_RESOURCE); - } - } - - byte[] arr = IOUtils.toByteArray(in); - Node fileNode = JcrUtils.copyBytesAsFile(parentNode, fileName, arr); - inputStream = new ByteArrayInputStream(arr); - ImageData id = new ImageData(inputStream); - processNewImageFile(context, fileNode, id); - - String mime = contentType != null ? contentType : Files.probeContentType(Paths.get(fileName)); - if (mime != null) { - fileNode.getNode(JCR_CONTENT).setProperty(Property.JCR_MIMETYPE, mime); - } - fileNode.getSession().save(); - - // reset resource manager - ResourceManager resourceManager = RWT.getResourceManager(); - if (previousResourceName != null && resourceManager.isRegistered(previousResourceName)) { - resourceManager.unregister(previousResourceName); - if (log.isDebugEnabled()) - log.debug("Unregistered image " + previousResourceName); - } - return CmsUiUtils.getDataPath(fileNode); - } catch (IOException e) { - throw new RuntimeException("Cannot upload image " + fileName + " in " + parentNode, e); - } catch (RepositoryException e) { - throw new JcrException(e); - } finally { - IOUtils.closeQuietly(inputStream); - } - } - - /** Does nothing by default. */ - protected void processNewImageFile(Node context, Node fileNode, ImageData id) - throws RepositoryException, IOException { - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/MenuLink.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/MenuLink.java deleted file mode 100644 index 284d2bd0c..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/MenuLink.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.argeo.cms.ui.util; - -import org.argeo.cms.swt.CmsStyles; - -/** - * Convenience class setting the custom style {@link CmsStyles#CMS_MENU_LINK} on - * a {@link CmsLink} when simple menus are used. - */ -public class MenuLink extends CmsLink { - public MenuLink() { - setCustom(CmsStyles.CMS_MENU_LINK); - } - - public MenuLink(String label, String target, String custom) { - super(label, target, custom); - } - - public MenuLink(String label, String target) { - super(label, target, CmsStyles.CMS_MENU_LINK); - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java deleted file mode 100644 index ab6a29f7d..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleCmsHeader.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** A header in three parts */ -public class SimpleCmsHeader implements CmsUiProvider { - private List lead = new ArrayList(); - private List center = new ArrayList(); - private List end = new ArrayList(); - - private Boolean subPartsSameWidth = false; - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Composite header = new Composite(parent, SWT.NONE); - header.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_HEADER); - header.setBackgroundMode(SWT.INHERIT_DEFAULT); - header.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false))); - - configurePart(context, header, lead); - configurePart(context, header, center); - configurePart(context, header, end); - return header; - } - - protected void configurePart(Node context, Composite parent, List partProviders) - throws RepositoryException { - final int style; - final String custom; - if (lead == partProviders) { - style = SWT.LEAD; - custom = CmsStyles.CMS_HEADER_LEAD; - } else if (center == partProviders) { - style = SWT.CENTER; - custom = CmsStyles.CMS_HEADER_CENTER; - } else if (end == partProviders) { - style = SWT.END; - custom = CmsStyles.CMS_HEADER_END; - } else { - throw new CmsException("Unsupported part providers " + partProviders); - } - - Composite part = new Composite(parent, SWT.NONE); - part.setData(RWT.CUSTOM_VARIANT, custom); - GridData gridData = new GridData(style, SWT.FILL, true, true); - part.setLayoutData(gridData); - part.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(partProviders.size(), subPartsSameWidth))); - for (CmsUiProvider uiProvider : partProviders) { - Control subPart = uiProvider.createUi(part, context); - subPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - } - } - - public void setLead(List lead) { - this.lead = lead; - } - - public void setCenter(List center) { - this.center = center; - } - - public void setEnd(List end) { - this.end = end; - } - - public void setSubPartsSameWidth(Boolean subPartsSameWidth) { - this.subPartsSameWidth = subPartsSameWidth; - } - - public List getLead() { - return lead; - } - - public List getCenter() { - return center; - } - - public List getEnd() { - return end; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java deleted file mode 100644 index c61a2fc9e..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleDynamicPages.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class SimpleDynamicPages implements CmsUiProvider { - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - if (context == null) - throw new CmsException("Context cannot be null"); - parent.setLayout(new GridLayout(2, false)); - - // parent - if (!context.getPath().equals("/")) { - new CmsLink("..", context.getParent().getPath()).createUi(parent, - context); - new Label(parent, SWT.NONE).setText(context.getParent() - .getPrimaryNodeType().getName()); - } - - // context - Label contextL = new Label(parent, SWT.NONE); - contextL.setData(RWT.MARKUP_ENABLED, true); - contextL.setText("" + context.getName() + ""); - new Label(parent, SWT.NONE).setText(context.getPrimaryNodeType() - .getName()); - - // children - // Label childrenL = new Label(parent, SWT.NONE); - // childrenL.setData(RWT.MARKUP_ENABLED, true); - // childrenL.setText("Children:"); - // childrenL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, - // false, 2, 1)); - - for (NodeIterator nIt = context.getNodes(); nIt.hasNext();) { - Node child = nIt.nextNode(); - new CmsLink(child.getName(), child.getPath()).createUi(parent, - context); - - new Label(parent, SWT.NONE).setText(child.getPrimaryNodeType() - .getName()); - } - - // properties - // Label propsL = new Label(parent, SWT.NONE); - // propsL.setData(RWT.MARKUP_ENABLED, true); - // propsL.setText("Properties:"); - // propsL.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false, - // 2, 1)); - for (PropertyIterator pIt = context.getProperties(); pIt.hasNext();) { - Property property = pIt.nextProperty(); - - Label label = new Label(parent, SWT.NONE); - label.setText(property.getName()); - label.setToolTipText(JcrUtils - .getPropertyDefinitionAsString(property)); - - new Label(parent, SWT.NONE).setText(getPropAsString(property)); - } - - return null; - } - - private String getPropAsString(Property property) - throws RepositoryException { - String result = ""; - DateFormat timeFormatter = new SimpleDateFormat(""); - if (property.isMultiple()) { - result = getMultiAsString(property, ", "); - } else { - Value value = property.getValue(); - if (value.getType() == PropertyType.BINARY) - result = ""; - else if (value.getType() == PropertyType.DATE) - result = timeFormatter.format(value.getDate().getTime()); - else - result = value.getString(); - } - return result; - } - - private String getMultiAsString(Property property, String separator) - throws RepositoryException { - if (separator == null) - separator = "; "; - Value[] values = property.getValues(); - StringBuilder builder = new StringBuilder(); - for (Value val : values) { - String currStr = val.getString(); - if (!"".equals(currStr.trim())) - builder.append(currStr).append(separator); - } - if (builder.lastIndexOf(separator) >= 0) - return builder.substring(0, builder.length() - separator.length()); - else - return builder.toString(); - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleImageManager.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleImageManager.java deleted file mode 100644 index ac09b2a02..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleImageManager.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.argeo.cms.ui.util; - -public class SimpleImageManager extends DefaultImageManager { - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java deleted file mode 100644 index 63e504b7a..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStaticPage.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.argeo.cms.ui.util; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -public class SimpleStaticPage implements CmsUiProvider { - private String text; - - @Override - public Control createUi(Composite parent, Node context) - throws RepositoryException { - Label textC = new Label(parent, SWT.WRAP); - textC.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_STATIC_TEXT); - textC.setData(RWT.MARKUP_ENABLED, Boolean.TRUE); - textC.setText(text); - - return textC; - } - - public void setText(String text) { - this.text = text; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStyle.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStyle.java deleted file mode 100644 index b5fca2699..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SimpleStyle.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.util; - -import org.argeo.api.cms.ux.CmsStyle; - -/** Simple styles used by the CMS UI utilities. */ -public enum SimpleStyle implements CmsStyle { - link; -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java deleted file mode 100644 index 1e17dc930..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/StyleSheetResourceLoader.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.io.IOUtils; -import org.argeo.cms.swt.CmsException; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; - -/** {@link ResourceLoader} caching stylesheets. */ -public class StyleSheetResourceLoader implements ResourceLoader { - private Bundle themeBundle; - private Map stylesheets = new LinkedHashMap(); - - public StyleSheetResourceLoader(Bundle themeBundle) { - this.themeBundle = themeBundle; - } - - @Override - public InputStream getResourceAsStream(String resourceName) throws IOException { - if (!stylesheets.containsKey(resourceName)) { - // TODO deal with other bundles - // Bundle bundle = bundleContext.getBundle(); - // String location = - // bundle.getLocation().substring("initial@reference:".length()); - // if (location.startsWith("file:")) { - // Path path = null; - // try { - // path = Paths.get(new URI(location)); - // } catch (URISyntaxException e) { - // e.printStackTrace(); - // } - // if (path != null) { - // Path resourcePath = path.resolve(resourceName); - // if (Files.exists(resourcePath)) - // return Files.newInputStream(resourcePath); - // } - // } - - URL res = themeBundle.getEntry(resourceName); - if (res == null) - throw new CmsException( - "Entry " + resourceName + " not found in bundle " + themeBundle.getSymbolicName()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(res.openStream(), out); - stylesheets.put(resourceName, new StyleSheet(out.toByteArray())); - } - return new ByteArrayInputStream(stylesheets.get(resourceName).getData()); - // return res.openStream(); - } - - private class StyleSheet { - private byte[] data; - - public StyleSheet(byte[] data) { - super(); - this.data = data; - } - - public byte[] getData() { - return data; - } - - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SystemNotifications.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SystemNotifications.java deleted file mode 100644 index 5a0078163..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/SystemNotifications.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.apache.commons.io.IOUtils; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** Shell displaying system notifications such as exceptions */ -public class SystemNotifications extends Shell implements CmsStyles, - MouseListener { - private static final long serialVersionUID = -8129377525216022683L; - - private Control source; - - public SystemNotifications(Control source) { - super(source.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - setData(RWT.CUSTOM_VARIANT, CMS_USER_MENU); - - this.source = source; - - // TODO UI - // setLocation(source.toDisplay(source.getSize().x - getSize().x, - // source.getSize().y)); - setLayout(new GridLayout()); - addMouseListener(this); - - addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - close(); - dispose(); - } - }); - - } - - public void notifyException(Throwable exception) { - Composite pane = this; - - Label lbl = new Label(pane, SWT.NONE); - lbl.setText(exception.getLocalizedMessage() - + (exception instanceof CmsException ? "" : "(" - + exception.getClass().getName() + ")") + "\n"); - lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - lbl.addMouseListener(this); - if (exception.getCause() != null) - appendCause(pane, exception.getCause()); - - StringBuilder mailToUrl = new StringBuilder("mailto:?"); - try { - mailToUrl.append("subject=").append( - URLEncoder.encode( - "Exception " - + new SimpleDateFormat("yyyy-MM-dd hh:mm") - .format(new Date()), "UTF-8") - .replace("+", "%20")); - - StringWriter sw = new StringWriter(); - exception.printStackTrace(new PrintWriter(sw)); - IOUtils.closeQuietly(sw); - - // see - // http://stackoverflow.com/questions/4737841/urlencoder-not-able-to-translate-space-character - String encoded = URLEncoder.encode(sw.toString(), "UTF-8").replace( - "+", "%20"); - mailToUrl.append("&body=").append(encoded); - } catch (UnsupportedEncodingException e) { - mailToUrl.append("&body=").append("Could not encode: ") - .append(e.getMessage()); - } - Label mailTo = new Label(pane, SWT.NONE); - CmsSwtUtils.markup(mailTo); - mailTo.setText("Send details"); - mailTo.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false)); - - pack(); - layout(); - - setLocation(source.toDisplay(source.getSize().x - getSize().x, - source.getSize().y - getSize().y)); - open(); - } - - private void appendCause(Composite parent, Throwable e) { - Label lbl = new Label(parent, SWT.NONE); - lbl.setText(" caused by: " + e.getLocalizedMessage() + " (" - + e.getClass().getName() + ")" + "\n"); - lbl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - lbl.addMouseListener(this); - if (e.getCause() != null) - appendCause(parent, e.getCause()); - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - } - - @Override - public void mouseDown(MouseEvent e) { - close(); - dispose(); - } - - @Override - public void mouseUp(MouseEvent e) { - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenu.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenu.java deleted file mode 100644 index 09aeff60f..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenu.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.argeo.cms.ui.util; - -import javax.jcr.Node; - -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.auth.CmsLoginShell; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ShellAdapter; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** The site-related user menu */ -public class UserMenu extends CmsLoginShell { - private final Control source; - private final Node context; - - public UserMenu(Control source, Node context) { - // FIXME pass CMS context - super(CmsUiUtils.getCmsView(), null); - this.context = context; - createUi(); - if (source == null) - throw new CmsException("Source control cannot be null."); - this.source = source; - open(); - } - - @Override - protected Shell createShell() { - return new Shell(Display.getCurrent(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - } - - @Override - public void open() { - Shell shell = getShell(); - shell.pack(); - shell.layout(); - shell.setLocation(source.toDisplay(source.getSize().x - shell.getSize().x, source.getSize().y)); - shell.addShellListener(new ShellAdapter() { - private static final long serialVersionUID = 5178980294808435833L; - - @Override - public void shellDeactivated(ShellEvent e) { - closeShell(); - } - }); - super.open(); - } - - protected Node getContext() { - return context; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenuLink.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenuLink.java deleted file mode 100644 index 317a7b55b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/UserMenuLink.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.argeo.cms.ui.util; - -import javax.jcr.Node; - -import org.argeo.cms.CmsMsg; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.auth.CmsLoginShell; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; - -/** Open the user menu when clicked */ -public class UserMenuLink extends MenuLink { - - public UserMenuLink() { - setCustom(CmsStyles.CMS_USER_MENU_LINK); - } - - @Override - public Control createUi(Composite parent, Node context) { - if (CurrentUser.isAnonymous()) - setLabel(CmsMsg.login.lead()); - else { - setLabel(CurrentUser.getDisplayName()); - } - Label link = (Label) ((Composite) super.createUi(parent, context)).getChildren()[0]; - link.addMouseListener(new UserMenuLinkController(context)); - return link.getParent(); - } - - protected CmsLoginShell createUserMenu(Control source, Node context) { - return new UserMenu(source.getParent(), context); - } - - private class UserMenuLinkController implements MouseListener, DisposeListener { - private static final long serialVersionUID = 3634864186295639792L; - - private CmsLoginShell userMenu = null; - private long lastDisposeTS = 0l; - - private final Node context; - - public UserMenuLinkController(Node context) { - this.context = context; - } - - // - // MOUSE LISTENER - // - @Override - public void mouseDown(MouseEvent e) { - if (e.button == 1) { - Control source = (Control) e.getSource(); - if (userMenu == null) { - long durationSinceLastDispose = System.currentTimeMillis() - lastDisposeTS; - // avoid to reopen the menu, if one has clicked gain - if (durationSinceLastDispose > 200) { - userMenu = createUserMenu(source, context); - userMenu.getShell().addDisposeListener(this); - } - } - } - } - - @Override - public void mouseDoubleClick(MouseEvent e) { - } - - @Override - public void mouseUp(MouseEvent e) { - } - - @Override - public void widgetDisposed(DisposeEvent event) { - userMenu = null; - lastDisposeTS = System.currentTimeMillis(); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/VerticalMenu.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/VerticalMenu.java deleted file mode 100644 index 7f846c932..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/VerticalMenu.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.argeo.cms.ui.util; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -public class VerticalMenu implements CmsUiProvider { - private List items = new ArrayList(); - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - Composite part = new Composite(parent, SWT.NONE); - part.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); -// part.setData(RWT.CUSTOM_VARIANT, custom); - part.setLayout(CmsSwtUtils.noSpaceGridLayout()); - for (CmsUiProvider uiProvider : items) { - Control subPart = uiProvider.createUi(part, context); - subPart.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); - } - return part; - } - - public void add(CmsUiProvider uiProvider) { - items.add(uiProvider); - } - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/package-info.java deleted file mode 100644 index 566df883c..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/util/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS UI utilities. */ -package org.argeo.cms.ui.util; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java deleted file mode 100644 index e23846e92..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java +++ /dev/null @@ -1,351 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Observable; -import java.util.Observer; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.security.auth.Subject; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsEditable; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.swt.widgets.ScrolledPage; -import org.argeo.jcr.JcrException; -import org.eclipse.jface.viewers.ContentViewer; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Widget; -import org.xml.sax.SAXParseException; - -/** Base class for viewers related to a page */ -public abstract class AbstractPageViewer extends ContentViewer implements Observer { - private static final long serialVersionUID = 5438688173410341485L; - - private final static CmsLog log = CmsLog.getLog(AbstractPageViewer.class); - - private final boolean readOnly; - /** The basis for the layouts, typically a ScrolledPage. */ - private final Composite page; - private final CmsEditable cmsEditable; - - private MouseListener mouseListener; - private FocusListener focusListener; - - private SwtEditablePart edited; - private ISelection selection = StructuredSelection.EMPTY; - - private AccessControlContext accessControlContext; - - protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) { - // read only at UI level - readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY); - - this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE : cmsEditable; - if (this.cmsEditable instanceof Observable) - ((Observable) this.cmsEditable).addObserver(this); - - if (cmsEditable.canEdit()) { - mouseListener = createMouseListener(); - focusListener = createFocusListener(); - } - page = findPage(parent); - accessControlContext = AccessController.getContext(); - } - - /** - * Can be called to simplify the called to isModelInitialized() and initModel() - */ - protected void initModelIfNeeded(Node node) { - try { - if (!isModelInitialized(node)) - if (getCmsEditable().canEdit()) { - initModel(node); - node.getSession().save(); - } - } catch (RepositoryException e) { - throw new JcrException("Cannot initialize model", e); - } - } - - /** Called if user can edit and model is not initialized */ - protected Boolean isModelInitialized(Node node) throws RepositoryException { - return true; - } - - /** Called if user can edit and model is not initialized */ - protected void initModel(Node node) throws RepositoryException { - } - - /** Create (retrieve) the MouseListener to use. */ - protected MouseListener createMouseListener() { - return new MouseAdapter() { - private static final long serialVersionUID = 1L; - }; - } - - /** Create (retrieve) the FocusListener to use. */ - protected FocusListener createFocusListener() { - return new FocusListener() { - private static final long serialVersionUID = 1L; - - @Override - public void focusLost(FocusEvent event) { - } - - @Override - public void focusGained(FocusEvent event) { - } - }; - } - - protected Composite findPage(Composite composite) { - if (composite instanceof ScrolledPage) { - return (ScrolledPage) composite; - } else { - if (composite.getParent() == null) - return composite; - return findPage(composite.getParent()); - } - } - - public void layoutPage() { - if (page != null) - page.layout(true, true); - } - - protected void showControl(Control control) { - if (page != null && (page instanceof ScrolledPage)) - ((ScrolledPage) page).showControl(control); - } - - @Override - public void update(Observable o, Object arg) { - if (o == cmsEditable) - editingStateChanged(cmsEditable); - } - - /** To be overridden in order to provide the actual refresh */ - protected void refresh(Control control) throws RepositoryException { - } - - /** To be overridden.Save the edited part. */ - protected void save(SwtEditablePart part) throws RepositoryException { - } - - /** Prepare the edited part */ - protected void prepare(SwtEditablePart part, Object caretPosition) { - } - - /** Notified when the editing state changed. Does nothing, to be overridden */ - protected void editingStateChanged(CmsEditable cmsEditable) { - } - - @Override - public void refresh() { - // TODO check actual context in order to notice a discrepancy - Subject viewerSubject = getViewerSubject(); - Subject.doAs(viewerSubject, (PrivilegedAction) () -> { - try { - if (cmsEditable.canEdit() && !readOnly) - mouseListener = createMouseListener(); - else - mouseListener = null; - refresh(getControl()); - // layout(getControl()); - if (!getControl().isDisposed()) - layoutPage(); - } catch (RepositoryException e) { - throw new JcrException("Cannot refresh", e); - } - return null; - }); - } - - @Override - public void setSelection(ISelection selection, boolean reveal) { - this.selection = selection; - } - - protected void updateContent(SwtEditablePart part) throws RepositoryException { - } - - // LOW LEVEL EDITION - protected void edit(SwtEditablePart part, Object caretPosition) { - try { - if (edited == part) - return; - - if (edited != null && edited != part) { - SwtEditablePart previouslyEdited = edited; - try { - stopEditing(true); - } catch (Exception e) { - notifyEditionException(e); - edit(previouslyEdited, caretPosition); - return; - } - } - - part.startEditing(); - edited = part; - updateContent(part); - prepare(part, caretPosition); - edited.getControl().addFocusListener(new FocusListener() { - private static final long serialVersionUID = 6883521812717097017L; - - @Override - public void focusLost(FocusEvent event) { - stopEditing(true); - } - - @Override - public void focusGained(FocusEvent event) { - } - }); - - layout(part.getControl()); - showControl(part.getControl()); - } catch (RepositoryException e) { - throw new JcrException("Cannot edit " + part, e); - } - } - - protected void stopEditing(Boolean save) { - if (edited instanceof Widget && ((Widget) edited).isDisposed()) { - edited = null; - return; - } - - assert edited != null; - if (edited == null) { - if (log.isTraceEnabled()) - log.warn("Told to stop editing while not editing anything"); - return; - } - - try { - if (save) - save(edited); - - edited.stopEditing(); - SwtEditablePart editablePart = edited; - Control control = ((SwtEditablePart) edited).getControl(); - edited = null; - // TODO make edited state management more robust - updateContent(editablePart); - layout(control); - } catch (RepositoryException e) { - throw new JcrException("Cannot stop editing", e); - } finally { - edited = null; - } - } - - // METHODS AVAILABLE TO EXTENDING CLASSES - protected void saveEdit() { - if (edited != null) - stopEditing(true); - } - - protected void cancelEdit() { - if (edited != null) - stopEditing(false); - } - - /** Layout this controls from the related base page. */ - public void layout(Control... controls) { - page.layout(controls); - } - - /** - * Find the first {@link SwtEditablePart} in the parents hierarchy of this control - */ - protected SwtEditablePart findDataParent(Control parent) { - if (parent instanceof SwtEditablePart) { - return (SwtEditablePart) parent; - } - if (parent.getParent() != null) - return findDataParent(parent.getParent()); - else - throw new IllegalStateException("No data parent found"); - } - - // UTILITIES - /** Check whether the edited part is in a proper state */ - protected void checkEdited() { - if (edited == null || (edited instanceof Widget) && ((Widget) edited).isDisposed()) - throw new IllegalStateException("Edited should not be null or disposed at this stage"); - } - - /** Persist all changes. */ - protected void persistChanges(Session session) throws RepositoryException { - session.save(); - session.refresh(false); - // TODO notify that changes have been persisted - } - - /** Convenience method using a Node in order to save the underlying session. */ - protected void persistChanges(Node anyNode) throws RepositoryException { - persistChanges(anyNode.getSession()); - } - - /** Notify edition exception */ - protected void notifyEditionException(Throwable e) { - Throwable eToLog = e; - if (e instanceof IllegalArgumentException) - if (e.getCause() instanceof SAXParseException) - eToLog = e.getCause(); - log.error(eToLog.getMessage(), eToLog); -// if (log.isTraceEnabled()) -// log.trace("Full stack of " + eToLog.getMessage(), e); - // TODO Light error notification popup - } - - protected Subject getViewerSubject() { - Subject res = null; - if (accessControlContext != null) { - res = Subject.getSubject(accessControlContext); - } - if (res == null) - throw new IllegalStateException("No subject associated with this viewer"); - return res; - } - - // GETTERS / SETTERS - public boolean isReadOnly() { - return readOnly; - } - - protected SwtEditablePart getEdited() { - return edited; - } - - public MouseListener getMouseListener() { - return mouseListener; - } - - public FocusListener getFocusListener() { - return focusListener; - } - - public CmsEditable getCmsEditable() { - return cmsEditable; - } - - @Override - public ISelection getSelection() { - return selection; - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/ItemPart.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/ItemPart.java deleted file mode 100644 index 4ca45d191..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/ItemPart.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -/** An editable part related to a JCR Item */ -public interface ItemPart { - public Item getItem() throws RepositoryException; -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java deleted file mode 100644 index 298fbdea9..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/JcrVersionCmsEditable.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.jcr.version.VersionManager; - -import org.argeo.api.cms.ux.CmsEditionEvent; -import org.argeo.cms.ux.AbstractCmsEditable; -import org.argeo.jcr.JcrException; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; - -/** Provides the CmsEditable semantic based on JCR versioning. */ -public class JcrVersionCmsEditable extends AbstractCmsEditable { - private final String nodePath;// cache - private final VersionManager versionManager; - private final Boolean canEdit; - - public JcrVersionCmsEditable(Node node) throws RepositoryException { - this.nodePath = node.getPath(); - if (node.getSession().hasPermission(node.getPath(), Session.ACTION_SET_PROPERTY)) { - // was Session.ACTION_ADD_NODE - canEdit = true; - if (!node.isNodeType(NodeType.MIX_VERSIONABLE)) { - node.addMixin(NodeType.MIX_VERSIONABLE); - node.getSession().save(); - } - versionManager = node.getSession().getWorkspace().getVersionManager(); - } else { - canEdit = false; - versionManager = null; - } - - // bind keys - if (canEdit) { - Display display = Display.getCurrent(); - display.setData(RWT.ACTIVE_KEYS, new String[] { "CTRL+RETURN", "CTRL+E" }); - display.addFilter(SWT.KeyDown, new Listener() { - private static final long serialVersionUID = -4378653870463187318L; - - public void handleEvent(Event e) { - boolean ctrlPressed = (e.stateMask & SWT.CTRL) != 0; - if (ctrlPressed && e.keyCode == '\r') - stopEditing(); - else if (ctrlPressed && e.keyCode == 'E') - stopEditing(); - } - }); - } - } - - @Override - public Boolean canEdit() { - return canEdit; - } - - public Boolean isEditing() { - try { - if (!canEdit()) - return false; - return versionManager.isCheckedOut(nodePath); - } catch (RepositoryException e) { - throw new JcrException("Cannot check whether " + nodePath + " is editing", e); - } - } - - @Override - public void startEditing() { - try { - versionManager.checkout(nodePath); -// setChanged(); - } catch (RepositoryException e1) { - throw new JcrException("Cannot publish " + nodePath, e1); - } - notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.START_EDITING, this)); - } - - @Override - public void stopEditing() { - try { - versionManager.checkin(nodePath); -// setChanged(); - } catch (RepositoryException e1) { - throw new JcrException("Cannot publish " + nodePath, e1); - } - notifyListeners(new CmsEditionEvent(nodePath, CmsEditionEvent.STOP_EDITING, this)); - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/NodePart.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/NodePart.java deleted file mode 100644 index b51d4fcba..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/NodePart.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Node; - -/** An editable part related to a node */ -public interface NodePart extends ItemPart { - public Node getNode(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java deleted file mode 100644 index 793079e20..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/PropertyPart.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import javax.jcr.Property; - -/** An editable part related to a JCR Property */ -public interface PropertyPart extends ItemPart { - public Property getProperty(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java deleted file mode 100644 index b27fa3845..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/Section.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SwtEditablePart; -import org.argeo.cms.ui.widgets.JcrComposite; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** A structured UI related to a JCR context. */ -public class Section extends JcrComposite { - private static final long serialVersionUID = -5933796173755739207L; - - private final Section parentSection; - private Composite sectionHeader; - private final Integer relativeDepth; - - public Section(Composite parent, int style, Node node) { - this(parent, findSection(parent), style, node); - } - - public Section(Section section, int style, Node node) { - this(section, section, style, node); - } - - protected Section(Composite parent, Section parentSection, int style, Node node) { - super(parent, style, node); - try { - this.parentSection = parentSection; - if (parentSection != null) { - relativeDepth = getNode().getDepth() - parentSection.getNode().getDepth(); - } else { - relativeDepth = 0; - } - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } catch (RepositoryException e) { - throw new IllegalStateException("Cannot create section from " + node, e); - } - } - - public Map getSubSections() throws RepositoryException { - LinkedHashMap result = new LinkedHashMap(); - for (Control child : getChildren()) { - if (child instanceof Composite) { - collectDirectSubSections((Composite) child, result); - } - } - return Collections.unmodifiableMap(result); - } - - private void collectDirectSubSections(Composite composite, LinkedHashMap subSections) - throws RepositoryException { - if (composite == sectionHeader || composite instanceof SwtEditablePart) - return; - if (composite instanceof Section) { - Section section = (Section) composite; - subSections.put(section.getNodeId(), section); - return; - } - - for (Control child : composite.getChildren()) - if (child instanceof Composite) - collectDirectSubSections((Composite) child, subSections); - } - - public Composite createHeader() { - return createHeader(this); - } - - public Composite createHeader(Composite parent) { - if (sectionHeader != null) - sectionHeader.dispose(); - - sectionHeader = new Composite(parent, SWT.NONE); - sectionHeader.setLayoutData(CmsSwtUtils.fillWidth()); - sectionHeader.setLayout(CmsSwtUtils.noSpaceGridLayout()); - // sectionHeader.moveAbove(null); - // layout(); - return sectionHeader; - } - - public Composite getHeader() { - if (sectionHeader != null && sectionHeader.isDisposed()) - sectionHeader = null; - return sectionHeader; - } - - // SECTION PARTS - public SectionPart getSectionPart(String partId) { - for (Control child : getChildren()) { - if (child instanceof SectionPart) { - SectionPart sectionPart = (SectionPart) child; - if (sectionPart.getPartId().equals(partId)) - return sectionPart; - } - } - return null; - } - - public SectionPart nextSectionPart(SectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) { - for (int j = i + 1; j < children.length; j++) { - if (children[i + 1] instanceof SectionPart) { - return (SectionPart) children[i + 1]; - } - } - -// if (i + 1 < children.length) { -// Composite next = (Composite) children[i + 1]; -// return (SectionPart) next; -// } else { -// // next section -// } - } - } - return null; - } - - public SectionPart previousSectionPart(SectionPart sectionPart) { - Control[] children = getChildren(); - for (int i = 0; i < children.length; i++) { - if (sectionPart == children[i]) - if (i != 0) { - Composite previous = (Composite) children[i - 1]; - return (SectionPart) previous; - } else { - // previous section - } - } - return null; - } - - @Override - public String toString() { - if (parentSection == null) - return "Main section " + getNode(); - return "Section " + getNode(); - } - - public Section getParentSection() { - return parentSection; - } - - public Integer getRelativeDepth() { - return relativeDepth; - } - - /** Recursively finds the related section in the parents (can be itself) */ - public static Section findSection(Control control) { - if (control == null) - return null; - if (control instanceof Section) - return (Section) control; - else - return findSection(control.getParent()); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/SectionPart.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/SectionPart.java deleted file mode 100644 index 4278c83cc..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/SectionPart.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.argeo.cms.ui.viewers; - -import org.argeo.cms.swt.SwtEditablePart; - -/** An editable part dynamically related to a Section */ -public interface SectionPart extends SwtEditablePart, NodePart { - public String getPartId(); - - public Section getSection(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/package-info.java deleted file mode 100644 index 2f0793127..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/viewers/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS generic viewers, based on JFace. */ -package org.argeo.cms.ui.viewers; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableImage.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableImage.java deleted file mode 100644 index 95d9e8ee9..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableImage.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** A stylable and editable image. */ -public abstract class EditableImage extends StyledControl { - private static final long serialVersionUID = -5689145523114022890L; - private final static CmsLog log = CmsLog.getLog(EditableImage.class); - - private Cms2DSize preferredImageSize; - private Boolean loaded = false; - - public EditableImage(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - - public EditableImage(Composite parent, int swtStyle, Cms2DSize preferredImageSize) { - super(parent, swtStyle); - this.preferredImageSize = preferredImageSize; - } - - public EditableImage(Composite parent, int style, Node node, boolean cacheImmediately, Cms2DSize preferredImageSize) - throws RepositoryException { - super(parent, style, node, cacheImmediately); - this.preferredImageSize = preferredImageSize; - } - - @Override - protected void setContainerLayoutData(Composite composite) { - // composite.setLayoutData(fillWidth()); - } - - @Override - protected void setControlLayoutData(Control control) { - // control.setLayoutData(fillWidth()); - } - - /** To be overriden. */ - protected String createImgTag() throws RepositoryException { - return CmsUiUtils - .noImg(preferredImageSize != null ? preferredImageSize : new Cms2DSize(getSize().x, getSize().y)); - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle()); - // lbl.setLayoutData(CmsUiUtils.fillWidth()); - CmsSwtUtils.markup(lbl); - CmsSwtUtils.style(lbl, style); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - load(lbl); - return lbl; - } - - /** To be overriden. */ - protected synchronized Boolean load(Control control) { - String imgTag; - try { - imgTag = createImgTag(); - } catch (Exception e) { - // throw new CmsException("Cannot retrieve image", e); - log.error("Cannot retrieve image", e); - imgTag = CmsUiUtils.noImg(preferredImageSize); - loaded = false; - } - - if (imgTag == null) { - loaded = false; - imgTag = CmsUiUtils.noImg(preferredImageSize); - } else - loaded = true; - if (control != null) { - ((Label) control).setText(imgTag); - control.setSize(preferredImageSize != null - ? new Point(preferredImageSize.getWidth(), preferredImageSize.getHeight()) - : getSize()); - } else { - loaded = false; - } - getParent().layout(); - return loaded; - } - - public void setPreferredSize(Cms2DSize size) { - this.preferredImageSize = size; - if (!loaded) { - load((Label) getControl()); - } - } - - protected Text createText(Composite box, String style) { - Text text = new Text(box, getStyle()); - CmsSwtUtils.style(text, style); - return text; - } - - public Cms2DSize getPreferredImageSize() { - return preferredImageSize; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableText.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableText.java deleted file mode 100644 index e3499ac4b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/EditableText.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** Editable text part displaying styled text. */ -public class EditableText extends StyledControl { - private static final long serialVersionUID = -6372283442330912755L; - - private boolean editable = true; - - private Color highlightColor; - private Composite highlight; - - private boolean useTextAsLabel = false; - - public EditableText(Composite parent, int style) { - super(parent, style); - editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); - highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); - } - - public EditableText(Composite parent, int style, Item item) throws RepositoryException { - this(parent, style, item, false); - } - - public EditableText(Composite parent, int style, Item item, boolean cacheImmediately) throws RepositoryException { - super(parent, style, item, cacheImmediately); - editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); - highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing() && getEditable()) { - return createText(box, style, true); - } else { - if (useTextAsLabel) { - return createTextLabel(box, style); - } else { - return createLabel(box, style); - } - } - } - - protected Label createLabel(Composite box, String style) { - Label lbl = new Label(box, getStyle() | SWT.WRAP); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - if (style != null) - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - protected Text createTextLabel(Composite box, String style) { - Text lbl = new Text(box, getStyle() | SWT.MULTI); - lbl.setEditable(false); - lbl.setLayoutData(CmsSwtUtils.fillWidth()); - if (style != null) - CmsSwtUtils.style(lbl, style); - CmsSwtUtils.markup(lbl); - if (mouseListener != null) - lbl.addMouseListener(mouseListener); - return lbl; - } - - protected Text createText(Composite box, String style, boolean editable) { - highlight = new Composite(box, SWT.NONE); - highlight.setBackground(highlightColor); - GridData highlightGd = new GridData(SWT.FILL, SWT.FILL, false, false); - highlightGd.widthHint = 5; - highlightGd.heightHint = 3; - highlight.setLayoutData(highlightGd); - - final Text text = new Text(box, getStyle() | SWT.MULTI | SWT.WRAP); - text.setEditable(editable); - GridData textLayoutData = CmsSwtUtils.fillWidth(); - // textLayoutData.heightHint = preferredHeight; - text.setLayoutData(textLayoutData); - if (style != null) - CmsSwtUtils.style(text, style); - text.setFocus(); - return text; - } - - @Override - protected void clear(boolean deep) { - if (highlight != null) - highlight.dispose(); - super.clear(deep); - } - - public void setText(String text) { - Control child = getControl(); - if (child instanceof Label) - ((Label) child).setText(text); - else if (child instanceof Text) - ((Text) child).setText(text); - } - - public Text getAsText() { - return (Text) getControl(); - } - - public Label getAsLabel() { - return (Label) getControl(); - } - - public String getText() { - Control child = getControl(); - - if (child instanceof Label) - return ((Label) child).getText(); - else if (child instanceof Text) - return ((Text) child).getText(); - else - throw new IllegalStateException("Unsupported control " + child.getClass()); - } - - /** @deprecated Use {@link #isEditable()} instead. */ - @Deprecated - public boolean getEditable() { - return isEditable(); - } - - public boolean isEditable() { - return editable; - } - - public void setUseTextAsLabel(boolean useTextAsLabel) { - this.useTextAsLabel = useTextAsLabel; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/Img.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/Img.java deleted file mode 100644 index 41063fa47..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/Img.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.ux.Cms2DSize; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.internal.JcrFileUploadReceiver; -import org.argeo.cms.ui.viewers.NodePart; -import org.argeo.cms.ui.viewers.Section; -import org.argeo.cms.ui.viewers.SectionPart; -import org.argeo.jcr.Jcr; -import org.argeo.jcr.JcrException; -import org.eclipse.rap.fileupload.FileUploadHandler; -import org.eclipse.rap.fileupload.FileUploadListener; -import org.eclipse.rap.fileupload.FileUploadReceiver; -import org.eclipse.rap.rwt.service.ServerPushSession; -import org.eclipse.rap.rwt.widgets.FileUpload; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** An image within the Argeo Text framework */ -public class Img extends EditableImage implements SectionPart, NodePart { - private static final long serialVersionUID = 6233572783968188476L; - - private final Section section; - - private final CmsImageManager imageManager; - private FileUploadHandler currentUploadHandler = null; - private FileUploadListener fileUploadListener; - - public Img(Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize) throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, preferredImageSize, null); - setStyle(TextStyles.TEXT_IMAGE); - } - - public Img(Composite parent, int swtStyle, Node imgNode) throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, null, null); - setStyle(TextStyles.TEXT_IMAGE); - } - - public Img(Composite parent, int swtStyle, Node imgNode, CmsImageManager imageManager) - throws RepositoryException { - this(Section.findSection(parent), parent, swtStyle, imgNode, null, imageManager); - setStyle(TextStyles.TEXT_IMAGE); - } - - Img(Section section, Composite parent, int swtStyle, Node imgNode, Cms2DSize preferredImageSize, - CmsImageManager imageManager) throws RepositoryException { - super(parent, swtStyle, imgNode, false, preferredImageSize); - this.section = section; - this.imageManager = imageManager != null ? imageManager - : (CmsImageManager) CmsSwtUtils.getCmsView(section).getImageManager(); - CmsSwtUtils.style(this, TextStyles.TEXT_IMG); - } - - @Override - protected Control createControl(Composite box, String style) { - if (isEditing()) { - try { - return createImageChooser(box, style); - } catch (RepositoryException e) { - throw new JcrException("Cannot create image chooser", e); - } - } else { - return createLabel(box, style); - } - } - - @Override - public synchronized void stopEditing() { - super.stopEditing(); - fileUploadListener = null; - } - - @Override - protected synchronized Boolean load(Control lbl) { - Node imgNode = getNode(); - boolean loaded = imageManager.load(imgNode, lbl, getPreferredImageSize()); - // getParent().layout(); - return loaded; - } - - protected Node getUploadFolder() { - return Jcr.getParent(getNode()); - } - - protected String getUploadName() { - Node node = getNode(); - return Jcr.getName(node) + '[' + Jcr.getIndex(node) + ']'; - } - - protected CmsImageManager getImageManager() { - return imageManager; - } - - protected Control createImageChooser(Composite box, String style) throws RepositoryException { - JcrFileUploadReceiver receiver = new JcrFileUploadReceiver(this, getUploadFolder(), getUploadName(), - imageManager); - if (currentUploadHandler != null) - currentUploadHandler.dispose(); - currentUploadHandler = prepareUpload(receiver); - final ServerPushSession pushSession = new ServerPushSession(); - final FileUpload fileUpload = new FileUpload(box, SWT.NONE); - CmsSwtUtils.style(fileUpload, style); - fileUpload.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -9158471843941668562L; - - @Override - public void widgetSelected(SelectionEvent e) { - pushSession.start(); - fileUpload.submit(currentUploadHandler.getUploadUrl()); - } - }); - return fileUpload; - } - - protected FileUploadHandler prepareUpload(FileUploadReceiver receiver) { - final FileUploadHandler uploadHandler = new FileUploadHandler(receiver); - if (fileUploadListener != null) - uploadHandler.addUploadListener(fileUploadListener); - return uploadHandler; - } - - @Override - public Section getSection() { - return section; - } - - public void setFileUploadListener(FileUploadListener fileUploadListener) { - this.fileUploadListener = fileUploadListener; - if (currentUploadHandler != null) - currentUploadHandler.addUploadListener(fileUploadListener); - } - - @Override - public Node getItem() throws RepositoryException { - return getNode(); - } - - @Override - public String getPartId() { - return getNodeId(); - } - - @Override - public String toString() { - return "Img #" + getPartId(); - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java deleted file mode 100644 index 6b54c0a7b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/JcrComposite.java +++ /dev/null @@ -1,213 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Item; -import javax.jcr.ItemNotFoundException; -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.jcr.JcrException; -import org.eclipse.swt.widgets.Composite; - -/** A composite which can (optionally) manage a JCR Item. */ -public class JcrComposite extends Composite { - private static final long serialVersionUID = -1447009015451153367L; - - private Session session; - - private String nodeId; - private String property = null; - private Node cache; - - /** Regular composite constructor. No layout is set. */ - public JcrComposite(Composite parent, int style) { - super(parent, style); - session = null; - nodeId = null; - } - - public JcrComposite(Composite parent, int style, Item item) { - this(parent, style, item, false); - } - - public JcrComposite(Composite parent, int style, Item item, boolean cacheImmediately) { - super(parent, style); - if (item != null) - try { - this.session = item.getSession(); -// if (!cacheImmediately && (SWT.READ_ONLY == (style & SWT.READ_ONLY))) { -// // (useless?) optimization: we only save a pointer to the session, -// // not even a reference to the item -// this.nodeId = null; -// } else { - Node node; - Property property = null; - if (item instanceof Node) { - node = (Node) item; - } else {// Property - property = (Property) item; - if (property.isMultiple())// TODO manage property index - throw new UnsupportedOperationException("Multiple properties not supported yet."); - this.property = property.getName(); - node = property.getParent(); - } - this.nodeId = node.getIdentifier(); - if (cacheImmediately) - this.cache = node; -// } - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } catch (RepositoryException e) { - throw new IllegalStateException("Cannot create composite from " + item, e); - } - } - - public synchronized Node getNode() { - try { - if (!itemIsNode()) - throw new IllegalStateException("Item is not a Node"); - return getNodeInternal(); - } catch (RepositoryException e) { - throw new JcrException("Cannot get node " + nodeId, e); - } - } - - private synchronized Node getNodeInternal() throws RepositoryException { - if (cache != null) - return cache; - else if (session != null) - if (nodeId != null) - return session.getNodeByIdentifier(nodeId); - else - return null; - else - return null; - } - - public synchronized String getPropertyName() { - try { - return getProperty().getName(); - } catch (RepositoryException e) { - throw new JcrException("Cannot get property name", e); - } - } - - public synchronized Node getPropertyNode() { - try { - return getProperty().getNode(); - } catch (RepositoryException e) { - throw new JcrException("Cannot get property name", e); - } - } - - public synchronized Property getProperty() { - try { - if (itemIsNode()) - throw new IllegalStateException("Item is not a Property"); - Node node = getNodeInternal(); - if (!node.hasProperty(property)) - throw new IllegalStateException("Property " + property + " is not set on " + node); - return node.getProperty(property); - } catch (RepositoryException e) { - throw new JcrException("Cannot get property " + property + " from node " + nodeId, e); - } - } - - public synchronized boolean itemIsNode() { - return property == null; - } - - public synchronized boolean itemExists() { - if (session == null) - return false; - try { - Node n = session.getNodeByIdentifier(nodeId); - if (!itemIsNode()) - return n.hasProperty(property); - else - return true; - } catch (ItemNotFoundException e) { - return false; - } catch (RepositoryException e) { - throw new JcrException("Cannot check whether node exists", e); - } - } - - /** Set/update the cache or change the node */ - public synchronized void setNode(Node node) { - if (!itemIsNode()) - throw new IllegalArgumentException("Cannot set a Node on a Property"); - - if (node == null) {// clear cache - this.cache = null; - return; - } - - try { -// if (session != null || session != node.getSession())// check session -// throw new IllegalArgumentException("Uncompatible session"); -// if (session == null) - session = node.getSession(); - if (nodeId == null || !nodeId.equals(node.getIdentifier())) { - nodeId = node.getIdentifier(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache - } - } catch (RepositoryException e) { - throw new IllegalStateException(e); - } - } - - /** Set/update the cache or change the property */ - public synchronized void setProperty(Property prop) { - if (itemIsNode()) - throw new IllegalArgumentException("Cannot set a Property on a Node"); - - if (prop == null) {// clear cache - this.cache = null; - return; - } - - try { - if (session == null || session != prop.getSession())// check session - throw new IllegalArgumentException("Uncompatible session"); - - Node node = prop.getNode(); - if (nodeId == null || !nodeId.equals(node.getIdentifier()) || !property.equals(prop.getName())) { - nodeId = node.getIdentifier(); - property = prop.getName(); - cache = node; - itemUpdated(); - } else { - cache = node;// set/update cache - } - } catch (RepositoryException e) { - throw new IllegalStateException(e); - } - } - - public synchronized String getNodeId() { - return nodeId; - } - - /** Change the node, does nothing if same. */ - public synchronized void setNodeId(String nodeId) throws RepositoryException { - if (this.nodeId != null && this.nodeId.equals(nodeId)) - return; - this.nodeId = nodeId; - if (cache != null) - cache = session.getNodeByIdentifier(this.nodeId); - itemUpdated(); - } - - protected synchronized void itemUpdated() { - layout(); - } - -// public Session getSession() { -// return session; -// } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java deleted file mode 100644 index e3a5cb473..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/StyledControl.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.argeo.cms.ui.widgets; - -import javax.jcr.Item; - -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.eclipse.ui.specific.EclipseUiSpecificUtils; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Editable text part displaying styled text. */ -public abstract class StyledControl extends JcrComposite implements CmsUiConstants { - private static final long serialVersionUID = -6372283442330912755L; - private Control control; - - private Composite container; - private Composite box; - - protected MouseListener mouseListener; - protected FocusListener focusListener; - - private Boolean editing = Boolean.FALSE; - - private Composite ancestorToLayout; - - public StyledControl(Composite parent, int swtStyle) { - super(parent, swtStyle); - setLayout(CmsSwtUtils.noSpaceGridLayout()); - } - - public StyledControl(Composite parent, int style, Item item) { - super(parent, style, item); - } - - public StyledControl(Composite parent, int style, Item item, boolean cacheImmediately) { - super(parent, style, item, cacheImmediately); - } - - protected abstract Control createControl(Composite box, String style); - - protected Composite createBox() { - Composite box = new Composite(container, SWT.INHERIT_DEFAULT); - setContainerLayoutData(box); - box.setLayout(CmsSwtUtils.noSpaceGridLayout(3)); - return box; - } - - protected Composite createContainer() { - Composite container = new Composite(this, SWT.INHERIT_DEFAULT); - setContainerLayoutData(container); - container.setLayout(CmsSwtUtils.noSpaceGridLayout()); - return container; - } - - public Control getControl() { - return control; - } - - protected synchronized Boolean isEditing() { - return editing; - } - - public synchronized void startEditing() { - assert !isEditing(); - editing = true; - // int height = control.getSize().y; - String style = (String) EclipseUiSpecificUtils.getStyleData(control); - clear(false); - refreshControl(style); - - // add the focus listener to the newly created edition control - if (focusListener != null) - control.addFocusListener(focusListener); - } - - public synchronized void stopEditing() { - assert isEditing(); - editing = false; - String style = (String) EclipseUiSpecificUtils.getStyleData(control); - clear(false); - refreshControl(style); - } - - protected void refreshControl(String style) { - control = createControl(box, style); - setControlLayoutData(control); - if (ancestorToLayout != null) - ancestorToLayout.layout(true, true); - else - getParent().layout(true, true); - } - - public void setStyle(String style) { - Object currentStyle = null; - if (control != null) - currentStyle = EclipseUiSpecificUtils.getStyleData(control); - if (currentStyle != null && currentStyle.equals(style)) - return; - - clear(true); - refreshControl(style); - - if (style != null) { - CmsSwtUtils.style(box, style + "_box"); - CmsSwtUtils.style(container, style + "_container"); - } - } - - /** To be overridden */ - protected void setControlLayoutData(Control control) { - control.setLayoutData(CmsSwtUtils.fillWidth()); - } - - /** To be overridden */ - protected void setContainerLayoutData(Composite composite) { - composite.setLayoutData(CmsSwtUtils.fillWidth()); - } - - protected void clear(boolean deep) { - if (deep) { - for (Control control : getChildren()) - control.dispose(); - container = createContainer(); - box = createBox(); - } else { - control.dispose(); - } - } - - public void setMouseListener(MouseListener mouseListener) { - if (this.mouseListener != null && control != null) - control.removeMouseListener(this.mouseListener); - this.mouseListener = mouseListener; - if (control != null && this.mouseListener != null) - control.addMouseListener(mouseListener); - } - - public void setFocusListener(FocusListener focusListener) { - if (this.focusListener != null && control != null) - control.removeFocusListener(this.focusListener); - this.focusListener = focusListener; - if (control != null && this.focusListener != null) - control.addFocusListener(focusListener); - } - - public void setAncestorToLayout(Composite ancestorToLayout) { - this.ancestorToLayout = ancestorToLayout; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TextStyles.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TextStyles.java deleted file mode 100644 index e461ed0df..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/TextStyles.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.argeo.cms.ui.widgets; - -/** Styles references in the CSS. */ -public interface TextStyles { - /** The whole page area */ - public final static String TEXT_AREA = "text_area"; - /** Area providing controls for editing text */ - public final static String TEXT_EDITOR_HEADER = "text_editor_header"; - /** The styled composite for editing the text */ - public final static String TEXT_STYLED_COMPOSITE = "text_styled_composite"; - /** A section */ - public final static String TEXT_SECTION = "text_section"; - /** A paragraph */ - public final static String TEXT_PARAGRAPH = "text_paragraph"; - /** An image */ - public final static String TEXT_IMG = "text_img"; - /** The dialog to edit styled paragraph */ - public final static String TEXT_STYLED_TOOLS_DIALOG = "text_styled_tools_dialog"; - - /* - * DEFAULT TEXT STYLES - */ - /** Default style for text body */ - public final static String TEXT_DEFAULT = "text_default"; - /** Fixed-width, typically code */ - public final static String TEXT_PRE = "text_pre"; - /** Quote */ - public final static String TEXT_QUOTE = "text_quote"; - /** Title */ - public final static String TEXT_TITLE = "text_title"; - /** Header (to be dynamically completed with the depth, e.g. text_h1) */ - public final static String TEXT_H = "text_h"; - - /** Default style for images */ - public final static String TEXT_IMAGE = "text_image"; - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/package-info.java deleted file mode 100644 index 514f75380..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/cms/ui/widgets/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS generic widgets, based on SWT. */ -package org.argeo.cms.ui.widgets; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java deleted file mode 100644 index fdafa982e..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/AbstractNodeContentProvider.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.AbstractTreeContentProvider; -import org.argeo.eclipse.ui.EclipseUiException; - -/** Canonical implementation of tree content provider manipulating JCR nodes. */ -public abstract class AbstractNodeContentProvider extends - AbstractTreeContentProvider { - private static final long serialVersionUID = -4905836490027272569L; - - private final static CmsLog log = CmsLog - .getLog(AbstractNodeContentProvider.class); - - private Session session; - - public AbstractNodeContentProvider(Session session) { - this.session = session; - } - - /** - * Whether this path is a base path (and thus has no parent). By default it - * returns true if path is '/' (root node) - */ - protected Boolean isBasePath(String path) { - // root node - return path.equals("/"); - } - - @Override - public Object[] getChildren(Object element) { - Object[] children; - if (element instanceof Node) { - try { - Node node = (Node) element; - children = getChildren(node); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get children of " + element, e); - } - } else if (element instanceof WrappedNode) { - WrappedNode wrappedNode = (WrappedNode) element; - try { - children = getChildren(wrappedNode.getNode()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get children of " - + wrappedNode, e); - } - } else if (element instanceof NodesWrapper) { - NodesWrapper node = (NodesWrapper) element; - children = node.getChildren(); - } else { - children = super.getChildren(element); - } - - children = sort(element, children); - return children; - } - - /** Do not sort by default. To be overidden to provide custom sort. */ - protected Object[] sort(Object parent, Object[] children) { - return children; - } - - /** - * To be overridden in order to filter out some nodes. Does nothing by - * default. The provided list is a temporary one and can thus be modified - * directly . (e.g. via an iterator) - */ - protected List filterChildren(List children) - throws RepositoryException { - return children; - } - - protected Object[] getChildren(Node node) throws RepositoryException { - List nodes = new ArrayList(); - for (NodeIterator nit = node.getNodes(); nit.hasNext();) - nodes.add(nit.nextNode()); - nodes = filterChildren(nodes); - return nodes.toArray(); - } - - @Override - public Object getParent(Object element) { - if (element instanceof Node) { - Node node = (Node) element; - try { - String path = node.getPath(); - if (isBasePath(path)) - return null; - else - return node.getParent(); - } catch (RepositoryException e) { - log.warn("Cannot get parent of " + element + ": " + e); - return null; - } - } else if (element instanceof WrappedNode) { - WrappedNode wrappedNode = (WrappedNode) element; - return wrappedNode.getParent(); - } else if (element instanceof NodesWrapper) { - NodesWrapper nodesWrapper = (NodesWrapper) element; - return this.getParent(nodesWrapper.getNode()); - } - return super.getParent(element); - } - - @Override - public boolean hasChildren(Object element) { - try { - if (element instanceof Node) { - Node node = (Node) element; - return node.hasNodes(); - } else if (element instanceof WrappedNode) { - WrappedNode wrappedNode = (WrappedNode) element; - return wrappedNode.getNode().hasNodes(); - } else if (element instanceof NodesWrapper) { - NodesWrapper nodesWrapper = (NodesWrapper) element; - return nodesWrapper.hasChildren(); - } - - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot check whether " + element - + " has children", e); - } - return super.hasChildren(element); - } - - public Session getSession() { - return session; - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java deleted file mode 100644 index b880a63c5..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/AsyncUiEventListener.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.RepositoryException; -import javax.jcr.observation.Event; -import javax.jcr.observation.EventIterator; -import javax.jcr.observation.EventListener; - -import org.argeo.api.cms.CmsLog; -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.swt.widgets.Display; - -/** - * {@link EventListener} which simplifies running actions within the UI thread. - */ -public abstract class AsyncUiEventListener implements EventListener { - // private final static Log logSuper = LogFactory - // .getLog(AsyncUiEventListener.class); - private final CmsLog logThis = CmsLog.getLog(getClass()); - - private final Display display; - - public AsyncUiEventListener(Display display) { - super(); - this.display = display; - } - - /** Called asynchronously in the UI thread. */ - protected abstract void onEventInUiThread(List events) throws RepositoryException; - - /** - * Whether these events should be processed in the UI or skipped with no UI - * job created. - */ - protected Boolean willProcessInUiThread(List events) throws RepositoryException { - return true; - } - - protected CmsLog getLog() { - return logThis; - } - - public final void onEvent(final EventIterator eventIterator) { - final List events = new ArrayList(); - while (eventIterator.hasNext()) - events.add(eventIterator.nextEvent()); - - if (logThis.isTraceEnabled()) - logThis.trace("Received " + events.size() + " events"); - - try { - if (!willProcessInUiThread(events)) - return; - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot test skip events " + events, e); - } - - // Job job = new Job("JCR Events") { - // protected IStatus run(IProgressMonitor monitor) { - // if (display.isDisposed()) { - // logSuper.warn("Display is disposed cannot update UI"); - // return Status.CANCEL_STATUS; - // } - - if (!display.isDisposed()) - display.asyncExec(new Runnable() { - public void run() { - try { - onEventInUiThread(events); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot process events " + events, e); - } - } - }); - - // return Status.OK_STATUS; - // } - // }; - // job.schedule(); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java deleted file mode 100644 index 22ffeafc6..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/DefaultNodeLabelProvider.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** - * Default label provider to manage node and corresponding UI objects. It - * provides reasonable overwrite-able default for known JCR types. - */ -public class DefaultNodeLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = 1216182332792151235L; - - public String getText(Object element) { - try { - if (element instanceof Node) { - return getText((Node) element); - } else if (element instanceof WrappedNode) { - return getText(((WrappedNode) element).getNode()); - } else if (element instanceof NodesWrapper) { - return getText(((NodesWrapper) element).getNode()); - } - return super.getText(element); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get text for of " + element, e); - } - } - - protected String getText(Node node) throws RepositoryException { - if (node.isNodeType(NodeType.MIX_TITLE) - && node.hasProperty(Property.JCR_TITLE)) - return node.getProperty(Property.JCR_TITLE).getString(); - else - return node.getName(); - } - - @Override - public Image getImage(Object element) { - try { - if (element instanceof Node) { - return getImage((Node) element); - } else if (element instanceof WrappedNode) { - return getImage(((WrappedNode) element).getNode()); - } else if (element instanceof NodesWrapper) { - return getImage(((NodesWrapper) element).getNode()); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot retrieve image for " + element, e); - } - return super.getImage(element); - } - - protected Image getImage(Node node) throws RepositoryException { - // FIXME who uses that? - return null; - } - - @Override - public String getToolTipText(Object element) { - try { - if (element instanceof Node) { - return getToolTipText((Node) element); - } else if (element instanceof WrappedNode) { - return getToolTipText(((WrappedNode) element).getNode()); - } else if (element instanceof NodesWrapper) { - return getToolTipText(((NodesWrapper) element).getNode()); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get tooltip for " + element, e); - } - return super.getToolTipText(element); - } - - protected String getToolTipText(Node node) throws RepositoryException { - return null; - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java deleted file mode 100644 index 420154b83..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/JcrUiUtils.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.Calendar; - -import javax.jcr.Node; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.jcr.lists.NodeViewerComparator; -import org.argeo.eclipse.ui.jcr.lists.RowViewerComparator; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.widgets.Table; - -/** Utility methods to simplify UI development using SWT (or RWT), jface and JCR. */ -public class JcrUiUtils { - - /** - * Centralizes management of updating property value. Among other to avoid - * infinite loop when the new value is the same as the ones that is already - * stored in JCR. - * - * @return true if the value as changed - */ - public static boolean setJcrProperty(Node node, String propName, - int propertyType, Object value) { - try { - switch (propertyType) { - case PropertyType.STRING: - if ("".equals((String) value) - && (!node.hasProperty(propName) || node - .hasProperty(propName) - && "".equals(node.getProperty(propName) - .getString()))) - // workaround the fact that the Text widget value cannot be - // set to null - return false; - else if (node.hasProperty(propName) - && node.getProperty(propName).getString() - .equals((String) value)) - // nothing changed yet - return false; - else { - node.setProperty(propName, (String) value); - return true; - } - case PropertyType.BOOLEAN: - if (node.hasProperty(propName) - && node.getProperty(propName).getBoolean() == (Boolean) value) - // nothing changed yet - return false; - else { - node.setProperty(propName, (Boolean) value); - return true; - } - case PropertyType.DATE: - if (node.hasProperty(propName) - && node.getProperty(propName).getDate() - .equals((Calendar) value)) - // nothing changed yet - return false; - else { - node.setProperty(propName, (Calendar) value); - return true; - } - case PropertyType.LONG: - Long lgValue = (Long) value; - - if (lgValue == null) - lgValue = 0L; - - if (node.hasProperty(propName) - && node.getProperty(propName).getLong() == lgValue) - // nothing changed yet - return false; - else { - node.setProperty(propName, lgValue); - return true; - } - - default: - throw new EclipseUiException("Unimplemented property save"); - } - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error while setting property", - re); - } - } - - /** - * Creates a new selection adapter in order to provide sorting abitily on a - * SWT Table that display a row list - **/ - public static SelectionAdapter getRowSelectionAdapter(final int index, - final int propertyType, final String selectorName, - final String propertyName, final RowViewerComparator comparator, - final TableViewer viewer) { - SelectionAdapter selectionAdapter = new SelectionAdapter() { - private static final long serialVersionUID = -5738918304901437720L; - - @Override - public void widgetSelected(SelectionEvent e) { - Table table = viewer.getTable(); - comparator.setColumn(propertyType, selectorName, propertyName); - int dir = table.getSortDirection(); - if (table.getSortColumn() == table.getColumn(index)) { - dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; - } else { - dir = SWT.DOWN; - } - table.setSortDirection(dir); - table.setSortColumn(table.getColumn(index)); - viewer.refresh(); - } - }; - return selectionAdapter; - } - - /** - * Creates a new selection adapter in order to provide sorting abitily on a - * swt table that display a row list - **/ - public static SelectionAdapter getNodeSelectionAdapter(final int index, - final int propertyType, final String propertyName, - final NodeViewerComparator comparator, final TableViewer viewer) { - SelectionAdapter selectionAdapter = new SelectionAdapter() { - private static final long serialVersionUID = -1683220869195484625L; - - @Override - public void widgetSelected(SelectionEvent e) { - Table table = viewer.getTable(); - comparator.setColumn(propertyType, propertyName); - int dir = table.getSortDirection(); - if (table.getSortColumn() == table.getColumn(index)) { - dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; - } else { - dir = SWT.DOWN; - } - table.setSortDirection(dir); - table.setSortColumn(table.getColumn(index)); - viewer.refresh(); - } - }; - return selectionAdapter; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java deleted file mode 100644 index 7e12becd8..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeColumnLabelProvider.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.Image; - -/** Simplifies writing JCR-based column label provider. */ -public class NodeColumnLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -6586692836928505358L; - - protected String getNodeText(Node node) throws RepositoryException { - return super.getText(node); - } - - protected String getNodeToolTipText(Node node) throws RepositoryException { - return super.getToolTipText(node); - } - - protected Image getNodeImage(Node node) throws RepositoryException { - return super.getImage(node); - } - - protected Font getNodeFont(Node node) throws RepositoryException { - return super.getFont(node); - } - - public Color getNodeBackground(Node node) throws RepositoryException { - return super.getBackground(node); - } - - public Color getNodeForeground(Node node) throws RepositoryException { - return super.getForeground(node); - } - - @Override - public String getText(Object element) { - try { - if (element instanceof Node) - return getNodeText((Node) element); - else if (element instanceof NodeElement) - return getNodeText(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Image getImage(Object element) { - try { - if (element instanceof Node) - return getNodeImage((Node) element); - else if (element instanceof NodeElement) - return getNodeImage(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public String getToolTipText(Object element) { - try { - if (element instanceof Node) - return getNodeToolTipText((Node) element); - else if (element instanceof NodeElement) - return getNodeToolTipText(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Font getFont(Object element) { - try { - if (element instanceof Node) - return getNodeFont((Node) element); - else if (element instanceof NodeElement) - return getNodeFont(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getBackground(Object element) { - try { - if (element instanceof Node) - return getNodeBackground((Node) element); - else if (element instanceof NodeElement) - return getNodeBackground(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getForeground(Object element) { - try { - if (element instanceof Node) - return getNodeForeground((Node) element); - else if (element instanceof NodeElement) - return getNodeForeground(((NodeElement) element).getNode()); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java deleted file mode 100644 index 787c92ed5..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeElement.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; - -/** An element which is related to a JCR {@link Node}. */ -public interface NodeElement { - Node getNode(); -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java deleted file mode 100644 index 2f3d64d6b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodeElementComparer.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.IElementComparer; - -/** Element comparer for JCR node, to be used in JFace viewers. */ -public class NodeElementComparer implements IElementComparer { - - public boolean equals(Object a, Object b) { - try { - if ((a instanceof Node) && (b instanceof Node)) { - Node nodeA = (Node) a; - Node nodeB = (Node) b; - return nodeA.getIdentifier().equals(nodeB.getIdentifier()); - } else { - return a.equals(b); - } - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot compare nodes", e); - } - } - - public int hashCode(Object element) { - try { - if (element instanceof Node) - return ((Node) element).getIdentifier().hashCode(); - return element.hashCode(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get hash code", e); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java deleted file mode 100644 index 2f808a51f..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/NodesWrapper.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; - -/** - * Element of tree which is based on a node, but whose children are not - * necessarily this node children. - */ -public class NodesWrapper { - private final Node node; - - public NodesWrapper(Node node) { - super(); - this.node = node; - } - - protected NodeIterator getNodeIterator() throws RepositoryException { - return node.getNodes(); - } - - protected List getWrappedNodes() throws RepositoryException { - List nodes = new ArrayList(); - for (NodeIterator nit = getNodeIterator(); nit.hasNext();) - nodes.add(new WrappedNode(this, nit.nextNode())); - return nodes; - } - - public Object[] getChildren() { - try { - return getWrappedNodes().toArray(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get wrapped children", e); - } - } - - /** - * @return true by default because we don't want to compute the wrapped - * nodes twice - */ - public Boolean hasChildren() { - return true; - } - - public Node getNode() { - return node; - } - - @Override - public int hashCode() { - return node.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof NodesWrapper) - return node.equals(((NodesWrapper) obj).getNode()); - else - return false; - } - - public String toString() { - return "nodes wrapper based on " + node; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java deleted file mode 100644 index 934fa6781..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/QueryTableContentProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.NodeIterator; -import javax.jcr.RepositoryException; -import javax.jcr.query.Query; - -import org.argeo.jcr.JcrException; -import org.argeo.jcr.JcrUtils; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** Content provider based on a JCR {@link Query}. */ -public class QueryTableContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = 760371460907204722L; - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - @Override - public Object[] getElements(Object inputElement) { - Query query = (Query) inputElement; - try { - NodeIterator nit = query.execute().getNodes(); - return JcrUtils.nodeIteratorToList(nit).toArray(); - } catch (RepositoryException e) { - throw new JcrException(e); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java deleted file mode 100644 index 70c71efea..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/RowColumnLabelProvider.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.RepositoryException; -import javax.jcr.query.Row; - -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.Image; - -/** Simplifies writing JCR-based column label provider. */ -public class RowColumnLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -6586692836928505358L; - - protected String getRowText(Row row) throws RepositoryException { - return super.getText(row); - } - - protected String getRowToolTipText(Row row) throws RepositoryException { - return super.getToolTipText(row); - } - - protected Image getRowImage(Row row) throws RepositoryException { - return super.getImage(row); - } - - protected Font getRowFont(Row row) throws RepositoryException { - return super.getFont(row); - } - - public Color getRowBackground(Row row) throws RepositoryException { - return super.getBackground(row); - } - - public Color getRowForeground(Row row) throws RepositoryException { - return super.getForeground(row); - } - - @Override - public String getText(Object element) { - try { - if (element instanceof Row) - return getRowText((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Image getImage(Object element) { - try { - if (element instanceof Row) - return getRowImage((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public String getToolTipText(Object element) { - try { - if (element instanceof Row) - return getRowToolTipText((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Font getFont(Object element) { - try { - if (element instanceof Row) - return getRowFont((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getBackground(Object element) { - try { - if (element instanceof Row) - return getRowBackground((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - - @Override - public Color getForeground(Object element) { - try { - if (element instanceof Row) - return getRowForeground((Row) element); - else - throw new IllegalArgumentException("Unsupported element type " + element.getClass()); - } catch (RepositoryException e) { - throw new IllegalStateException("Repository exception when accessing " + element, e); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java deleted file mode 100644 index cb235d76b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/SimpleNodeContentProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.jcr.JcrUtils; - -/** Simple JCR node content provider taking a list of String as base path. */ -public class SimpleNodeContentProvider extends AbstractNodeContentProvider { - private static final long serialVersionUID = -8245193308831384269L; - private final List basePaths; - private Boolean mkdirs = false; - - public SimpleNodeContentProvider(Session session, String... basePaths) { - this(session, Arrays.asList(basePaths)); - } - - public SimpleNodeContentProvider(Session session, List basePaths) { - super(session); - this.basePaths = basePaths; - } - - @Override - protected Boolean isBasePath(String path) { - if (basePaths.contains(path)) - return true; - return super.isBasePath(path); - } - - public Object[] getElements(Object inputElement) { - try { - List baseNodes = new ArrayList(); - for (String basePath : basePaths) - if (mkdirs && !getSession().itemExists(basePath)) - baseNodes.add(JcrUtils.mkdirs(getSession(), basePath)); - else - baseNodes.add(getSession().getNode(basePath)); - return baseNodes.toArray(); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot get base nodes for " + basePaths, - e); - } - } - - public List getBasePaths() { - return basePaths; - } - - public void setMkdirs(Boolean mkdirs) { - this.mkdirs = mkdirs; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java deleted file mode 100644 index 1ce315429..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/VersionColumnLabelProvider.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.version.Version; - -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.swt.graphics.Image; - -/** Simplifies writing JCR-based column label provider. */ -public class VersionColumnLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -6117690082313161159L; - - protected String getVersionText(Version version) throws RepositoryException { - return super.getText(version); - } - - protected String getVersionToolTipText(Version version) throws RepositoryException { - return super.getToolTipText(version); - } - - protected Image getVersionImage(Version version) throws RepositoryException { - return super.getImage(version); - } - - protected String getUserName(Version version) throws RepositoryException { - Node node = version.getFrozenNode(); - if(node.hasProperty(Property.JCR_LAST_MODIFIED_BY)) - return node.getProperty(Property.JCR_LAST_MODIFIED_BY).getString(); - if(node.hasProperty(Property.JCR_CREATED_BY)) - return node.getProperty(Property.JCR_CREATED_BY).getString(); - return null; - } - -// protected String getActivityTitle(Version version) throws RepositoryException { -// Node activity = getActivity(version); -// if (activity == null) -// return null; -// if (activity.hasProperty("jcr:activityTitle")) -// return activity.getProperty("jcr:activityTitle").getString(); -// else -// return activity.getName(); -// } -// -// protected Node getActivity(Version version) throws RepositoryException { -// if (version.hasProperty(Property.JCR_ACTIVITY)) { -// return version.getProperty(Property.JCR_ACTIVITY).getNode(); -// } else -// return null; -// } - - @Override - public String getText(Object element) { - try { - return getVersionText((Version) element); - } catch (RepositoryException e) { - throw new RuntimeException("Runtime repository exception when accessing " + element, e); - } - } - - @Override - public Image getImage(Object element) { - try { - return getVersionImage((Version) element); - } catch (RepositoryException e) { - throw new RuntimeException("Runtime repository exception when accessing " + element, e); - } - } - - @Override - public String getToolTipText(Object element) { - try { - return getVersionToolTipText((Version) element); - } catch (RepositoryException e) { - throw new RuntimeException("Runtime repository exception when accessing " + element, e); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java deleted file mode 100644 index 32e5d30c1..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/VersionHistoryContentProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.version.VersionHistory; - -import org.argeo.jcr.Jcr; -import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.Viewer; - -/** Content provider based on a {@link VersionHistory}. */ -public class VersionHistoryContentProvider implements IStructuredContentProvider { - private static final long serialVersionUID = -4921107883428887012L; - - @Override - public void dispose() { - } - - @Override - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - } - - @Override - public Object[] getElements(Object inputElement) { - VersionHistory versionHistory = (VersionHistory) inputElement; - return Jcr.getLinearVersions(versionHistory).toArray(); - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java deleted file mode 100644 index 43df1fe01..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/WrappedNode.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.argeo.eclipse.ui.jcr; - -import javax.jcr.Node; - -/** Wrap a node (created from a {@link NodesWrapper}) */ -public class WrappedNode { - private final NodesWrapper parent; - private final Node node; - - public WrappedNode(NodesWrapper parent, Node node) { - super(); - this.parent = parent; - this.node = node; - } - - public NodesWrapper getParent() { - return parent; - } - - public Node getNode() { - return node; - } - - public String toString() { - return "wrapped " + node; - } - - @Override - public int hashCode() { - return node.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof WrappedNode) - return node.equals(((WrappedNode) obj).getNode()); - else - return false; - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java deleted file mode 100644 index c5dd73317..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/JcrColumnDefinition.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import javax.jcr.Node; -import javax.jcr.query.Row; - -import org.argeo.eclipse.ui.ColumnDefinition; - -/** - * Utility object to manage column in various tables and extracts displaying - * data from JCR - */ -public class JcrColumnDefinition extends ColumnDefinition { - private final static int DEFAULT_COLUMN_SIZE = 120; - - private String selectorName; - private String propertyName; - private int propertyType; - private int columnSize; - - /** - * Use this kind of columns to configure a table that displays JCR - * {@link Row} - * - * @param selectorName - * @param propertyName - * @param propertyType - * @param headerLabel - */ - public JcrColumnDefinition(String selectorName, String propertyName, - int propertyType, String headerLabel) { - super(new SimpleJcrRowLabelProvider(selectorName, propertyName), - headerLabel); - this.selectorName = selectorName; - this.propertyName = propertyName; - this.propertyType = propertyType; - this.columnSize = DEFAULT_COLUMN_SIZE; - } - - /** - * Use this kind of columns to configure a table that displays JCR - * {@link Row} - * - * @param selectorName - * @param propertyName - * @param propertyType - * @param headerLabel - * @param columnSize - */ - public JcrColumnDefinition(String selectorName, String propertyName, - int propertyType, String headerLabel, int columnSize) { - super(new SimpleJcrRowLabelProvider(selectorName, propertyName), - headerLabel, columnSize); - this.selectorName = selectorName; - this.propertyName = propertyName; - this.propertyType = propertyType; - this.columnSize = columnSize; - } - - /** - * Use this kind of columns to configure a table that displays JCR - * {@link Node} - * - * @param propertyName - * @param propertyType - * @param headerLabel - * @param columnSize - */ - public JcrColumnDefinition(String propertyName, int propertyType, - String headerLabel, int columnSize) { - super(new SimpleJcrNodeLabelProvider(propertyName), headerLabel, - columnSize); - this.propertyName = propertyName; - this.propertyType = propertyType; - this.columnSize = columnSize; - } - - public String getSelectorName() { - return selectorName; - } - - public void setSelectorName(String selectorName) { - this.selectorName = selectorName; - } - - public String getPropertyName() { - return propertyName; - } - - public void setPropertyName(String propertyName) { - this.propertyName = propertyName; - } - - public int getPropertyType() { - return propertyType; - } - - public void setPropertyType(int propertyType) { - this.propertyType = propertyType; - } - - public int getColumnSize() { - return columnSize; - } - - public void setColumnSize(int columnSize) { - this.columnSize = columnSize; - } - - public String getHeaderLabel() { - return super.getLabel(); - } - - public void setHeaderLabel(String headerLabel) { - super.setLabel(headerLabel); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java deleted file mode 100644 index 341b3abee..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/NodeViewerComparator.java +++ /dev/null @@ -1,190 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import java.math.BigDecimal; -import java.util.Calendar; - -import javax.jcr.Node; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; -import javax.jcr.ValueFormatException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerComparator; - -/** - * Base comparator to enable ordering on Table or Tree viewer that display Jcr - * Nodes. - * - * Note that the following snippet must be added before setting the comparator - * to the corresponding control: - * // IMPORTANT: initialize comparator before setting it - * JcrColumnDefinition firstCol = colDefs.get(0); - * comparator.setColumn(firstCol.getPropertyType(), - * firstCol.getPropertyName()); - * viewer.setComparator(comparator); - */ -public class NodeViewerComparator extends ViewerComparator { - private static final long serialVersionUID = -7782916140737279027L; - - protected String propertyName; - - protected int propertyType; - public static final int ASCENDING = 0, DESCENDING = 1; - protected int direction = DESCENDING; - - public NodeViewerComparator() { - } - - /** - * e1 and e2 must both be Jcr nodes. - * - * @param viewer - * @param e1 - * @param e2 - * @return - */ - @Override - public int compare(Viewer viewer, Object e1, Object e2) { - int rc = 0; - long lc = 0; - - try { - Node n1 = (Node) e1; - Node n2 = (Node) e2; - - Value v1 = null; - Value v2 = null; - if (n1.hasProperty(propertyName)) - v1 = n1.getProperty(propertyName).getValue(); - if (n2.hasProperty(propertyName)) - v2 = n2.getProperty(propertyName).getValue(); - - if (v2 == null && v1 == null) - return 0; - else if (v2 == null) - return -1; - else if (v1 == null) - return 1; - - switch (propertyType) { - case PropertyType.STRING: - rc = v1.getString().compareTo(v2.getString()); - break; - case PropertyType.BOOLEAN: - boolean b1 = v1.getBoolean(); - boolean b2 = v2.getBoolean(); - if (b1 == b2) - rc = 0; - else - // we assume true is greater than false - rc = b1 ? 1 : -1; - break; - case PropertyType.DATE: - Calendar c1 = v1.getDate(); - Calendar c2 = v2.getDate(); - if (c1 == null || c2 == null) - // log.trace("undefined date"); - ; - lc = c1.getTimeInMillis() - c2.getTimeInMillis(); - if (lc < Integer.MIN_VALUE) - rc = -1; - else if (lc > Integer.MAX_VALUE) - rc = 1; - else - rc = (int) lc; - break; - case PropertyType.LONG: - long l1; - long l2; - // TODO Sometimes an empty string is set instead of a long - try { - l1 = v1.getLong(); - } catch (ValueFormatException ve) { - l1 = 0; - } - try { - l2 = v2.getLong(); - } catch (ValueFormatException ve) { - l2 = 0; - } - - lc = l1 - l2; - if (lc < Integer.MIN_VALUE) - rc = -1; - else if (lc > Integer.MAX_VALUE) - rc = 1; - else - rc = (int) lc; - break; - case PropertyType.DECIMAL: - BigDecimal bd1 = v1.getDecimal(); - BigDecimal bd2 = v2.getDecimal(); - rc = bd1.compareTo(bd2); - break; - case PropertyType.DOUBLE: - Double d1 = v1.getDouble(); - Double d2 = v2.getDouble(); - rc = d1.compareTo(d2); - break; - default: - throw new EclipseUiException( - "Unimplemented comparaison for PropertyType " - + propertyType); - } - // If descending order, flip the direction - if (direction == DESCENDING) { - rc = -rc; - } - - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error " - + "while comparing nodes", re); - } - return rc; - } - - /** - * @param propertyType - * Corresponding JCR type - * @param propertyName - * name of the property to use. - */ - public void setColumn(int propertyType, String propertyName) { - if (this.propertyName != null && this.propertyName.equals(propertyName)) { - // Same column as last sort; toggle the direction - direction = 1 - direction; - } else { - // New column; do an ascending sort - this.propertyType = propertyType; - this.propertyName = propertyName; - direction = ASCENDING; - } - } - - // Getters and setters - protected String getPropertyName() { - return propertyName; - } - - protected void setPropertyName(String propertyName) { - this.propertyName = propertyName; - } - - protected int getPropertyType() { - return propertyType; - } - - protected void setPropertyType(int propertyType) { - this.propertyType = propertyType; - } - - protected int getDirection() { - return direction; - } - - protected void setDirection(int direction) { - this.direction = direction; - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java deleted file mode 100644 index 455fb0dcd..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/RowViewerComparator.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.query.Row; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.Viewer; - -/** - * Base comparator to enable ordering on Table or Tree viewer that display Jcr - * rows - */ -public class RowViewerComparator extends NodeViewerComparator { - private static final long serialVersionUID = 7020939505172625113L; - protected String selectorName; - - public RowViewerComparator() { - } - - /** - * e1 and e2 must both be Jcr rows. - * - * @param viewer - * @param e1 - * @param e2 - * @return - */ - @Override - public int compare(Viewer viewer, Object e1, Object e2) { - try { - Node n1 = ((Row) e1).getNode(selectorName); - Node n2 = ((Row) e2).getNode(selectorName); - return super.compare(viewer, n1, n2); - } catch (RepositoryException re) { - throw new EclipseUiException("Unexpected error " - + "while comparing nodes", re); - } - } - - /** - * @param propertyType - * Corresponding JCR type - * @param propertyName - * name of the property to use. - */ - public void setColumn(int propertyType, String selectorName, - String propertyName) { - if (this.selectorName != null && getPropertyName() != null - && this.selectorName.equals(selectorName) - && this.getPropertyName().equals(propertyName)) { - // Same column as last sort; toggle the direction - setDirection(1 - getDirection()); - } else { - // New column; do a descending sort - setPropertyType(propertyType); - setPropertyName(propertyName); - this.selectorName = selectorName; - setDirection(NodeViewerComparator.ASCENDING); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java deleted file mode 100644 index aa2e3375c..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrNodeLabelProvider.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; - -import javax.jcr.Node; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.ColumnLabelProvider; - -/** Base implementation of a label provider for controls that display JCR Nodes */ -public class SimpleJcrNodeLabelProvider extends ColumnLabelProvider { - private static final long serialVersionUID = -5215787695436221993L; - - private final static String DEFAULT_DATE_FORMAT = "EEE, dd MMM yyyy"; - private final static String DEFAULT_NUMBER_FORMAT = "#,##0.0"; - - private DateFormat dateFormat; - private NumberFormat numberFormat; - - final private String propertyName; - - /** - * Default Label provider for a given property of a node. Using default - * pattern for date and number formating - */ - public SimpleJcrNodeLabelProvider(String propertyName) { - this.propertyName = propertyName; - dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT); - numberFormat = DecimalFormat.getInstance(); - ((DecimalFormat) numberFormat).applyPattern(DEFAULT_NUMBER_FORMAT); - } - - /** - * Label provider for a given property of a node optionally precising date - * and/or number format patterns - */ - public SimpleJcrNodeLabelProvider(String propertyName, - String dateFormatPattern, String numberFormatPattern) { - this.propertyName = propertyName; - dateFormat = new SimpleDateFormat( - dateFormatPattern == null ? DEFAULT_DATE_FORMAT - : dateFormatPattern); - numberFormat = DecimalFormat.getInstance(); - ((DecimalFormat) numberFormat) - .applyPattern(numberFormatPattern == null ? DEFAULT_NUMBER_FORMAT - : numberFormatPattern); - } - - @Override - public String getText(Object element) { - try { - Node currNode = (Node) element; - - if (currNode.hasProperty(propertyName)) { - if (currNode.getProperty(propertyName).isMultiple()) { - StringBuilder builder = new StringBuilder(); - for (Value value : currNode.getProperty(propertyName) - .getValues()) { - String currStr = getSingleValueAsString(value); - if (notEmptyString(currStr)) - builder.append(currStr).append("; "); - } - if (builder.length() > 0) - builder.deleteCharAt(builder.length() - 2); - - return builder.toString(); - } else - return getSingleValueAsString(currNode.getProperty( - propertyName).getValue()); - } else - return ""; - } catch (RepositoryException re) { - throw new EclipseUiException("Unable to get text from row", re); - } - } - - private String getSingleValueAsString(Value value) - throws RepositoryException { - switch (value.getType()) { - case PropertyType.STRING: - return value.getString(); - case PropertyType.BOOLEAN: - return "" + value.getBoolean(); - case PropertyType.DATE: - return dateFormat.format(value.getDate().getTime()); - case PropertyType.LONG: - return "" + value.getLong(); - case PropertyType.DECIMAL: - return numberFormat.format(value.getDecimal()); - case PropertyType.DOUBLE: - return numberFormat.format(value.getDouble()); - case PropertyType.NAME: - return value.getString(); - default: - throw new EclipseUiException("Unimplemented label provider " - + "for property type " + value.getType() - + " while getting property " + propertyName + " - value: " - + value.getString()); - - } - } - - private boolean notEmptyString(String string) { - return string != null && !"".equals(string.trim()); - } - - public void setDateFormat(String dateFormatPattern) { - dateFormat = new SimpleDateFormat(dateFormatPattern); - } - - public void setNumberFormat(String numberFormatPattern) { - ((DecimalFormat) numberFormat).applyPattern(numberFormatPattern); - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java deleted file mode 100644 index 5d421f64b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/SimpleJcrRowLabelProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.argeo.eclipse.ui.jcr.lists; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.query.Row; - -import org.argeo.eclipse.ui.EclipseUiException; - -/** - * Base implementation of a label provider for widgets that display JCR Rows. - */ -public class SimpleJcrRowLabelProvider extends SimpleJcrNodeLabelProvider { - private static final long serialVersionUID = -3414654948197181740L; - - final private String selectorName; - - /** - * Default Label provider for a given property of a row. Using default - * pattern for date and number formating - */ - public SimpleJcrRowLabelProvider(String selectorName, String propertyName) { - super(propertyName); - this.selectorName = selectorName; - } - - /** - * Label provider for a given property of a node optionally precising date - * and/or number format patterns - */ - public SimpleJcrRowLabelProvider(String selectorName, String propertyName, - String dateFormatPattern, String numberFormatPattern) { - super(propertyName, dateFormatPattern, numberFormatPattern); - this.selectorName = selectorName; - } - - @Override - public String getText(Object element) { - try { - Row currRow = (Row) element; - Node currNode = currRow.getNode(selectorName); - return super.getText(currNode); - } catch (RepositoryException re) { - throw new EclipseUiException("Unable to get Node " + selectorName - + " from row " + element, re); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java deleted file mode 100644 index 3678aab88..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/lists/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace JCR utilities for lists. */ -package org.argeo.eclipse.ui.jcr.lists; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/package-info.java deleted file mode 100644 index 19e3cc3ff..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace JCR utilities. */ -package org.argeo.eclipse.ui.jcr; \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java deleted file mode 100644 index c82e666d1..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/JcrFileProvider.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.IOUtils; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.FileProvider; - -/** - * Implements a FileProvider for UI purposes. Note that it might not be very - * reliable as long as we have not fixed login and multi repository issues that - * will be addressed in the next version. - * - * NOTE: id used here is the real id of the JCR Node, not the JCR Path - * - * Relies on common approach for JCR file handling implementation. - * - */ -@SuppressWarnings("deprecation") -public class JcrFileProvider implements FileProvider { - - // private Object[] rootNodes; - private Node refNode; - - /** - * Must be set in order for the provider to be able to get current session - * and thus have the ability to get the file node corresponding to a given - * file ID - * - * @param refNode - */ - public void setReferenceNode(Node refNode) { - // FIXME : this introduces some concurrency ISSUES. - this.refNode = refNode; - } - - public byte[] getByteArrayFileFromId(String fileId) { - InputStream fis = null; - byte[] ba = null; - Node child = getFileNodeFromId(fileId); - try { - fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); - ba = IOUtils.toByteArray(fis); - - } catch (Exception e) { - throw new EclipseUiException("Stream error while opening file", e); - } finally { - IOUtils.closeQuietly(fis); - } - return ba; - } - - public InputStream getInputStreamFromFileId(String fileId) { - try { - InputStream fis = null; - - Node child = getFileNodeFromId(fileId); - fis = (InputStream) child.getProperty(Property.JCR_DATA).getBinary().getStream(); - return fis; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot get stream from file node for Id " + fileId, re); - } - } - - /** - * Throws an exception if the node is not found in the current repository (a - * bit like a FileNotFoundException) - * - * @param fileId - * @return Returns the child node of the nt:file node. It is the child node - * that have the jcr:data property where actual file is stored. - * never null - */ - private Node getFileNodeFromId(String fileId) { - try { - Node result = refNode.getSession().getNodeByIdentifier(fileId); - - // rootNodes: for (int j = 0; j < rootNodes.length; j++) { - // // in case we have a classic JCR Node - // if (rootNodes[j] instanceof Node) { - // Node curNode = (Node) rootNodes[j]; - // if (result != null) - // break rootNodes; - // } // Case of a repository Node - // else if (rootNodes[j] instanceof RepositoryNode) { - // Object[] nodes = ((RepositoryNode) rootNodes[j]) - // .getChildren(); - // for (int i = 0; i < nodes.length; i++) { - // Node node = (Node) nodes[i]; - // result = node.getSession().getNodeByIdentifier(fileId); - // if (result != null) - // break rootNodes; - // } - // } - // } - - // Sanity checks - if (result == null) - throw new EclipseUiException("File node not found for ID" + fileId); - - Node child = null; - - boolean isValid = true; - if (!result.isNodeType(NodeType.NT_FILE)) - // useless: mandatory child node - // || !result.hasNode(Property.JCR_CONTENT)) - isValid = false; - else { - child = result.getNode(Property.JCR_CONTENT); - if (!(child.isNodeType(NodeType.NT_RESOURCE) || child.hasProperty(Property.JCR_DATA))) - isValid = false; - } - - if (!isValid) - throw new EclipseUiException("ERROR: In the current implemented model, '" + NodeType.NT_FILE - + "' file node must have a child node named jcr:content " - + "that has a BINARY Property named jcr:data " + "where the actual data is stored"); - return child; - - } catch (RepositoryException re) { - throw new EclipseUiException("Erreur while getting file node of ID " + fileId, re); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java deleted file mode 100644 index fb123992f..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/JcrItemsComparator.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import java.util.Comparator; - -import javax.jcr.Item; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; - -/** Compares two JCR items (node or properties) based on their names. */ -public class JcrItemsComparator implements Comparator { - public int compare(Item o1, Item o2) { - try { - // TODO: put folder before files - return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); - } catch (RepositoryException e) { - throw new EclipseUiException("Cannot compare " + o1 + " and " + o2, e); - } - } - -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java deleted file mode 100644 index 54b795f3b..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/NodeViewerComparer.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; - -import org.argeo.eclipse.ui.EclipseUiException; -import org.eclipse.jface.viewers.IElementComparer; - -/** Compare JCR nodes based on their JCR identifiers, for use in JFace viewers. */ -public class NodeViewerComparer implements IElementComparer { - - // force comparison on Node IDs only. - public boolean equals(Object elementA, Object elementB) { - if (!(elementA instanceof Node) || !(elementB instanceof Node)) { - return elementA == null ? elementB == null : elementA - .equals(elementB); - } else { - - boolean result = false; - try { - String idA = ((Node) elementA).getIdentifier(); - String idB = ((Node) elementB).getIdentifier(); - result = idA == null ? idB == null : idA.equals(idB); - } catch (RepositoryException re) { - throw new EclipseUiException("cannot compare nodes", re); - } - - return result; - } - } - - public int hashCode(Object element) { - // TODO enhanced this method. - return element.getClass().toString().hashCode(); - } -} \ No newline at end of file diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java deleted file mode 100644 index 291d579ac..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/SingleSessionFileProvider.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.argeo.eclipse.ui.jcr.util; - -import java.io.InputStream; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.io.IOUtils; -import org.argeo.eclipse.ui.EclipseUiException; -import org.argeo.eclipse.ui.FileProvider; - -/** - * Implements a FileProvider for UI purposes. Unlike the - * JcrFileProvider , it relies on a single session and manages - * nodes with path only. - * - * Note that considered id is the JCR path - * - * Relies on common approach for JCR file handling implementation. - */ -@SuppressWarnings("deprecation") -public class SingleSessionFileProvider implements FileProvider { - - private Session session; - - public SingleSessionFileProvider(Session session) { - this.session = session; - } - - public byte[] getByteArrayFileFromId(String fileId) { - InputStream fis = null; - byte[] ba = null; - Node child = getFileNodeFromId(fileId); - try { - fis = (InputStream) child.getProperty(Property.JCR_DATA) - .getBinary().getStream(); - ba = IOUtils.toByteArray(fis); - - } catch (Exception e) { - throw new EclipseUiException("Stream error while opening file", e); - } finally { - IOUtils.closeQuietly(fis); - } - return ba; - } - - public InputStream getInputStreamFromFileId(String fileId) { - try { - InputStream fis = null; - - Node child = getFileNodeFromId(fileId); - fis = (InputStream) child.getProperty(Property.JCR_DATA) - .getBinary().getStream(); - return fis; - } catch (RepositoryException re) { - throw new EclipseUiException("Cannot get stream from file node for Id " - + fileId, re); - } - } - - /** - * - * @param fileId - * @return Returns the child node of the nt:file node. It is the child node - * that have the jcr:data property where actual file is stored. - * never null - */ - private Node getFileNodeFromId(String fileId) { - try { - Node result = null; - result = session.getNode(fileId); - - // Sanity checks - if (result == null) - throw new EclipseUiException("File node not found for ID" + fileId); - - // Ensure that the node have the correct type. - if (!result.isNodeType(NodeType.NT_FILE)) - throw new EclipseUiException( - "Cannot open file children Node that are not of " - + NodeType.NT_RESOURCE + " type."); - - Node child = result.getNodes().nextNode(); - if (child == null || !child.isNodeType(NodeType.NT_RESOURCE)) - throw new EclipseUiException( - "ERROR: IN the current implemented model, " - + NodeType.NT_FILE - + " file node must have one and only one child of the nt:ressource, where actual data is stored"); - return child; - } catch (RepositoryException re) { - throw new EclipseUiException("Erreur while getting file node of ID " - + fileId, re); - } - } -} diff --git a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java b/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java deleted file mode 100644 index 016348cda..000000000 --- a/jcr/org.argeo.cms.ui/src/org/argeo/eclipse/ui/jcr/util/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Generic SWT/JFace JCR helpers. */ -package org.argeo.eclipse.ui.jcr.util; \ No newline at end of file diff --git a/org.argeo.cms.ux/src/org/argeo/cms/ux/AbstractImageManager.java b/org.argeo.cms.ux/src/org/argeo/cms/ux/AbstractImageManager.java new file mode 100644 index 000000000..41c905ef6 --- /dev/null +++ b/org.argeo.cms.ux/src/org/argeo/cms/ux/AbstractImageManager.java @@ -0,0 +1,62 @@ +package org.argeo.cms.ux; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.api.cms.ux.CmsImageManager; + +/** Manages only public images so far. */ +public abstract class AbstractImageManager implements CmsImageManager { + public final static String NO_IMAGE = "icons/noPic-square-640px.png"; + public final static Cms2DSize NO_IMAGE_SIZE = new Cms2DSize(320, 320); + public final static Float NO_IMAGE_RATIO = 1f; + + protected Cms2DSize resizeTo(Cms2DSize orig, Cms2DSize constraints) { + if (constraints.getWidth() != 0 && constraints.getHeight() != 0) { + return constraints; + } else if (constraints.getWidth() == 0 && constraints.getHeight() == 0) { + return orig; + } else if (constraints.getHeight() == 0) {// force width + return new Cms2DSize(constraints.getWidth(), + scale(orig.getHeight(), orig.getWidth(), constraints.getWidth())); + } else if (constraints.getWidth() == 0) {// force height + return new Cms2DSize(scale(orig.getWidth(), orig.getHeight(), constraints.getHeight()), + constraints.getHeight()); + } + throw new IllegalArgumentException("Cannot resize " + orig + " to " + constraints); + } + + protected int scale(int origDimension, int otherDimension, int otherConstraint) { + return Math.round(origDimension * divide(otherConstraint, otherDimension)); + } + + protected float divide(int a, int b) { + return ((float) a) / ((float) b); + } + + /** @return null if not available */ + @Override + public String getImageTag(M node) { + return getImageTag(node, getImageSize(node)); + } + + protected String getImageTag(M node, Cms2DSize size) { + StringBuilder buf = getImageTagBuilder(node, size); + if (buf == null) + return null; + return buf.append("/>").toString(); + } + + /** @return null if not available */ + @Override + public StringBuilder getImageTagBuilder(M node, Cms2DSize size) { + return getImageTagBuilder(node, Integer.toString(size.getWidth()), Integer.toString(size.getHeight())); + } + + /** @return null if not available */ + protected StringBuilder getImageTagBuilder(M node, String width, String height) { + String url = getImageUrl(node); + if (url == null) + return null; + return CmsUxUtils.imgBuilder(url, width, height); + } + +} diff --git a/org.argeo.cms.ux/src/org/argeo/cms/ux/CmsUxUtils.java b/org.argeo.cms.ux/src/org/argeo/cms/ux/CmsUxUtils.java index d6fd221a4..916cc74f2 100644 --- a/org.argeo.cms.ux/src/org/argeo/cms/ux/CmsUxUtils.java +++ b/org.argeo.cms.ux/src/org/argeo/cms/ux/CmsUxUtils.java @@ -3,6 +3,7 @@ package org.argeo.cms.ux; import org.argeo.api.acr.Content; import org.argeo.api.acr.ContentRepository; import org.argeo.api.acr.ContentSession; +import org.argeo.api.cms.ux.Cms2DSize; import org.argeo.api.cms.ux.CmsView; import org.argeo.util.CurrentSubject; @@ -19,4 +20,17 @@ public class CmsUxUtils { private CmsUxUtils() { } + + public static StringBuilder imgBuilder(String src, String width, String height) { + return new StringBuilder(64).append("").toString(); + } + + public static String img(String src, Cms2DSize size) { + return img(src, Integer.toString(size.getWidth()), Integer.toString(size.getHeight())); + } } diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/AbstractSwtImageManager.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/AbstractSwtImageManager.java new file mode 100644 index 000000000..00a51ef92 --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/AbstractSwtImageManager.java @@ -0,0 +1,77 @@ +package org.argeo.cms.swt; + +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.ux.AbstractImageManager; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** Manages only public images so far. */ +public abstract class AbstractSwtImageManager extends AbstractImageManager { + protected abstract Image getSwtImage(M node); + + protected abstract String noImg(Cms2DSize size); + + public Boolean load(M node, Control control, Cms2DSize preferredSize) { + Cms2DSize imageSize = getImageSize(node); + Cms2DSize size; + String imgTag = null; + if (preferredSize == null || imageSize.getWidth() == 0 || imageSize.getHeight() == 0 + || (preferredSize.getWidth() == 0 && preferredSize.getHeight() == 0)) { + if (imageSize.getWidth() != 0 && imageSize.getHeight() != 0) { + // actual image size if completely known + size = imageSize; + } else { + // no image if not completely known + size = resizeTo(NO_IMAGE_SIZE, preferredSize != null ? preferredSize : imageSize); + imgTag = noImg(size); + } + + } else if (preferredSize.getWidth() != 0 && preferredSize.getHeight() != 0) { + // given size if completely provided + size = preferredSize; + } else { + // at this stage : + // image is completely known + assert imageSize.getWidth() != 0 && imageSize.getHeight() != 0; + // one and only one of the dimension as been specified + assert preferredSize.getWidth() == 0 || preferredSize.getHeight() == 0; + size = resizeTo(imageSize, preferredSize); + } + + boolean loaded = false; + if (control == null) + return loaded; + + if (control instanceof Label) { + if (imgTag == null) { + // IMAGE RETRIEVED HERE + imgTag = getImageTag(node, size); + // + if (imgTag == null) + imgTag = noImg(size); + else + loaded = true; + } + + Label lbl = (Label) control; + lbl.setText(imgTag); + // lbl.setSize(size); +// } else if (control instanceof FileUpload) { +// FileUpload lbl = (FileUpload) control; +// lbl.setImage(CmsUiUtils.noImage(size)); +// lbl.setSize(new Point(size.getWidth(), size.getHeight())); +// return loaded; + } else + loaded = false; + + return loaded; + } + + public Cms2DSize getImageSize(M node) { + // TODO optimise + Image image = getSwtImage(node); + return new Cms2DSize(image.getBounds().width, image.getBounds().height); + } + +} diff --git a/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/AcrSwtImageManager.java b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/AcrSwtImageManager.java new file mode 100644 index 000000000..5e83454de --- /dev/null +++ b/swt/org.argeo.cms.swt/src/org/argeo/cms/swt/acr/AcrSwtImageManager.java @@ -0,0 +1,37 @@ +package org.argeo.cms.swt.acr; + +import java.io.InputStream; + +import org.argeo.api.acr.Content; +import org.argeo.api.cms.ux.Cms2DSize; +import org.argeo.cms.swt.AbstractSwtImageManager; +import org.eclipse.swt.graphics.Image; + +public class AcrSwtImageManager extends AbstractSwtImageManager { + + @Override + public String getImageUrl(Content node) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String uploadImage(Content context, Content uploadFolder, String fileName, InputStream in, + String contentType) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected Image getSwtImage(Content node) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected String noImg(Cms2DSize size) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/swt/rap/org.argeo.cms.e4.rap/bnd.bnd b/swt/rap/org.argeo.cms.e4.rap/bnd.bnd index 5bbe4bc4b..29ce1708a 100644 --- a/swt/rap/org.argeo.cms.e4.rap/bnd.bnd +++ b/swt/rap/org.argeo.cms.e4.rap/bnd.bnd @@ -1,7 +1,9 @@ Bundle-ActivationPolicy: lazy Service-Component: OSGI-INF/cms-admin-rap.xml -Import-Package: org.eclipse.swt,\ +Import-Package: \ +org.argeo.api.acr, \ +org.eclipse.swt,\ org.eclipse.swt.graphics,\ org.eclipse.e4.ui.workbench,\ org.eclipse.rap.rwt.client,\ diff --git a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java index 471cdeca5..cdd489928 100644 --- a/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java +++ b/swt/rap/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsLoginLifecycle.java @@ -15,9 +15,9 @@ import org.argeo.api.cms.ux.UxContext; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.SimpleSwtUxContext; +import org.argeo.cms.swt.acr.AcrSwtImageManager; import org.argeo.cms.swt.auth.CmsLoginShell; import org.argeo.cms.swt.dialogs.CmsFeedback; -import org.argeo.cms.ui.util.SimpleImageManager; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.workbench.UIEvents; import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; @@ -82,7 +82,7 @@ public class CmsLoginLifecycle implements CmsView { if (CurrentUser.getUsername(getSubject()) == null) return false; uxContext = new SimpleSwtUxContext(); - imageManager = new SimpleImageManager(); + imageManager = (CmsImageManager) new AcrSwtImageManager(); eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE, new EventHandler() { @Override diff --git a/swt/rap/org.argeo.cms.swt.rap/bnd.bnd b/swt/rap/org.argeo.cms.swt.rap/bnd.bnd index db2ac0beb..81178ebd3 100644 --- a/swt/rap/org.argeo.cms.swt.rap/bnd.bnd +++ b/swt/rap/org.argeo.cms.swt.rap/bnd.bnd @@ -2,8 +2,6 @@ Import-Package:\ org.argeo.api.acr,\ org.eclipse.swt,\ org.argeo.eclipse.ui,\ -javax.jcr.nodetype,\ -javax.jcr.security,\ org.eclipse.swt.graphics,\ javax.servlet.*;version="[3,5)",\ * diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java deleted file mode 100644 index 01ebb237e..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/AppUi.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.script.Invocable; -import javax.script.ScriptException; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.Selected; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsPane; -import org.argeo.cms.web.SimpleErgonomics; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.application.EntryPointFactory; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -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.osgi.framework.BundleContext; - -public class AppUi implements CmsUiProvider, Branding { - private final CmsScriptApp app; - - private CmsUiProvider ui; - private String createUi; - private Object impl; - private String script; - // private Branding branding = new Branding(); - - private EntryPointFactory factory; - - // Branding - private String themeId; - private String additionalHeaders; - private String bodyHtml; - private String pageTitle; - private String pageOverflow; - private String favicon; - - public AppUi(CmsScriptApp app) { - this.app = app; - } - - public AppUi(CmsScriptApp app, String scriptPath) { - this.app = app; - this.ui = new ScriptUi((BundleContext) app.getScriptEngine().get(CmsScriptRwtApplication.BC), - app.getScriptEngine(), scriptPath); - } - - public AppUi(CmsScriptApp app, CmsUiProvider uiProvider) { - this.app = app; - this.ui = uiProvider; - } - - public AppUi(CmsScriptApp app, EntryPointFactory factory) { - this.app = app; - this.factory = factory; - } - - public void apply(Repository repository, Application application, Branding appBranding, String path) { - Map factoryProperties = new HashMap<>(); - if (appBranding != null) - appBranding.applyBranding(factoryProperties); - applyBranding(factoryProperties); - if (factory != null) { - application.addEntryPoint("/" + path, factory, factoryProperties); - } else { - EntryPointFactory entryPointFactory = new EntryPointFactory() { - @Override - public EntryPoint create() { - SimpleErgonomics ergonomics = new SimpleErgonomics(repository, CmsConstants.SYS_WORKSPACE, - "/home/root/argeo:keyring", AppUi.this, factoryProperties); -// CmsUiProvider header = app.getHeader(); -// if (header != null) -// ergonomics.setHeader(header); - app.applySides(ergonomics); - Integer headerHeight = app.getHeaderHeight(); - if (headerHeight != null) - ergonomics.setHeaderHeight(headerHeight); - return ergonomics; - } - }; - application.addEntryPoint("/" + path, entryPointFactory, factoryProperties); - } - } - - public void setUi(CmsUiProvider uiProvider) { - this.ui = uiProvider; - } - - public void applyBranding(Map properties) { - if (themeId != null) - properties.put(WebClient.THEME_ID, themeId); - if (additionalHeaders != null) - properties.put(WebClient.HEAD_HTML, additionalHeaders); - if (bodyHtml != null) - properties.put(WebClient.BODY_HTML, bodyHtml); - if (pageTitle != null) - properties.put(WebClient.PAGE_TITLE, pageTitle); - if (pageOverflow != null) - properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); - if (favicon != null) - properties.put(WebClient.FAVICON, favicon); - } - - // public Branding getBranding() { - // return branding; - // } - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - CmsPane cmsPane = new CmsPane(parent, SWT.NONE); - - if (false) { - // QA - CmsSwtUtils.style(cmsPane.getQaArea(), "qa"); - Button reload = new Button(cmsPane.getQaArea(), SWT.FLAT); - CmsSwtUtils.style(reload, "qa"); - reload.setText("Reload"); - reload.addSelectionListener(new Selected() { - private static final long serialVersionUID = 1L; - - @Override - public void widgetSelected(SelectionEvent e) { - new Thread() { - @Override - public void run() { - app.reload(); - } - }.start(); - RWT.getClient().getService(JavaScriptExecutor.class) - .execute("setTimeout('location.reload()',1000)"); - } - }); - - // Support - CmsSwtUtils.style(cmsPane.getSupportArea(), "support"); - Label msg = new Label(cmsPane.getSupportArea(), SWT.NONE); - CmsSwtUtils.style(msg, "support"); - msg.setText("UNSUPPORTED DEVELOPMENT VERSION"); - } - - if (ui != null) { - ui.createUi(cmsPane.getMainArea(), context); - } - if (createUi != null) { - Invocable invocable = (Invocable) app.getScriptEngine(); - try { - invocable.invokeFunction(createUi, cmsPane.getMainArea(), context); - - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - if (impl != null) { - Invocable invocable = (Invocable) app.getScriptEngine(); - try { - invocable.invokeMethod(impl, "createUi", cmsPane.getMainArea(), context); - - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - // Invocable invocable = (Invocable) app.getScriptEngine(); - // try { - // invocable.invokeMethod(AppUi.this, "initUi", parent, context); - // - // } catch (NoSuchMethodException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } catch (ScriptException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - - return null; - } - - public void setCreateUi(String createUi) { - this.createUi = createUi; - } - - public void setImpl(Object impl) { - this.impl = impl; - } - - public Object getImpl() { - return impl; - } - - public String getScript() { - return script; - } - - public void setScript(String script) { - this.script = script; - } - - // Branding - public String getThemeId() { - return themeId; - } - - public void setThemeId(String themeId) { - this.themeId = themeId; - } - - public String getAdditionalHeaders() { - return additionalHeaders; - } - - public void setAdditionalHeaders(String additionalHeaders) { - this.additionalHeaders = additionalHeaders; - } - - public String getBodyHtml() { - return bodyHtml; - } - - public void setBodyHtml(String bodyHtml) { - this.bodyHtml = bodyHtml; - } - - public String getPageTitle() { - return pageTitle; - } - - public void setPageTitle(String pageTitle) { - this.pageTitle = pageTitle; - } - - public String getPageOverflow() { - return pageOverflow; - } - - public void setPageOverflow(String pageOverflow) { - this.pageOverflow = pageOverflow; - } - - public String getFavicon() { - return favicon; - } - - public void setFavicon(String favicon) { - this.favicon = favicon; - } - -} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java deleted file mode 100644 index f72338ef7..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/Branding.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.util.Map; - -public interface Branding { - public void applyBranding(Map properties); - - public String getThemeId(); - - public String getAdditionalHeaders(); - - public String getBodyHtml(); - - public String getPageTitle(); - - public String getPageOverflow(); - - public String getFavicon(); - -} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java deleted file mode 100644 index 6b3a670d7..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptApp.java +++ /dev/null @@ -1,421 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.PropertyIterator; -import javax.jcr.PropertyType; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.script.ScriptEngine; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsTheme; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.web.BundleResourceLoader; -import org.argeo.cms.web.SimpleErgonomics; -import org.argeo.cms.web.WebThemeUtils; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.http.HttpContext; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; - -public class CmsScriptApp implements Branding { - public final static String CONTEXT_NAME = "contextName"; - - ServiceRegistration appConfigReg; - - private ScriptEngine scriptEngine; - - private final static CmsLog log = CmsLog.getLog(CmsScriptApp.class); - - private String webPath; - private String repo = "(cn=node)"; - - // private Branding branding = new Branding(); - private CmsTheme theme; - - private List resources = new ArrayList<>(); - - private Map ui = new HashMap<>(); - - private CmsUiProvider header; - private Integer headerHeight = null; - private CmsUiProvider lead; - private CmsUiProvider end; - private CmsUiProvider footer; - - // Branding - private String themeId; - private String additionalHeaders; - private String bodyHtml; - private String pageTitle; - private String pageOverflow; - private String favicon; - - public CmsScriptApp(ScriptEngine scriptEngine) { - super(); - this.scriptEngine = scriptEngine; - } - - public void apply(BundleContext bundleContext, Repository repository, Application application) { - BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); - - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); - - application.setExceptionHandler(new CmsExceptionHandler()); - - // loading animated gif - application.addResource(CmsUiConstants.LOADING_IMAGE, createResourceLoader(CmsUiConstants.LOADING_IMAGE)); - // empty image - application.addResource(CmsUiConstants.NO_IMAGE, createResourceLoader(CmsUiConstants.NO_IMAGE)); - - for (String resource : resources) { - application.addResource(resource, bundleRL); - if (log.isTraceEnabled()) - log.trace("Resource " + resource); - } - - if (theme != null) { - WebThemeUtils.apply(application, theme); - String themeHeaders = theme.getHtmlHeaders(); - if (themeHeaders != null) { - if (additionalHeaders == null) - additionalHeaders = themeHeaders; - else - additionalHeaders = themeHeaders + "\n" + additionalHeaders; - } - themeId = theme.getThemeId(); - } - - // client JavaScript - Bundle appBundle = bundleRL.getBundle(); - BundleContext bc = appBundle.getBundleContext(); - HttpService httpService = bc.getService(bc.getServiceReference(HttpService.class)); - HttpContext httpContext = new BundleHttpContext(bc); - Enumeration themeResources = appBundle.findEntries("/js/", "*", true); - if (themeResources != null) - bundleResources: while (themeResources.hasMoreElements()) { - try { - String name = themeResources.nextElement().getPath(); - if (name.endsWith("/")) - continue bundleResources; - String alias = "/" + getWebPath() + name; - - httpService.registerResources(alias, name, httpContext); - if (log.isDebugEnabled()) - log.debug("Mapped " + name + " to alias " + alias); - - } catch (NamespaceException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - // App UIs - for (String appUiName : ui.keySet()) { - AppUi appUi = ui.get(appUiName); - appUi.apply(repository, application, this, appUiName); - - } - - } - - public void applySides(SimpleErgonomics simpleErgonomics) { - simpleErgonomics.setHeader(header); - simpleErgonomics.setLead(lead); - simpleErgonomics.setEnd(end); - simpleErgonomics.setFooter(footer); - } - - public void register(BundleContext bundleContext, ApplicationConfiguration appConfig) { - Hashtable props = new Hashtable<>(); - props.put(CONTEXT_NAME, webPath); - appConfigReg = bundleContext.registerService(ApplicationConfiguration.class, appConfig, props); - } - - public void reload() { - BundleContext bundleContext = appConfigReg.getReference().getBundle().getBundleContext(); - ApplicationConfiguration appConfig = bundleContext.getService(appConfigReg.getReference()); - appConfigReg.unregister(); - register(bundleContext, appConfig); - - // BundleContext bundleContext = (BundleContext) - // getScriptEngine().get("bundleContext"); - // try { - // Bundle bundle = bundleContext.getBundle(); - // bundle.stop(); - // bundle.start(); - // } catch (BundleException e) { - // // TODO Auto-generated catch block - // e.printStackTrace(); - // } - } - - private static ResourceLoader createResourceLoader(final String resourceName) { - return new ResourceLoader() { - public InputStream getResourceAsStream(String resourceName) throws IOException { - return getClass().getClassLoader().getResourceAsStream(resourceName); - } - }; - } - - public List getResources() { - return resources; - } - - public AppUi newUi(String name) { - if (ui.containsKey(name)) - throw new IllegalArgumentException("There is already an UI named " + name); - AppUi appUi = new AppUi(this); - // appUi.setApp(this); - ui.put(name, appUi); - return appUi; - } - - public void addUi(String name, AppUi appUi) { - if (ui.containsKey(name)) - throw new IllegalArgumentException("There is already an UI named " + name); - // appUi.setApp(this); - ui.put(name, appUi); - } - - public void applyBranding(Map properties) { - if (themeId != null) - properties.put(WebClient.THEME_ID, themeId); - if (additionalHeaders != null) - properties.put(WebClient.HEAD_HTML, additionalHeaders); - if (bodyHtml != null) - properties.put(WebClient.BODY_HTML, bodyHtml); - if (pageTitle != null) - properties.put(WebClient.PAGE_TITLE, pageTitle); - if (pageOverflow != null) - properties.put(WebClient.PAGE_OVERFLOW, pageOverflow); - if (favicon != null) - properties.put(WebClient.FAVICON, favicon); - } - - class CmsExceptionHandler implements ExceptionHandler { - - @Override - public void handleException(Throwable throwable) { - // TODO be smarter - CmsUiUtils.getCmsView().exception(throwable); - } - - } - - // public Branding getBranding() { - // return branding; - // } - - ScriptEngine getScriptEngine() { - return scriptEngine; - } - - public static String toJson(Node node) { - try { - StringBuilder sb = new StringBuilder(); - sb.append('{'); - PropertyIterator pit = node.getProperties(); - int count = 0; - while (pit.hasNext()) { - Property p = pit.nextProperty(); - int type = p.getType(); - if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE || type == PropertyType.PATH) { - Node ref = p.getNode(); - if (count != 0) - sb.append(','); - // TODO limit depth? - sb.append(toJson(ref)); - count++; - } else if (!p.isMultiple()) { - if (count != 0) - sb.append(','); - sb.append('\"').append(p.getName()).append("\":\"").append(p.getString()).append('\"'); - count++; - } - } - sb.append('}'); - return sb.toString(); - } catch (RepositoryException e) { - throw new CmsException("Cannot convert " + node + " to JSON", e); - } - } - - public void fromJson(Node node, String json) { - // TODO - } - - public CmsTheme getTheme() { - return theme; - } - - public void setTheme(CmsTheme theme) { - this.theme = theme; - } - - public String getWebPath() { - return webPath; - } - - public void setWebPath(String context) { - this.webPath = context; - } - - public String getRepo() { - return repo; - } - - public void setRepo(String repo) { - this.repo = repo; - } - - public Map getUi() { - return ui; - } - - public void setUi(Map ui) { - this.ui = ui; - } - - // Branding - public String getThemeId() { - return themeId; - } - - public void setThemeId(String themeId) { - this.themeId = themeId; - } - - public String getAdditionalHeaders() { - return additionalHeaders; - } - - public void setAdditionalHeaders(String additionalHeaders) { - this.additionalHeaders = additionalHeaders; - } - - public String getBodyHtml() { - return bodyHtml; - } - - public void setBodyHtml(String bodyHtml) { - this.bodyHtml = bodyHtml; - } - - public String getPageTitle() { - return pageTitle; - } - - public void setPageTitle(String pageTitle) { - this.pageTitle = pageTitle; - } - - public String getPageOverflow() { - return pageOverflow; - } - - public void setPageOverflow(String pageOverflow) { - this.pageOverflow = pageOverflow; - } - - public String getFavicon() { - return favicon; - } - - public void setFavicon(String favicon) { - this.favicon = favicon; - } - - public CmsUiProvider getHeader() { - return header; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public Integer getHeaderHeight() { - return headerHeight; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public CmsUiProvider getLead() { - return lead; - } - - public void setLead(CmsUiProvider lead) { - this.lead = lead; - } - - public CmsUiProvider getEnd() { - return end; - } - - public void setEnd(CmsUiProvider end) { - this.end = end; - } - - public CmsUiProvider getFooter() { - return footer; - } - - public void setFooter(CmsUiProvider footer) { - this.footer = footer; - } - - static class BundleHttpContext implements HttpContext { - private BundleContext bundleContext; - - public BundleHttpContext(BundleContext bundleContext) { - super(); - this.bundleContext = bundleContext; - } - - @Override - public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException { - // TODO Auto-generated method stub - return true; - } - - @Override - public URL getResource(String name) { - - return bundleContext.getBundle().getEntry(name); - } - - @Override - public String getMimeType(String name) { - return null; - } - - } - -} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java deleted file mode 100644 index 9879fb019..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/CmsScriptRwtApplication.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; - -import javax.jcr.Repository; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.swt.CmsException; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.framework.wiring.BundleWiring; - -public class CmsScriptRwtApplication implements ApplicationConfiguration { - public final static String APP = "APP"; - public final static String BC = "BC"; - - private final CmsLog log = CmsLog.getLog(CmsScriptRwtApplication.class); - - BundleContext bundleContext; - Repository repository; - - ScriptEngine engine; - - public void init(BundleContext bundleContext) { - this.bundleContext = bundleContext; - ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); - ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); - try { -// Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager -// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); -// engine = scriptEngineManager.getEngineByName("JavaScript"); -// if (engine == null) {// Nashorn -// Thread.currentThread().setContextClassLoader(originalCcl); -// scriptEngineManager = new ScriptEngineManager(); -// Thread.currentThread().setContextClassLoader(bundleCl); -// engine = scriptEngineManager.getEngineByName("JavaScript"); -// } - engine = loadScriptEngine(originalCcl, bundleCl); - - // Load script - URL appUrl = bundleContext.getBundle().getEntry("cms/app.js"); - // System.out.println("Loading " + appUrl); - // System.out.println("Loading " + appUrl.getHost()); - // System.out.println("Loading " + appUrl.getPath()); - - CmsScriptApp app = new CmsScriptApp(engine); - engine.put(APP, app); - engine.put(BC, bundleContext); - try (Reader reader = new InputStreamReader(appUrl.openStream())) { - engine.eval(reader); - } catch (IOException | ScriptException e) { - throw new CmsException("Cannot execute " + appUrl, e); - } - - if (log.isDebugEnabled()) - log.debug("CMS script app initialized from " + appUrl); - - } catch (Exception e) { - e.printStackTrace(); - } finally { - Thread.currentThread().setContextClassLoader(originalCcl); - } - } - - public void destroy(BundleContext bundleContext) { - engine = null; - } - - @Override - public void configure(Application application) { - load(application); - } - - void load(Application application) { - CmsScriptApp app = getApp(); - app.apply(bundleContext, repository, application); - if (log.isDebugEnabled()) - log.debug("CMS script app loaded to " + app.getWebPath()); - } - - CmsScriptApp getApp() { - if (engine == null) - throw new IllegalStateException("CMS script app is not initialized"); - return (CmsScriptApp) engine.get(APP); - } - - void update() { - - try { - bundleContext.getBundle().update(); - } catch (BundleException e) { - e.printStackTrace(); - } - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - private static ScriptEngine loadScriptEngine(ClassLoader originalCcl, ClassLoader bundleCl) { - Thread.currentThread().setContextClassLoader(bundleCl);// GraalVM needs it to be before creating manager - ScriptEngineManager scriptEngineManager = new ScriptEngineManager(bundleCl); - ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript"); - if (engine == null) {// Nashorn - Thread.currentThread().setContextClassLoader(originalCcl); - scriptEngineManager = new ScriptEngineManager(); - Thread.currentThread().setContextClassLoader(bundleCl); - engine = scriptEngineManager.getEngineByName("JavaScript"); - } - return engine; - } -} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java deleted file mode 100644 index a55095371..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptAppActivator.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.argeo.cms.ui.script; - -import javax.jcr.Repository; - -import org.argeo.api.cms.CmsLog; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; - -public class ScriptAppActivator implements BundleActivator { - private final static CmsLog log = CmsLog.getLog(ScriptAppActivator.class); - - @Override - public void start(BundleContext context) throws Exception { - try { - CmsScriptRwtApplication appConfig = new CmsScriptRwtApplication(); - appConfig.init(context); - CmsScriptApp app = appConfig.getApp(); - ServiceTracker repoSt = new ServiceTracker(context, - FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) { - - @Override - public Repository addingService(ServiceReference reference) { - Repository repository = super.addingService(reference); - appConfig.setRepository(repository); - CmsScriptApp app = appConfig.getApp(); - app.register(context, appConfig); - return repository; - } - - }; - repoSt.open(); - } catch (Exception e) { - log.error("Cannot initialise script bundle " + context.getBundle().getSymbolicName(), e); - throw e; - } - } - - @Override - public void stop(BundleContext context) throws Exception { - } - -} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java deleted file mode 100644 index 0c870e1e8..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/ScriptUi.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.argeo.cms.ui.script; - -import java.net.URL; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.ui.CmsUiProvider; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.osgi.framework.BundleContext; - -class ScriptUi implements CmsUiProvider { - private final static CmsLog log = CmsLog.getLog(ScriptUi.class); - - private boolean development = true; - private ScriptEngine scriptEngine; - - private URL appUrl; - // private BundleContext bundleContext; - // private String path; - - // private Bindings bindings; - // private String script; - - public ScriptUi(BundleContext bundleContext,ScriptEngine scriptEngine, String path) { - this.scriptEngine = scriptEngine; -//// ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); -// ClassLoader bundleCl = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader(); -// ClassLoader originalCcl = Thread.currentThread().getContextClassLoader(); -// try { -//// Thread.currentThread().setContextClassLoader(bundleCl); -//// scriptEngine = scriptEngineManager.getEngineByName("JavaScript"); -//// scriptEngine.put(CmsScriptRwtApplication.BC, bundleContext); -// scriptEngine = CmsScriptRwtApplication.loadScriptEngine(originalCcl, bundleCl); -// -// } catch (Exception e) { -// e.printStackTrace(); -// } finally { -// Thread.currentThread().setContextClassLoader(originalCcl); -// } - this.appUrl = bundleContext.getBundle().getEntry(path); - load(); - } - - private void load() { -// try (Reader reader = new InputStreamReader(appUrl.openStream())) { -// scriptEngine.eval(reader); -// } catch (IOException | ScriptException e) { -// log.warn("Cannot execute " + appUrl, e); -// } - - try { - scriptEngine.eval("load('" + appUrl + "')"); - } catch (ScriptException e) { - log.warn("Cannot execute " + appUrl, e); - } - - } - - // public ScriptUiProvider(ScriptEngine scriptEngine, String script) throws - // ScriptException { - // super(); - // this.scriptEngine = scriptEngine; - // this.script = script; - // bindings = scriptEngine.createBindings(); - // scriptEngine.eval(script, bindings); - // } - - @Override - public Control createUi(Composite parent, Node context) throws RepositoryException { - long begin = System.currentTimeMillis(); - // if (bindings == null) { - // bindings = scriptEngine.createBindings(); - // try { - // scriptEngine.eval(script, bindings); - // } catch (ScriptException e) { - // log.warn("Cannot evaluate script", e); - // } - // } - // Bindings bindings = scriptEngine.createBindings(); - // bindings.put("parent", parent); - // bindings.put("context", context); - // URL appUrl = bundleContext.getBundle().getEntry(path); - // try (Reader reader = new InputStreamReader(appUrl.openStream())) { - // scriptEngine.eval(reader,bindings); - // } catch (IOException | ScriptException e) { - // log.warn("Cannot execute " + appUrl, e); - // } - - if (development) - load(); - - Invocable invocable = (Invocable) scriptEngine; - try { - invocable.invokeFunction("createUi", parent, context); - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ScriptException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - long duration = System.currentTimeMillis() - begin; - if (log.isTraceEnabled()) - log.trace(appUrl + " UI in " + duration + " ms"); - return null; - } - -} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js deleted file mode 100644 index be9618dcb..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/cms.js +++ /dev/null @@ -1,90 +0,0 @@ -// CMS -var ScrolledPage = Java.type('org.argeo.cms.ui.widgets.ScrolledPage'); - -var CmsScriptApp = Java.type('org.argeo.cms.ui.script.CmsScriptApp'); -var AppUi = Java.type('org.argeo.cms.ui.script.AppUi'); -var Theme = Java.type('org.argeo.cms.ui.script.Theme'); -var ScriptUi = Java.type('org.argeo.cms.ui.script.ScriptUi'); -var CmsUtils = Java.type('org.argeo.cms.ui.util.CmsUiUtils'); -var SimpleCmsHeader = Java.type('org.argeo.cms.ui.util.SimpleCmsHeader'); -var CmsLink = Java.type('org.argeo.cms.ui.util.CmsLink'); -var MenuLink = Java.type('org.argeo.cms.ui.util.MenuLink'); -var UserMenuLink = Java.type('org.argeo.cms.ui.util.UserMenuLink'); - -// SWT -var SWT = Java.type('org.eclipse.swt.SWT'); -var Composite = Java.type('org.eclipse.swt.widgets.Composite'); -var Label = Java.type('org.eclipse.swt.widgets.Label'); -var Button = Java.type('org.eclipse.swt.widgets.Button'); -var Text = Java.type('org.eclipse.swt.widgets.Text'); -var Browser = Java.type('org.eclipse.swt.browser.Browser'); - -var FillLayout = Java.type('org.eclipse.swt.layout.FillLayout'); -var GridLayout = Java.type('org.eclipse.swt.layout.GridLayout'); -var RowLayout = Java.type('org.eclipse.swt.layout.RowLayout'); -var FormLayout = Java.type('org.eclipse.swt.layout.FormLayout'); -var GridData = Java.type('org.eclipse.swt.layout.GridData'); - -function loadNode(node) { - var json = CmsScriptApp.toJson(node) - var fromJson = JSON.parse(json) - return fromJson -} - -function newArea(parent, style, layout) { - var control = new Composite(parent, SWT.NONE) - control.setLayout(layout) - CmsUtils.style(control, style) - return control -} - -function newLabel(parent, style, text) { - var control = new Label(parent, SWT.WRAP) - control.setText(text) - CmsUtils.style(control, style) - CmsUtils.markup(control) - return control -} - -function newButton(parent, style, text) { - var control = new Button(parent, SWT.FLAT) - control.setText(text) - CmsUtils.style(control, style) - CmsUtils.markup(control) - return control -} - -function newFormLabel(parent, style, text) { - return newLabel(parent, style, '' + text + '') -} - -function newText(parent, style, msg) { - var control = new Text(parent, SWT.NONE) - control.setMessage(msg) - CmsUtils.style(control, style) - return control -} - -function newScrolledPage(parent) { - var scrolled = new ScrolledPage(parent, SWT.NONE) - scrolled.setLayoutData(CmsUtils.fillAll()) - scrolled.setLayout(CmsUtils.noSpaceGridLayout()) - var page = new Composite(scrolled, SWT.NONE) - page.setLayout(CmsUtils.noSpaceGridLayout()) - page.setBackgroundMode(SWT.INHERIT_NONE) - return page -} - -function gridData(control) { - var gridData = new GridData() - control.setLayoutData(gridData) - return gridData -} - -function gridData(control, hAlign, vAlign) { - var gridData = new GridData(hAlign, vAlign, false, false) - control.setLayoutData(gridData) - return gridData -} - -// print(__FILE__, __LINE__, __DIR__) diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java deleted file mode 100644 index 7440596ab..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/ui/script/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Argeo CMS user interface scripting. */ -package org.argeo.cms.ui.script; \ No newline at end of file diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java deleted file mode 100644 index f3269f2c4..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/AbstractCmsEntryPoint.java +++ /dev/null @@ -1,398 +0,0 @@ -package org.argeo.cms.web; - -import static org.argeo.util.directory.ldap.SharedSecret.X_SHARED_SECRET; - -import java.io.IOException; -import java.security.PrivilegedAction; -import java.util.HashMap; -import java.util.Map; - -import javax.jcr.Node; -import javax.jcr.PathNotFoundException; -import javax.jcr.Property; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; -import javax.servlet.http.HttpServletRequest; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsView; -import org.argeo.api.cms.CmsAuth; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.auth.RemoteAuthCallback; -import org.argeo.cms.auth.RemoteAuthCallbackHandler; -import org.argeo.cms.servlet.ServletHttpRequest; -import org.argeo.cms.servlet.ServletHttpResponse; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.eclipse.ui.specific.UiContext; -import org.argeo.jcr.JcrUtils; -import org.argeo.util.directory.ldap.AuthPassword; -import org.argeo.util.directory.ldap.SharedSecret; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.AbstractEntryPoint; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.BrowserNavigation; -import org.eclipse.rap.rwt.client.service.BrowserNavigationEvent; -import org.eclipse.rap.rwt.client.service.BrowserNavigationListener; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; - -/** Manages history and navigation */ -@Deprecated -public abstract class AbstractCmsEntryPoint extends AbstractEntryPoint implements CmsView { - private static final long serialVersionUID = 906558779562569784L; - - private final CmsLog log = CmsLog.getLog(AbstractCmsEntryPoint.class); - - // private final Subject subject; - private LoginContext loginContext; - - private final Repository repository; - private final String workspace; - private final String defaultPath; - private final Map factoryProperties; - - // Current state - private Session session; - private Node node; - private String nodePath;// useful when changing auth - private String state; - private Throwable exception; - - // Client services - private final JavaScriptExecutor jsExecutor; - private final BrowserNavigation browserNavigation; - - public AbstractCmsEntryPoint(Repository repository, String workspace, String defaultPath, - Map factoryProperties) { - this.repository = repository; - this.workspace = workspace; - this.defaultPath = defaultPath; - this.factoryProperties = new HashMap(factoryProperties); - // subject = new Subject(); - - // Initial login - LoginContext lc; - try { - lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, - new RemoteAuthCallbackHandler(new ServletHttpRequest(UiContext.getHttpRequest()), - new ServletHttpResponse(UiContext.getHttpResponse()))); - lc.login(); - } catch (LoginException e) { - try { - lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS); - lc.login(); - } catch (LoginException e1) { - throw new CmsException("Cannot log in as anonymous", e1); - } - } - authChange(lc); - - jsExecutor = RWT.getClient().getService(JavaScriptExecutor.class); - browserNavigation = RWT.getClient().getService(BrowserNavigation.class); - if (browserNavigation != null) - browserNavigation.addBrowserNavigationListener(new CmsNavigationListener()); - } - - @Override - protected Shell createShell(Display display) { - Shell shell = super.createShell(display); - shell.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_SHELL); - display.disposeExec(new Runnable() { - - @Override - public void run() { - if (log.isTraceEnabled()) - log.trace("Logging out " + session); - JcrUtils.logoutQuietly(session); - } - }); - return shell; - } - - @Override - protected final void createContents(final Composite parent) { - // UiContext.setData(CmsView.KEY, this); - CmsSwtUtils.registerCmsView(parent.getShell(), this); - Subject.doAs(getSubject(), new PrivilegedAction() { - @Override - public Void run() { - try { - initUi(parent); - } catch (Exception e) { - throw new CmsException("Cannot create entrypoint contents", e); - } - return null; - } - }); - } - - /** Create UI */ - protected abstract void initUi(Composite parent); - - /** Recreate UI after navigation or auth change */ - protected abstract void refresh(); - - /** - * The node to return when no node was found (for authenticated users and - * anonymous) - */ -// private Node getDefaultNode(Session session) throws RepositoryException { -// if (!session.hasPermission(defaultPath, "read")) { -// String userId = session.getUserID(); -// if (userId.equals(NodeConstants.ROLE_ANONYMOUS)) -// // TODO throw a special exception -// throw new CmsException("Login required"); -// else -// throw new CmsException("Unauthorized"); -// } -// return session.getNode(defaultPath); -// } - - protected String getBaseTitle() { - return factoryProperties.get(WebClient.PAGE_TITLE); - } - - public void navigateTo(String state) { - exception = null; - String title = setState(state); - doRefresh(); - if (browserNavigation != null) - browserNavigation.pushState(state, title); - } - - // @Override - // public synchronized Subject getSubject() { - // return subject; - // } - - // @Override - // public LoginContext getLoginContext() { - // return loginContext; - // } - protected Subject getSubject() { - return loginContext.getSubject(); - } - - @Override - public boolean isAnonymous() { - return CurrentUser.isAnonymous(getSubject()); - } - - @Override - public synchronized void logout() { - if (loginContext == null) - throw new CmsException("Login context should not be null"); - try { - CurrentUser.logoutCmsSession(loginContext.getSubject()); - loginContext.logout(); - LoginContext anonymousLc = new LoginContext(CmsAuth.LOGIN_CONTEXT_ANONYMOUS); - anonymousLc.login(); - authChange(anonymousLc); - } catch (LoginException e) { - log.error("Cannot logout", e); - } - } - - @Override - public synchronized void authChange(LoginContext lc) { - if (lc == null) - throw new CmsException("Login context cannot be null"); - // logout previous login context - if (this.loginContext != null) - try { - this.loginContext.logout(); - } catch (LoginException e1) { - log.warn("Could not log out: " + e1); - } - this.loginContext = lc; - Subject.doAs(getSubject(), new PrivilegedAction() { - - @Override - public Void run() { - try { - JcrUtils.logoutQuietly(session); - session = repository.login(workspace); - if (nodePath != null) - try { - node = session.getNode(nodePath); - } catch (PathNotFoundException e) { - navigateTo("~"); - } - - // refresh UI - doRefresh(); - } catch (RepositoryException e) { - throw new CmsException("Cannot perform auth change", e); - } - return null; - } - - }); - } - - @Override - public void exception(final Throwable e) { - AbstractCmsEntryPoint.this.exception = e; - log.error("Unexpected exception in CMS", e); - doRefresh(); - } - - protected synchronized void doRefresh() { - Subject.doAs(getSubject(), new PrivilegedAction() { - @Override - public Void run() { - refresh(); - return null; - } - }); - } - - /** Sets the state of the entry point and retrieve the related JCR node. */ - protected synchronized String setState(String newState) { - String previousState = this.state; - - String newNodePath = null; - String prefix = null; - this.state = newState; - if (newState.equals("~")) - this.state = ""; - - try { - int firstSlash = state.indexOf('/'); - if (firstSlash == 0) { - newNodePath = state; - prefix = ""; - } else if (firstSlash > 0) { - prefix = state.substring(0, firstSlash); - newNodePath = state.substring(firstSlash); - } else { - newNodePath = defaultPath; - prefix = state; - - } - - // auth - int colonIndex = prefix.indexOf('$'); - if (colonIndex > 0) { - SharedSecret token = new SharedSecret(new AuthPassword(X_SHARED_SECRET + '$' + prefix)) { - - @Override - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - super.handle(callbacks); - // handle HTTP context - for (Callback callback : callbacks) { - if (callback instanceof RemoteAuthCallback) { - ((RemoteAuthCallback) callback) - .setRequest(new ServletHttpRequest(UiContext.getHttpRequest())); - ((RemoteAuthCallback) callback) - .setResponse(new ServletHttpResponse(UiContext.getHttpResponse())); - } - } - } - }; - LoginContext lc = new LoginContext(CmsAuth.LOGIN_CONTEXT_USER, token); - lc.login(); - authChange(lc);// sets the node as well - // } else { - // // TODO check consistency - // } - } else { - Node newNode = null; - if (session.nodeExists(newNodePath)) - newNode = session.getNode(newNodePath); - else { -// throw new CmsException("Data " + newNodePath + " does not exist"); - newNode = null; - } - setNode(newNode); - } - String title = publishMetaData(getNode()); - - if (log.isTraceEnabled()) - log.trace("node=" + newNodePath + ", state=" + state + " (prefix=" + prefix + ")"); - - return title; - } catch (Exception e) { - log.error("Cannot set state '" + state + "'", e); - if (state.equals("") || newState.equals("~") || newState.equals(previousState)) - return "Unrecoverable exception : " + e.getClass().getSimpleName(); - if (previousState.equals("")) - previousState = "~"; - navigateTo(previousState); - throw new CmsException("Unexpected issue when accessing #" + newState, e); - } - } - - private String publishMetaData(Node node) throws RepositoryException { - // Title - String title; - if (node != null && node.isNodeType(NodeType.MIX_TITLE) && node.hasProperty(Property.JCR_TITLE)) - title = node.getProperty(Property.JCR_TITLE).getString() + " - " + getBaseTitle(); - else - title = getBaseTitle(); - - HttpServletRequest request = UiContext.getHttpRequest(); - if (request == null) - return null; - - StringBuilder js = new StringBuilder(); - if (title == null) - title = ""; - title = title.replace("'", "\\'");// sanitize - js.append("document.title = '" + title + "';"); - jsExecutor.execute(js.toString()); - return title; - } - - // Simply remove some illegal character - // private String clean(String stringToClean) { - // return stringToClean.replaceAll("'", "").replaceAll("\\n", "") - // .replaceAll("\\t", ""); - // } - - protected synchronized Node getNode() { - return node; - } - - private synchronized void setNode(Node node) throws RepositoryException { - this.node = node; - this.nodePath = node == null ? null : node.getPath(); - } - - protected String getState() { - return state; - } - - protected Throwable getException() { - return exception; - } - - protected void resetException() { - exception = null; - } - - protected Session getSession() { - return session; - } - - private class CmsNavigationListener implements BrowserNavigationListener { - private static final long serialVersionUID = -3591018803430389270L; - - @Override - public void navigated(BrowserNavigationEvent event) { - setState(event.getState()); - doRefresh(); - } - } -} \ No newline at end of file diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java index 159719720..477a6569d 100644 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java +++ b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebEntryPoint.java @@ -27,8 +27,8 @@ import org.argeo.cms.servlet.ServletHttpRequest; import org.argeo.cms.servlet.ServletHttpResponse; import org.argeo.cms.swt.CmsSwtUtils; import org.argeo.cms.swt.SimpleSwtUxContext; +import org.argeo.cms.swt.acr.AcrSwtImageManager; import org.argeo.cms.swt.dialogs.CmsFeedback; -import org.argeo.cms.ui.util.DefaultImageManager; import org.argeo.eclipse.ui.specific.UiContext; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.application.EntryPoint; @@ -111,7 +111,7 @@ public class CmsWebEntryPoint implements EntryPoint, CmsView, BrowserNavigationL public Void run() { try { uxContext = new SimpleSwtUxContext(); - imageManager = new DefaultImageManager(); + imageManager = (CmsImageManager) new AcrSwtImageManager(); CmsSession cmsSession = getCmsSession(); if (cmsSession != null) { UiContext.setLocale(cmsSession.getLocale()); diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java deleted file mode 100644 index 38a9b4449..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleApp.java +++ /dev/null @@ -1,414 +0,0 @@ -package org.argeo.cms.web; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.security.Privilege; -import javax.jcr.version.VersionManager; - -import org.argeo.api.cms.CmsConstants; -import org.argeo.api.cms.CmsLog; -import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.ui.CmsUiConstants; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.LifeCycleUiProvider; -import org.argeo.cms.ui.util.CmsUiUtils; -import org.argeo.cms.ui.util.StyleSheetResourceLoader; -import org.argeo.jcr.JcrUtils; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.rap.rwt.application.Application; -import org.eclipse.rap.rwt.application.Application.OperationMode; -import org.eclipse.rap.rwt.application.ApplicationConfiguration; -import org.eclipse.rap.rwt.application.EntryPoint; -import org.eclipse.rap.rwt.application.EntryPointFactory; -import org.eclipse.rap.rwt.application.ExceptionHandler; -import org.eclipse.rap.rwt.client.WebClient; -import org.eclipse.rap.rwt.client.service.JavaScriptExecutor; -import org.eclipse.rap.rwt.service.ResourceLoader; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; - -/** A basic generic app based on {@link SimpleErgonomics}. */ -@Deprecated -public class SimpleApp implements CmsUiConstants, ApplicationConfiguration { - private final static CmsLog log = CmsLog.getLog(SimpleApp.class); - - private String contextName = null; - - private Map> branding = new HashMap>(); - private Map> styleSheets = new HashMap>(); - - private List resources = new ArrayList(); - - private BundleContext bundleContext; - - private Repository repository; - private String workspace = null; - private String jcrBasePath = "/"; - private List roPrincipals = Arrays.asList(CmsConstants.ROLE_ANONYMOUS, CmsConstants.ROLE_USER); - private List rwPrincipals = Arrays.asList(CmsConstants.ROLE_USER); - - private CmsUiProvider header; - private Map pages = new LinkedHashMap(); - - private Integer headerHeight = 40; - - private ServiceRegistration appReg; - - public void configure(Application application) { - try { - BundleResourceLoader bundleRL = new BundleResourceLoader(bundleContext.getBundle()); - - application.setOperationMode(OperationMode.SWT_COMPATIBILITY); - // application.setOperationMode(OperationMode.JEE_COMPATIBILITY); - - application.setExceptionHandler(new CmsExceptionHandler()); - - // loading animated gif - application.addResource(LOADING_IMAGE, createResourceLoader(LOADING_IMAGE)); - // empty image - application.addResource(NO_IMAGE, createResourceLoader(NO_IMAGE)); - - for (String resource : resources) { - application.addResource(resource, bundleRL); - if (log.isTraceEnabled()) - log.trace("Resource " + resource); - } - - Map defaultBranding = null; - if (branding.containsKey("*")) - defaultBranding = branding.get("*"); - // String defaultTheme = defaultBranding.get(WebClient.THEME_ID); - - // entry points - for (String page : pages.keySet()) { - Map properties = defaultBranding != null ? new HashMap(defaultBranding) - : new HashMap(); - if (branding.containsKey(page)) { - properties.putAll(branding.get(page)); - } - // favicon - if (properties.containsKey(WebClient.FAVICON)) { - String themeId = defaultBranding.get(WebClient.THEME_ID); - Bundle themeBundle = findThemeBundle(bundleContext, themeId); - String faviconRelPath = properties.get(WebClient.FAVICON); - application.addResource(faviconRelPath, - new BundleResourceLoader(themeBundle != null ? themeBundle : bundleContext.getBundle())); - if (log.isTraceEnabled()) - log.trace("Favicon " + faviconRelPath); - - } - - // page title - if (!properties.containsKey(WebClient.PAGE_TITLE)) { - if (page.length() > 0) - properties.put(WebClient.PAGE_TITLE, Character.toUpperCase(page.charAt(0)) + page.substring(1)); - } - - // default body HTML - if (!properties.containsKey(WebClient.BODY_HTML)) - properties.put(WebClient.BODY_HTML, DEFAULT_LOADING_BODY); - - // - // ADD ENTRY POINT - // - application.addEntryPoint("/" + page, - new CmsEntryPointFactory(pages.get(page), repository, workspace, properties), properties); - log.info("Page /" + page); - } - - // stylesheets and themes - Set themeBundles = new HashSet<>(); - for (String themeId : styleSheets.keySet()) { - Bundle themeBundle = findThemeBundle(bundleContext, themeId); - StyleSheetResourceLoader styleSheetRL = new StyleSheetResourceLoader( - themeBundle != null ? themeBundle : bundleContext.getBundle()); - if (themeBundle != null) - themeBundles.add(themeBundle); - List cssLst = styleSheets.get(themeId); - if (log.isDebugEnabled()) - log.debug("Theme " + themeId); - for (String css : cssLst) { - application.addStyleSheet(themeId, css, styleSheetRL); - if (log.isDebugEnabled()) - log.debug(" CSS " + css); - } - - } - for (Bundle themeBundle : themeBundles) { - BundleResourceLoader themeBRL = new BundleResourceLoader(themeBundle); - SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.png"); - SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.gif"); - SimpleApp.addThemeResources(application, themeBundle, themeBRL, "*.jpg"); - } - } catch (RuntimeException e) { - // Easier access to initialisation errors - log.error("Unexpected exception when configuring RWT application.", e); - throw e; - } - } - - public void init() throws RepositoryException { - Session session = null; - try { - session = CmsJcrUtils.openDataAdminSession(repository, workspace); - // session = JcrUtils.loginOrCreateWorkspace(repository, workspace); - VersionManager vm = session.getWorkspace().getVersionManager(); - JcrUtils.mkdirs(session, jcrBasePath); - session.save(); - if (!vm.isCheckedOut(jcrBasePath)) - vm.checkout(jcrBasePath); - for (String principal : rwPrincipals) - JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_WRITE); - for (String principal : roPrincipals) - JcrUtils.addPrivilege(session, jcrBasePath, principal, Privilege.JCR_READ); - - for (String pageName : pages.keySet()) { - try { - initPage(session, pages.get(pageName)); - session.save(); - } catch (Exception e) { - throw new CmsException("Cannot initialize page " + pageName, e); - } - } - - } finally { - JcrUtils.logoutQuietly(session); - } - - // publish to OSGi - register(); - } - - protected void initPage(Session adminSession, CmsUiProvider page) throws RepositoryException { - if (page instanceof LifeCycleUiProvider) - ((LifeCycleUiProvider) page).init(adminSession); - } - - public void destroy() { - for (String pageName : pages.keySet()) { - try { - CmsUiProvider page = pages.get(pageName); - if (page instanceof LifeCycleUiProvider) - ((LifeCycleUiProvider) page).destroy(); - } catch (Exception e) { - log.error("Cannot destroy page " + pageName, e); - } - } - } - - protected void register() { - Hashtable props = new Hashtable(); - if (contextName != null) - props.put("contextName", contextName); - appReg = bundleContext.registerService(ApplicationConfiguration.class, this, props); - if (log.isDebugEnabled()) - log.debug("Registered " + (contextName == null ? "/" : contextName)); - } - - protected void unregister() { - appReg.unregister(); - if (log.isDebugEnabled()) - log.debug("Unregistered " + (contextName == null ? "/" : contextName)); - } - - public void setRepository(Repository repository) { - this.repository = repository; - } - - public void setWorkspace(String workspace) { - this.workspace = workspace; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public void setPages(Map pages) { - this.pages = pages; - } - - public void setJcrBasePath(String basePath) { - this.jcrBasePath = basePath; - } - - public void setRoPrincipals(List roPrincipals) { - this.roPrincipals = roPrincipals; - } - - public void setRwPrincipals(List rwPrincipals) { - this.rwPrincipals = rwPrincipals; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public void setBranding(Map> branding) { - this.branding = branding; - } - - public void setStyleSheets(Map> styleSheets) { - this.styleSheets = styleSheets; - } - - public void setBundleContext(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - - public void setResources(List resources) { - this.resources = resources; - } - - public void setContextName(String contextName) { - this.contextName = contextName; - } - - private static void addThemeResources(Application application, Bundle themeBundle, BundleResourceLoader themeBRL, - String pattern) { - Enumeration themeResources = themeBundle.findEntries("/", pattern, true); - if (themeResources == null) - return; - while (themeResources.hasMoreElements()) { - String resource = themeResources.nextElement().getPath(); - // remove first '/' so that RWT registers it - resource = resource.substring(1); - if (!resource.endsWith("/")) { - application.addResource(resource, themeBRL); - if (log.isTraceEnabled()) - log.trace("Registered " + resource + " from theme " + themeBundle); - } - - } - - } - - private static Bundle findThemeBundle(BundleContext bundleContext, String themeId) { - if (themeId == null) - return null; - // TODO optimize - // TODO deal with multiple versions - Bundle themeBundle = null; - if (themeId != null) { - for (Bundle bundle : bundleContext.getBundles()) - if (themeId.equals(bundle.getSymbolicName())) { - themeBundle = bundle; - break; - } - } - return themeBundle; - } - - class CmsExceptionHandler implements ExceptionHandler { - - @Override - public void handleException(Throwable throwable) { - // TODO be smarter - CmsUiUtils.getCmsView().exception(throwable); - } - - } - - private class CmsEntryPointFactory implements EntryPointFactory { - private final CmsUiProvider page; - private final Repository repository; - private final String workspace; - private final Map properties; - - public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace, - Map properties) { - this.page = page; - this.repository = repository; - this.workspace = workspace; - this.properties = properties; - } - - @Override - public EntryPoint create() { - SimpleErgonomics entryPoint = new SimpleErgonomics(repository, workspace, jcrBasePath, page, properties) { - private static final long serialVersionUID = -637940404865527290L; - - @Override - protected void createAdminArea(Composite parent) { - Composite adminArea = new Composite(parent, SWT.NONE); - adminArea.setLayout(new FillLayout()); - Button refresh = new Button(adminArea, SWT.PUSH); - refresh.setText("Reload App"); - refresh.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -7671999525536351366L; - - @Override - public void widgetSelected(SelectionEvent e) { - long timeBeforeReload = 1000; - RWT.getClient().getService(JavaScriptExecutor.class).execute( - "setTimeout(function() { " + "location.reload();" + "}," + timeBeforeReload + ");"); - reloadApp(); - } - }); - } - }; - // entryPoint.setState(""); - entryPoint.setHeader(header); - entryPoint.setHeaderHeight(headerHeight); - // CmsSession.current.set(entryPoint); - return entryPoint; - } - - private void reloadApp() { - new Thread("Refresh app") { - @Override - public void run() { - unregister(); - register(); - } - }.start(); - } - } - - private static ResourceLoader createResourceLoader(final String resourceName) { - return new ResourceLoader() { - public InputStream getResourceAsStream(String resourceName) throws IOException { - return getClass().getClassLoader().getResourceAsStream(resourceName); - } - }; - } - - // private static ResourceLoader createUrlResourceLoader(final URL url) { - // return new ResourceLoader() { - // public InputStream getResourceAsStream(String resourceName) - // throws IOException { - // return url.openStream(); - // } - // }; - // } - - /* - * TEXTS - */ - private static String DEFAULT_LOADING_BODY = "" - + "" + ""; -} diff --git a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java b/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java deleted file mode 100644 index 783f6eb73..000000000 --- a/swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/SimpleErgonomics.java +++ /dev/null @@ -1,238 +0,0 @@ -package org.argeo.cms.web; - -import java.util.Map; -import java.util.UUID; - -import javax.jcr.Node; -import javax.jcr.Repository; -import javax.jcr.RepositoryException; - -import org.argeo.api.cms.CmsLog; -import org.argeo.api.cms.ux.CmsImageManager; -import org.argeo.api.cms.ux.UxContext; -import org.argeo.cms.swt.CmsException; -import org.argeo.cms.swt.CmsStyles; -import org.argeo.cms.swt.CmsSwtUtils; -import org.argeo.cms.swt.SimpleSwtUxContext; -import org.argeo.cms.ui.CmsUiProvider; -import org.argeo.cms.ui.util.DefaultImageManager; -import org.argeo.cms.ui.util.SystemNotifications; -import org.eclipse.rap.rwt.RWT; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -/** Simple header/body ergonomics. */ -@Deprecated -public class SimpleErgonomics extends AbstractCmsEntryPoint { - private static final long serialVersionUID = 8743413921359548523L; - - private final static CmsLog log = CmsLog.getLog(SimpleErgonomics.class); - - private boolean uiInitialized = false; - private Composite headerArea; - private Composite leftArea; - private Composite rightArea; - private Composite footerArea; - private Composite bodyArea; - private final CmsUiProvider uiProvider; - - private CmsUiProvider header; - private Integer headerHeight = 0; - private Integer footerHeight = 0; - private CmsUiProvider lead; - private CmsUiProvider end; - private CmsUiProvider footer; - - private CmsImageManager imageManager = new DefaultImageManager(); - private UxContext uxContext = null; - private String uid; - - public SimpleErgonomics(Repository repository, String workspace, String defaultPath, CmsUiProvider uiProvider, - Map factoryProperties) { - super(repository, workspace, defaultPath, factoryProperties); - this.uiProvider = uiProvider; - } - - @Override - protected void initUi(Composite parent) { - uid = UUID.randomUUID().toString(); - parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - parent.setLayout(CmsSwtUtils.noSpaceGridLayout(new GridLayout(3, false))); - - uxContext = new SimpleSwtUxContext(); - if (!getUxContext().isMasterData()) - createAdminArea(parent); - headerArea = new Composite(parent, SWT.NONE); - headerArea.setLayout(new FillLayout()); - GridData headerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); - headerData.heightHint = headerHeight; - headerArea.setLayoutData(headerData); - - // TODO: bi-directional - leftArea = new Composite(parent, SWT.NONE); - leftArea.setLayoutData(new GridData(SWT.LEAD, SWT.TOP, false, false)); - leftArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - bodyArea = new Composite(parent, SWT.NONE); - bodyArea.setData(RWT.CUSTOM_VARIANT, CmsStyles.CMS_BODY); - bodyArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - // TODO: bi-directional - rightArea = new Composite(parent, SWT.NONE); - rightArea.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); - rightArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - footerArea = new Composite(parent, SWT.NONE); - // footerArea.setLayout(new FillLayout()); - GridData footerData = new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1); - footerData.heightHint = footerHeight; - footerArea.setLayoutData(footerData); - - uiInitialized = true; - refresh(); - } - - @Override - protected void refresh() { - if (!uiInitialized) - return; - if (getState() == null) - setState(""); - refreshSides(); - refreshBody(); - if (log.isTraceEnabled()) - log.trace("UI refreshed " + getNode()); - } - - protected void createAdminArea(Composite parent) { - } - - @Deprecated - protected void refreshHeader() { - if (header == null) - return; - - for (Control child : headerArea.getChildren()) - child.dispose(); - try { - header.createUi(headerArea, getNode()); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh header", e); - } - headerArea.layout(true, true); - } - - protected void refreshSides() { - refresh(headerArea, header, CmsStyles.CMS_HEADER); - refresh(leftArea, lead, CmsStyles.CMS_LEAD); - refresh(rightArea, end, CmsStyles.CMS_END); - refresh(footerArea, footer, CmsStyles.CMS_FOOTER); - } - - private void refresh(Composite area, CmsUiProvider uiProvider, String style) { - if (uiProvider == null) - return; - - for (Control child : area.getChildren()) - child.dispose(); - CmsSwtUtils.style(area, style); - try { - uiProvider.createUi(area, getNode()); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh header", e); - } - area.layout(true, true); - } - - protected void refreshBody() { - // Exception - Throwable exception = getException(); - if (exception != null) { - SystemNotifications systemNotifications = new SystemNotifications(bodyArea); - systemNotifications.notifyException(exception); - resetException(); - return; - // TODO report - } - - // clear - for (Control child : bodyArea.getChildren()) - child.dispose(); - bodyArea.setLayout(CmsSwtUtils.noSpaceGridLayout()); - - try { - Node node = getNode(); -// if (node == null) -// log.error("Context cannot be null"); -// else - uiProvider.createUi(bodyArea, node); - } catch (RepositoryException e) { - throw new CmsException("Cannot refresh body", e); - } - - bodyArea.layout(true, true); - } - - @Override - public UxContext getUxContext() { - return uxContext; - } - @Override - public String getUid() { - return uid; - } - - public CmsImageManager getImageManager() { - return imageManager; - } - - public void setHeader(CmsUiProvider header) { - this.header = header; - } - - public void setHeaderHeight(Integer headerHeight) { - this.headerHeight = headerHeight; - } - - public void setImageManager(CmsImageManager imageManager) { - this.imageManager = imageManager; - } - - public CmsUiProvider getLead() { - return lead; - } - - public void setLead(CmsUiProvider lead) { - this.lead = lead; - } - - public CmsUiProvider getEnd() { - return end; - } - - public void setEnd(CmsUiProvider end) { - this.end = end; - } - - public CmsUiProvider getFooter() { - return footer; - } - - public void setFooter(CmsUiProvider footer) { - this.footer = footer; - } - - public CmsUiProvider getHeader() { - return header; - } - - public void setFooterHeight(Integer footerHeight) { - this.footerHeight = footerHeight; - } - -}