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 \
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.cms.jcr.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+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
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ icons/
--- /dev/null
+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";
+}
--- /dev/null
+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");
+ }
+ }
+
+}
--- /dev/null
+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();
+}
--- /dev/null
+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
+ * <code>true</code> 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 <code>false</code>
+ */
+ 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 <code>true</code> if the part is dirty, <code>false</code>
+ * 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 <code>true</code> if the part is stale, <code>false</code>
+ * 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();
+ }
+}
--- /dev/null
+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 <code>IFormColors.TITLE</code>.
+ */
+ public static final String TITLE = IFormColors.TITLE;
+
+ /**
+ * Key for the tree/table border color.
+ *
+ * @deprecated use <code>IFormColors.BORDER</code>
+ */
+ public static final String BORDER = IFormColors.BORDER;
+
+ /**
+ * Key for the section separator color.
+ *
+ * @deprecated use <code>IFormColors.SEPARATOR</code>.
+ */
+ public static final String SEPARATOR = IFormColors.SEPARATOR;
+
+ /**
+ * Key for the section title bar background.
+ *
+ * @deprecated use <code>IFormColors.TB_BG
+ */
+ public static final String TB_BG = IFormColors.TB_BG;
+
+ /**
+ * Key for the section title bar foreground.
+ *
+ * @deprecated use <code>IFormColors.TB_FG</code>
+ */
+ public static final String TB_FG = IFormColors.TB_FG;
+
+ /**
+ * Key for the section title bar gradient.
+ *
+ * @deprecated use <code>IFormColors.TB_GBG</code>
+ */
+ public static final String TB_GBG = IFormColors.TB_GBG;
+
+ /**
+ * Key for the section title bar border.
+ *
+ * @deprecated use <code>IFormColors.TB_BORDER</code>.
+ */
+ 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 <code>IFormColors.TB_TOGGLE</code>.
+ */
+ public static final String TB_TOGGLE = IFormColors.TB_TOGGLE;
+
+ /**
+ * Key for the section toggle hover color.
+ *
+ * @deprecated use <code>IFormColors.TB_TOGGLE_HOVER</code>.
+ */
+ 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 <code>initializeColorTable()</code>.
+ *
+ * @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 <code>SWT</code> class.
+ *
+ * @param code
+ * the system color constant as defined in <code>SWT</code>
+ * 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.
+ *
+ * <p>
+ * 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 <samp>true</samp> if background is white, <samp>false</samp>
+ * 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 <samp>null </samp> if
+ * not in the registry.
+ *
+ * @param key
+ * the color key
+ * @return color object if found, or <samp>null </samp> 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 <code>true</code> if shared, <code>false</code> 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 <code>true</code> if at least one of the primary colors in the
+ * source RGB are within the provided range, <code>false</code>
+ * 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 <code>true</code> if at least two of the primary colors in the
+ * source RGB are within the provided range, <code>false</code>
+ * 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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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.
+ * <p>
+ * 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.
+ * <p>
+ * The toolkit creates some of the most common controls used to populate Eclipse
+ * forms. Controls that must be created using their constructors,
+ * <code>adapt()</code> method is available to change its properties in the
+ * same way as with the supported toolkit controls.
+ * <p>
+ * 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).
+ * <p>
+ * FormToolkit is normally instantiated, but can also be subclassed if some of
+ * the methods needs to be modified. In those cases, <code>super</code> 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).
+ * <p>
+ * 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 <b>not</b> marked as
+ * shared via the <code>markShared()</code> method.
+ * <p>
+ * 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 <code>null</code>)
+ * @param style
+ * the button style (for example, <code>SWT.PUSH</code>)
+ * @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 <code>true</code>, 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 <code>true</code>, form will be scrolled horizontally
+ * and/or vertically if needed to ensure that the control is
+ * visible when it gains focus. Set it to <code>false</code> if
+ * the control is not capable of gaining focus.
+ * @param trackKeyboard
+ * if <code>true</code>, 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
+ * <code>false</code> 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.
+// * <p>
+// * 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:
+// *
+// * <pre>
+// *
+// *
+// *
+// * widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
+// *
+// * or
+// *
+// * widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
+// *
+// *
+// *
+// * </pre>
+// *
+// * @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
+ * <code>SWT.BORDER</code> or <code>SWT.NULL</code>
+ * @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 <code>true</code>, 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 <code>SWT.NULL</code>,
+ * <code>SWT.LEFT_TO_RIGHT</code> and <code>SWT.RIGHT_TO_LEFT</code>.
+ *
+ * @return orientation style for this toolkit, or <code>SWT.NULL</code> 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 <code>SWT.NULL</code>, <code>SWT.LEFT_TO_RIGHT</code>
+ * and <code>SWT.RIGHT_TO_LEFT</code>.
+ *
+ * @param orientation
+ * style for this toolkit.
+ */
+
+ public void setOrientation(int orientation) {
+ this.orientation = orientation;
+ }
+}
--- /dev/null
+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);
+// }
+}
--- /dev/null
+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
--- /dev/null
+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).
+ * <p>
+ * The form part has two 'out of sync' states in respect to the model(s) that
+ * feed the form: <b>dirty</b> and <b>stale</b>. 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).
+ * <p>
+ * 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 <code>true</code> if the part has selected and revealed the
+ * input object, <code>false</code> 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).
+ * <p>
+ * 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 <code>true</code> if the part needs refreshing,
+ * <code>false</code> 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();
+}
--- /dev/null
+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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 <code>true</code>, 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 <code>true</code> if the form contains this object,
+ * <code>false</code> otherwise.
+ */
+ boolean setInput(Object input);
+
+ /**
+ * Returns the current page input.
+ *
+ * @return page input object or <code>null</code> if not applicable.
+ */
+ Object getInput();
+
+ /**
+ * Tests if form is dirty. A managed form is dirty if at least one managed
+ * part is dirty.
+ *
+ * @return <code>true</code> if at least one managed part is dirty,
+ * <code>false</code> 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 <code>true</code> if the form is stale, <code>false</code>
+ * 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();
+}
--- /dev/null
+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);
+}
--- /dev/null
+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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 <code>Display.syncExec</code> or
+ * <code>asyncExec</code>.
+ */
+ 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();
+// }
+}
--- /dev/null
+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.
+ * <p>
+ * 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.
+ * <p>
+ * Subclasses should extend this class and implement <code>addPages</code>
+ * method. One of the two <code>addPage</code> 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
+ * <code>IFormPage</code> 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]
+}
--- /dev/null
+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
+ * <code>initialize</code> 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 <code>true</code> if the page is currently active,
+ * <code>false</code> 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
+ * <code>createFormContent(IManagedForm)</code> 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 <code>null</code>- form page has no title image. Subclasses
+ * may override.
+ *
+ * @return <code>null</code>
+ */
+ 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 <code>true</code> if the managed form is dirty,
+ * <code>false</code> 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 <code>false</code>
+ */
+ 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 <code>true</code> if the page has been successfully selected
+ * and revealed by one of the managed form parts, <code>false</code>
+ * 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 <code>true</code>
+ */
+ public boolean canLeaveThePage() {
+ return true;
+ }
+}
--- /dev/null
+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:
+ * <ul>
+ * <li>The form page has a managed form</li>
+ * <li>The form page has a unique id</li>
+ * <li>The form page can be GUI but can also wrap a complete
+ * editor class (in that case, it should return <code>true</code>
+ * from <code>isEditor()</code> method).</li>
+ * <li>The form page is lazy i.e. understands that
+ * its part control will be created at the last possible
+ * moment.</li>.
+ * </ul>
+ * <p>Existing editors can be wrapped by implementing
+ * this interface. In this case, 'isEditor' should return <code>true</code>.
+ * A common editor to wrap in <code>TextEditor</code> 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 <samp>null </samp> 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
+ * <code>false</code>) or lazily create and/or populate the content on
+ * <code>true</code>.
+ *
+ * @param active
+ * <code>true</code> if page should be visible, <code>false</code>
+ * otherwise.
+ */
+ void setActive(boolean active);
+ /**
+ * Returns <samp>true </samp> if page is currently active, false if not.
+ *
+ * @return <samp>true </samp> 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 <code>true</code> if the editor can flip to another page,
+ * <code>false</code> otherwise.
+ */
+ boolean canLeaveThePage();
+ /**
+ * Returns the control associated with this page.
+ *
+ * @return the control of this page if created or <samp>null </samp> 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 <samp>true </samp> if the page wraps an editor,
+ * <samp>false </samp> 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, <code>false</code> should be returned to allow another
+ * page to try.
+ *
+ * @param object
+ * object to select and reveal
+ * @return <code>true</code> if the request was successful, <code>false</code>
+ * otherwise.
+ */
+ boolean selectReveal(Object object);
+}
--- /dev/null
+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
--- /dev/null
+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<String> 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<String> 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<String> getValues() {
+ return values;
+ }
+
+ public void setValues(List<String> 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
--- /dev/null
+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
--- /dev/null
+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("<br/>", "\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
--- /dev/null
+package org.argeo.cms.ui.forms;
+
+/** Constants used in the various CMS Forms */
+public interface FormConstants {
+ // DATAKEYS
+ public final static String LINKED_VALUE = "LinkedValue";
+}
--- /dev/null
+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
--- /dev/null
+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<Control, Node> 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<String> 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<String> valStrings = new ArrayList<String>();
+ 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<Control, Node> imageManager() {
+ if (imageManager == null)
+ imageManager = (CmsImageManager<Control, Node>) 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<String> valueStrings = new ArrayList<String>();
+
+ 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<String> 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", "<br/>");
+ // 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
--- /dev/null
+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";
+}
--- /dev/null
+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("<a href=\"tel:");
+ builder.append(value).append("\" target=\"_blank\" >").append(label)
+ .append("</a>");
+ 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("<a href=\"mailto:");
+ builder.append(value).append("\" >").append(label).append("</a>");
+ 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("<a href=\"");
+ builder.append(value + "\" target=\"_blank\" >" + label + "</a>");
+ return builder.toString();
+ }
+
+ private static String AMPERSAND = "&";
+
+ /**
+ * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to
+ * avoid <code>SAXParseException</code> 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() {
+ }
+}
--- /dev/null
+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<String, String[]> 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("<html>");
+ markup.append(text);
+ markup.append("</html>");
+ 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("<!DOCTYPE html [");
+ result.append("<!ENTITY quot \""\">");
+ result.append("<!ENTITY amp \"&\">");
+ result.append("<!ENTITY apos \"'\">");
+ result.append("<!ENTITY lt \"<\">");
+ result.append("<!ENTITY gt \">\">");
+ result.append("<!ENTITY nbsp \" \">");
+ result.append("<!ENTITY ensp \" \">");
+ result.append("<!ENTITY emsp \" \">");
+ result.append("<!ENTITY ndash \"–\">");
+ result.append("<!ENTITY mdash \"—\">");
+ result.append("]>");
+ return result.toString();
+ }
+
+ private static Map<String, String[]> createSupportedElementsMap() {
+ Map<String, String[]> result = new HashMap<String, String[]>();
+ 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<String> 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);
+ }
+ }
+ }
+
+ }
+
+}
--- /dev/null
+/** Argeo CMS forms, based on SWT/JFace. */
+package org.argeo.cms.ui.forms;
\ No newline at end of file
--- /dev/null
+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<FileSystem> pea = new PrivilegedExceptionAction<FileSystem>() {
+ @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<ColumnDefinition> 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 = "<i>Unknown</i>";
+ 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<Path> 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;
+ }
+}
--- /dev/null
+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 {
+
+ }
+}
--- /dev/null
+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<String, Button> actionButtons = new HashMap<String, Button>();
+
+ 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<Object> iterator = selection.iterator();
+ List<Path> 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;
+ }
+}
--- /dev/null
+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";
+}
--- /dev/null
+/** SWT/JFace file system components. */
+package org.argeo.cms.ui.fs;
\ No newline at end of file
--- /dev/null
+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<CmsState, CmsState> 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();
+ }
+}
--- /dev/null
+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<Node> arr = new ArrayList<Node>();
+ 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<Node> arr = new ArrayList<Node>();
+ 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);
+ }
+ }
+}
--- /dev/null
+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) {
+
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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<String, Repository> repositories = Collections.unmodifiableMap(new TreeMap<String, Repository>());
+
+ @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<String, Repository> 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<String, Repository> map = new TreeMap<String, Repository>(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<String, Repository> map = new TreeMap<String, Repository>(repositories);
+ if (map.remove(alias) == null) {
+ log.warn("No repository was registered with alias " + alias);
+ return;
+ }
+ repositories = Collections.unmodifiableMap(map);
+ setChanged();
+ notifyObservers(alias);
+ }
+}
--- /dev/null
+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<Version> result = new ArrayList<Version>();
+ 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<Node> tmp = new ArrayList<Node>();
+ 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) {
+ }
+
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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
+ }
+}
--- /dev/null
+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");
+
+}
--- /dev/null
+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<Node> children = new ArrayList<Node>();
+ 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) {
+ }
+}
--- /dev/null
+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<Object> objs = new ArrayList<Object>();
+ 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<TreeParent> {
+
+ 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());
+ }
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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<Repository, Repository> repositoryTracker;
+
+ public OsgiRepositoryRegister() {
+ repositoryTracker = new ServiceTracker<Repository, Repository>(bc, Repository.class, null) {
+
+ @Override
+ public Repository addingService(ServiceReference<Repository> reference) {
+
+ Repository repository = super.addingService(reference);
+ Map<String, Object> props = new HashMap<>();
+ for (String key : reference.getPropertyKeys()) {
+ props.put(key, reference.getProperty(key));
+ }
+ register(repository, props);
+ return repository;
+ }
+
+ @Override
+ public void removedService(ServiceReference<Repository> reference, Repository service) {
+ Map<String, Object> 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();
+ }
+}
--- /dev/null
+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<Property> props = new TreeSet<Property>(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);
+ }
+ }
+}
--- /dev/null
+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 = "<binary>";
+ 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);
+ }
+ }
+}
--- /dev/null
+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<String, Repository> getRepositories();
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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();
+ // }
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+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<String, Repository> 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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+ }
+}
--- /dev/null
+/** Model for SWT/JFace JCR components. */
+package org.argeo.cms.ui.jcr.model;
\ No newline at end of file
--- /dev/null
+/** SWT/JFace JCR components. */
+package org.argeo.cms.ui.jcr;
\ No newline at end of file
--- /dev/null
+/** SWT/JFace components for Argeo CMS. */
+package org.argeo.cms.ui;
\ No newline at end of file
--- /dev/null
+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("<a style='color:inherit;text-decoration:inherit;' href=\"");
+ if (loggedInTarget.equals("")) {
+ try {
+ Node homeNode = CmsJcrUtils.getUserHome(context.getSession());
+ String homePath = homeNode.getPath();
+ labelText.append("/#" + homePath);
+ } catch (RepositoryException e) {
+ throw new JcrException("Cannot get home path", e);
+ }
+ } else {
+ labelText.append(loggedInTarget);
+ }
+ labelText.append("\">");
+ } else if (target != null) {
+ labelText.append("<a style='color:inherit;text-decoration:inherit;' href='");
+ labelText.append(target).append("'");
+ if (openNew) {
+ labelText.append(" target='_blank'");
+ }
+ labelText.append(">");
+ }
+ if (image != null) {
+ registerImageIfNeeded();
+ String imageLocation = RWT.getResourceManager().getLocation(image);
+ labelText.append("<img");
+ if (imageWidth != null)
+ labelText.append(" width='").append(imageWidth).append('\'');
+ if (imageHeight != null)
+ labelText.append(" height='").append(imageHeight).append('\'');
+ labelText.append(" src=\"").append(imageLocation).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("</a>");
+
+ 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();
+ }
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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() {
+ }
+
+}
--- /dev/null
+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<Node> {
+ 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);
+ }
+}
--- /dev/null
+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);
+ }
+
+}
--- /dev/null
+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<CmsUiProvider> lead = new ArrayList<CmsUiProvider>();
+ private List<CmsUiProvider> center = new ArrayList<CmsUiProvider>();
+ private List<CmsUiProvider> end = new ArrayList<CmsUiProvider>();
+
+ 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<CmsUiProvider> 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<CmsUiProvider> lead) {
+ this.lead = lead;
+ }
+
+ public void setCenter(List<CmsUiProvider> center) {
+ this.center = center;
+ }
+
+ public void setEnd(List<CmsUiProvider> end) {
+ this.end = end;
+ }
+
+ public void setSubPartsSameWidth(Boolean subPartsSameWidth) {
+ this.subPartsSameWidth = subPartsSameWidth;
+ }
+
+ public List<CmsUiProvider> getLead() {
+ return lead;
+ }
+
+ public List<CmsUiProvider> getCenter() {
+ return center;
+ }
+
+ public List<CmsUiProvider> getEnd() {
+ return end;
+ }
+
+}
--- /dev/null
+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("<b>" + context.getName() + "</b>");
+ 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("<i>Children:</i>");
+ // 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("<i>Properties:</i>");
+ // 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 = "<binary>";
+ 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
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+}
--- /dev/null
+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<String, StyleSheet> stylesheets = new LinkedHashMap<String, StyleSheet>();
+
+ 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;
+ }
+
+ }
+}
--- /dev/null
+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("<a href=\"" + mailToUrl + "\">Send details</a>");
+ 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) {
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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();
+ }
+ }
+}
--- /dev/null
+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<CmsUiProvider> items = new ArrayList<CmsUiProvider>();
+
+ @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<CmsUiProvider> getItems() {
+ return items;
+ }
+
+ public void setItems(List<CmsUiProvider> items) {
+ this.items = items;
+ }
+
+}
--- /dev/null
+/** Argeo CMS UI utilities. */
+package org.argeo.cms.ui.util;
\ No newline at end of file
--- /dev/null
+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<Void>) () -> {
+ 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
--- /dev/null
+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<T extends Item> {
+ public Item getItem() throws RepositoryException;
+}
--- /dev/null
+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));
+ }
+
+}
--- /dev/null
+package org.argeo.cms.ui.viewers;
+
+import javax.jcr.Node;
+
+/** An editable part related to a node */
+public interface NodePart extends ItemPart<Node> {
+ public Node getNode();
+}
--- /dev/null
+package org.argeo.cms.ui.viewers;
+
+import javax.jcr.Property;
+
+/** An editable part related to a JCR Property */
+public interface PropertyPart extends ItemPart<Property> {
+ public Property getProperty();
+}
--- /dev/null
+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<String, Section> getSubSections() throws RepositoryException {
+ LinkedHashMap<String, Section> result = new LinkedHashMap<String, Section>();
+ for (Control child : getChildren()) {
+ if (child instanceof Composite) {
+ collectDirectSubSections((Composite) child, result);
+ }
+ }
+ return Collections.unmodifiableMap(result);
+ }
+
+ private void collectDirectSubSections(Composite composite, LinkedHashMap<String, Section> 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());
+ }
+}
--- /dev/null
+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();
+}
--- /dev/null
+/** Argeo CMS generic viewers, based on JFace. */
+package org.argeo.cms.ui.viewers;
\ No newline at end of file
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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<Control, Node> 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<Control, Node> 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<Control, Node> imageManager) throws RepositoryException {
+ super(parent, swtStyle, imgNode, false, preferredImageSize);
+ this.section = section;
+ this.imageManager = imageManager != null ? imageManager
+ : (CmsImageManager<Control, Node>) 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<Control, Node> 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();
+ }
+
+}
--- /dev/null
+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;
+// }
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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";
+
+}
--- /dev/null
+/** Argeo CMS generic widgets, based on SWT. */
+package org.argeo.cms.ui.widgets;
\ No newline at end of file
--- /dev/null
+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<Node> filterChildren(List<Node> children)
+ throws RepositoryException {
+ return children;
+ }
+
+ protected Object[] getChildren(Node node) throws RepositoryException {
+ List<Node> nodes = new ArrayList<Node>();
+ 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
--- /dev/null
+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<Event> events) throws RepositoryException;
+
+ /**
+ * Whether these events should be processed in the UI or skipped with no UI
+ * job created.
+ */
+ protected Boolean willProcessInUiThread(List<Event> events) throws RepositoryException {
+ return true;
+ }
+
+ protected CmsLog getLog() {
+ return logThis;
+ }
+
+ public final void onEvent(final EventIterator eventIterator) {
+ final List<Event> events = new ArrayList<Event>();
+ 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();
+ }
+}
--- /dev/null
+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
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+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();
+}
--- /dev/null
+package org.argeo.eclipse.ui.jcr;\r
+\r
+import javax.jcr.Node;\r
+import javax.jcr.RepositoryException;\r
+\r
+import org.argeo.eclipse.ui.EclipseUiException;\r
+import org.eclipse.jface.viewers.IElementComparer;\r
+\r
+/** Element comparer for JCR node, to be used in JFace viewers. */\r
+public class NodeElementComparer implements IElementComparer {\r
+\r
+ public boolean equals(Object a, Object b) {\r
+ try {\r
+ if ((a instanceof Node) && (b instanceof Node)) {\r
+ Node nodeA = (Node) a;\r
+ Node nodeB = (Node) b;\r
+ return nodeA.getIdentifier().equals(nodeB.getIdentifier());\r
+ } else {\r
+ return a.equals(b);\r
+ }\r
+ } catch (RepositoryException e) {\r
+ throw new EclipseUiException("Cannot compare nodes", e);\r
+ }\r
+ }\r
+\r
+ public int hashCode(Object element) {\r
+ try {\r
+ if (element instanceof Node)\r
+ return ((Node) element).getIdentifier().hashCode();\r
+ return element.hashCode();\r
+ } catch (RepositoryException e) {\r
+ throw new EclipseUiException("Cannot get hash code", e);\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+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<WrappedNode> getWrappedNodes() throws RepositoryException {
+ List<WrappedNode> nodes = new ArrayList<WrappedNode>();
+ 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;
+ }
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+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<String> basePaths;
+ private Boolean mkdirs = false;
+
+ public SimpleNodeContentProvider(Session session, String... basePaths) {
+ this(session, Arrays.asList(basePaths));
+ }
+
+ public SimpleNodeContentProvider(Session session, List<String> 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<Node> baseNodes = new ArrayList<Node>();
+ 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<String> getBasePaths() {
+ return basePaths;
+ }
+
+ public void setMkdirs(Boolean mkdirs) {
+ this.mkdirs = mkdirs;
+ }
+
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+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();
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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: <code>
+ * // IMPORTANT: initialize comparator before setting it
+ * JcrColumnDefinition firstCol = colDefs.get(0);
+ * comparator.setColumn(firstCol.getPropertyType(),
+ * firstCol.getPropertyName());
+ * viewer.setComparator(comparator); </code>
+ */
+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;
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+/** Generic SWT/JFace JCR utilities for lists. */
+package org.argeo.eclipse.ui.jcr.lists;
\ No newline at end of file
--- /dev/null
+/** Generic SWT/JFace JCR utilities. */
+package org.argeo.eclipse.ui.jcr;
\ No newline at end of file
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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<Item> {
+ 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);
+ }
+ }
+
+}
--- /dev/null
+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
--- /dev/null
+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
+ * <code> JcrFileProvider </code>, 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);
+ }
+ }
+}
--- /dev/null
+/** Generic SWT/JFace JCR helpers. */
+package org.argeo.eclipse.ui.jcr.util;
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.cms.ui</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-/MANIFEST.MF
+++ /dev/null
-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
+++ /dev/null
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
- .,\
- icons/
+++ /dev/null
-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";
-}
+++ /dev/null
-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");
- }
- }
-
-}
+++ /dev/null
-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();
-}
+++ /dev/null
-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
- * <code>true</code> 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 <code>false</code>
- */
- 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 <code>true</code> if the part is dirty, <code>false</code>
- * 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 <code>true</code> if the part is stale, <code>false</code>
- * 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();
- }
-}
+++ /dev/null
-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 <code>IFormColors.TITLE</code>.
- */
- public static final String TITLE = IFormColors.TITLE;
-
- /**
- * Key for the tree/table border color.
- *
- * @deprecated use <code>IFormColors.BORDER</code>
- */
- public static final String BORDER = IFormColors.BORDER;
-
- /**
- * Key for the section separator color.
- *
- * @deprecated use <code>IFormColors.SEPARATOR</code>.
- */
- public static final String SEPARATOR = IFormColors.SEPARATOR;
-
- /**
- * Key for the section title bar background.
- *
- * @deprecated use <code>IFormColors.TB_BG
- */
- public static final String TB_BG = IFormColors.TB_BG;
-
- /**
- * Key for the section title bar foreground.
- *
- * @deprecated use <code>IFormColors.TB_FG</code>
- */
- public static final String TB_FG = IFormColors.TB_FG;
-
- /**
- * Key for the section title bar gradient.
- *
- * @deprecated use <code>IFormColors.TB_GBG</code>
- */
- public static final String TB_GBG = IFormColors.TB_GBG;
-
- /**
- * Key for the section title bar border.
- *
- * @deprecated use <code>IFormColors.TB_BORDER</code>.
- */
- 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 <code>IFormColors.TB_TOGGLE</code>.
- */
- public static final String TB_TOGGLE = IFormColors.TB_TOGGLE;
-
- /**
- * Key for the section toggle hover color.
- *
- * @deprecated use <code>IFormColors.TB_TOGGLE_HOVER</code>.
- */
- 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 <code>initializeColorTable()</code>.
- *
- * @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 <code>SWT</code> class.
- *
- * @param code
- * the system color constant as defined in <code>SWT</code>
- * 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.
- *
- * <p>
- * 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 <samp>true</samp> if background is white, <samp>false</samp>
- * 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 <samp>null </samp> if
- * not in the registry.
- *
- * @param key
- * the color key
- * @return color object if found, or <samp>null </samp> 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 <code>true</code> if shared, <code>false</code> 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 <code>true</code> if at least one of the primary colors in the
- * source RGB are within the provided range, <code>false</code>
- * 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 <code>true</code> if at least two of the primary colors in the
- * source RGB are within the provided range, <code>false</code>
- * 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;
- }
-}
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-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.
- * <p>
- * 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.
- * <p>
- * The toolkit creates some of the most common controls used to populate Eclipse
- * forms. Controls that must be created using their constructors,
- * <code>adapt()</code> method is available to change its properties in the
- * same way as with the supported toolkit controls.
- * <p>
- * 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).
- * <p>
- * FormToolkit is normally instantiated, but can also be subclassed if some of
- * the methods needs to be modified. In those cases, <code>super</code> 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).
- * <p>
- * 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 <b>not</b> marked as
- * shared via the <code>markShared()</code> method.
- * <p>
- * 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 <code>null</code>)
- * @param style
- * the button style (for example, <code>SWT.PUSH</code>)
- * @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 <code>true</code>, 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 <code>true</code>, form will be scrolled horizontally
- * and/or vertically if needed to ensure that the control is
- * visible when it gains focus. Set it to <code>false</code> if
- * the control is not capable of gaining focus.
- * @param trackKeyboard
- * if <code>true</code>, 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
- * <code>false</code> 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.
-// * <p>
-// * 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:
-// *
-// * <pre>
-// *
-// *
-// *
-// * widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER);
-// *
-// * or
-// *
-// * widget.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
-// *
-// *
-// *
-// * </pre>
-// *
-// * @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
- * <code>SWT.BORDER</code> or <code>SWT.NULL</code>
- * @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 <code>true</code>, 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 <code>SWT.NULL</code>,
- * <code>SWT.LEFT_TO_RIGHT</code> and <code>SWT.RIGHT_TO_LEFT</code>.
- *
- * @return orientation style for this toolkit, or <code>SWT.NULL</code> 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 <code>SWT.NULL</code>, <code>SWT.LEFT_TO_RIGHT</code>
- * and <code>SWT.RIGHT_TO_LEFT</code>.
- *
- * @param orientation
- * style for this toolkit.
- */
-
- public void setOrientation(int orientation) {
- this.orientation = orientation;
- }
-}
+++ /dev/null
-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);
-// }
-}
+++ /dev/null
-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
+++ /dev/null
-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).
- * <p>
- * The form part has two 'out of sync' states in respect to the model(s) that
- * feed the form: <b>dirty</b> and <b>stale</b>. 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).
- * <p>
- * 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 <code>true</code> if the part has selected and revealed the
- * input object, <code>false</code> 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).
- * <p>
- * 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 <code>true</code> if the part needs refreshing,
- * <code>false</code> 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();
-}
+++ /dev/null
-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.
- * <p>
- * 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.
- * <p>
- * 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 <code>true</code>, 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 <code>true</code> if the form contains this object,
- * <code>false</code> otherwise.
- */
- boolean setInput(Object input);
-
- /**
- * Returns the current page input.
- *
- * @return page input object or <code>null</code> if not applicable.
- */
- Object getInput();
-
- /**
- * Tests if form is dirty. A managed form is dirty if at least one managed
- * part is dirty.
- *
- * @return <code>true</code> if at least one managed part is dirty,
- * <code>false</code> 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 <code>true</code> if the form is stale, <code>false</code>
- * 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();
-}
+++ /dev/null
-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);
-}
+++ /dev/null
-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.
- * <p>
- * 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.
- * <p>
- * 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 <code>Display.syncExec</code> or
- * <code>asyncExec</code>.
- */
- 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();
-// }
-}
+++ /dev/null
-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.
- * <p>
- * 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.
- * <p>
- * Subclasses should extend this class and implement <code>addPages</code>
- * method. One of the two <code>addPage</code> 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
- * <code>IFormPage</code> 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]
-}
+++ /dev/null
-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
- * <code>initialize</code> 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 <code>true</code> if the page is currently active,
- * <code>false</code> 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
- * <code>createFormContent(IManagedForm)</code> 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 <code>null</code>- form page has no title image. Subclasses
- * may override.
- *
- * @return <code>null</code>
- */
- 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 <code>true</code> if the managed form is dirty,
- * <code>false</code> 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 <code>false</code>
- */
- 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 <code>true</code> if the page has been successfully selected
- * and revealed by one of the managed form parts, <code>false</code>
- * 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 <code>true</code>
- */
- public boolean canLeaveThePage() {
- return true;
- }
-}
+++ /dev/null
-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:
- * <ul>
- * <li>The form page has a managed form</li>
- * <li>The form page has a unique id</li>
- * <li>The form page can be GUI but can also wrap a complete
- * editor class (in that case, it should return <code>true</code>
- * from <code>isEditor()</code> method).</li>
- * <li>The form page is lazy i.e. understands that
- * its part control will be created at the last possible
- * moment.</li>.
- * </ul>
- * <p>Existing editors can be wrapped by implementing
- * this interface. In this case, 'isEditor' should return <code>true</code>.
- * A common editor to wrap in <code>TextEditor</code> 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 <samp>null </samp> 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
- * <code>false</code>) or lazily create and/or populate the content on
- * <code>true</code>.
- *
- * @param active
- * <code>true</code> if page should be visible, <code>false</code>
- * otherwise.
- */
- void setActive(boolean active);
- /**
- * Returns <samp>true </samp> if page is currently active, false if not.
- *
- * @return <samp>true </samp> 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 <code>true</code> if the editor can flip to another page,
- * <code>false</code> otherwise.
- */
- boolean canLeaveThePage();
- /**
- * Returns the control associated with this page.
- *
- * @return the control of this page if created or <samp>null </samp> 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 <samp>true </samp> if the page wraps an editor,
- * <samp>false </samp> 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, <code>false</code> should be returned to allow another
- * page to try.
- *
- * @param object
- * object to select and reveal
- * @return <code>true</code> if the request was successful, <code>false</code>
- * otherwise.
- */
- boolean selectReveal(Object object);
-}
+++ /dev/null
-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
+++ /dev/null
-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<String> 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<String> 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<String> getValues() {
- return values;
- }
-
- public void setValues(List<String> 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
+++ /dev/null
-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
+++ /dev/null
-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("<br/>", "\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
+++ /dev/null
-package org.argeo.cms.ui.forms;
-
-/** Constants used in the various CMS Forms */
-public interface FormConstants {
- // DATAKEYS
- public final static String LINKED_VALUE = "LinkedValue";
-}
+++ /dev/null
-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
+++ /dev/null
-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<Control, Node> 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<String> 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<String> valStrings = new ArrayList<String>();
- 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<Control, Node> imageManager() {
- if (imageManager == null)
- imageManager = (CmsImageManager<Control, Node>) 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<String> valueStrings = new ArrayList<String>();
-
- 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<String> 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", "<br/>");
- // 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
+++ /dev/null
-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";
-}
+++ /dev/null
-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("<a href=\"tel:");
- builder.append(value).append("\" target=\"_blank\" >").append(label)
- .append("</a>");
- 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("<a href=\"mailto:");
- builder.append(value).append("\" >").append(label).append("</a>");
- 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("<a href=\"");
- builder.append(value + "\" target=\"_blank\" >" + label + "</a>");
- return builder.toString();
- }
-
- private static String AMPERSAND = "&";
-
- /**
- * Cleans a String by replacing any '&' by its HTML encoding '&#38;' to
- * avoid <code>SAXParseException</code> 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() {
- }
-}
+++ /dev/null
-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<String, String[]> 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("<html>");
- markup.append(text);
- markup.append("</html>");
- 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("<!DOCTYPE html [");
- result.append("<!ENTITY quot \""\">");
- result.append("<!ENTITY amp \"&\">");
- result.append("<!ENTITY apos \"'\">");
- result.append("<!ENTITY lt \"<\">");
- result.append("<!ENTITY gt \">\">");
- result.append("<!ENTITY nbsp \" \">");
- result.append("<!ENTITY ensp \" \">");
- result.append("<!ENTITY emsp \" \">");
- result.append("<!ENTITY ndash \"–\">");
- result.append("<!ENTITY mdash \"—\">");
- result.append("]>");
- return result.toString();
- }
-
- private static Map<String, String[]> createSupportedElementsMap() {
- Map<String, String[]> result = new HashMap<String, String[]>();
- 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<String> 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);
- }
- }
- }
-
- }
-
-}
+++ /dev/null
-/** Argeo CMS forms, based on SWT/JFace. */
-package org.argeo.cms.ui.forms;
\ No newline at end of file
+++ /dev/null
-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<FileSystem> pea = new PrivilegedExceptionAction<FileSystem>() {
- @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<ColumnDefinition> 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 = "<i>Unknown</i>";
- 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<Path> 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;
- }
-}
+++ /dev/null
-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 {
-
- }
-}
+++ /dev/null
-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<String, Button> actionButtons = new HashMap<String, Button>();
-
- 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<Object> iterator = selection.iterator();
- List<Path> 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;
- }
-}
+++ /dev/null
-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";
-}
+++ /dev/null
-/** SWT/JFace file system components. */
-package org.argeo.cms.ui.fs;
\ No newline at end of file
+++ /dev/null
-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<CmsState, CmsState> 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();
- }
-}
+++ /dev/null
-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<Node> arr = new ArrayList<Node>();
- 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<Node> arr = new ArrayList<Node>();
- 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);
- }
- }
-}
+++ /dev/null
-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) {
-
- }
-
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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<String, Repository> repositories = Collections.unmodifiableMap(new TreeMap<String, Repository>());
-
- @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<String, Repository> 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<String, Repository> map = new TreeMap<String, Repository>(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<String, Repository> map = new TreeMap<String, Repository>(repositories);
- if (map.remove(alias) == null) {
- log.warn("No repository was registered with alias " + alias);
- return;
- }
- repositories = Collections.unmodifiableMap(map);
- setChanged();
- notifyObservers(alias);
- }
-}
+++ /dev/null
-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<Version> result = new ArrayList<Version>();
- 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<Node> tmp = new ArrayList<Node>();
- 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) {
- }
-
-}
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-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
- }
-}
+++ /dev/null
-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");
-
-}
+++ /dev/null
-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<Node> children = new ArrayList<Node>();
- 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) {
- }
-}
+++ /dev/null
-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<Object> objs = new ArrayList<Object>();
- 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<TreeParent> {
-
- 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());
- }
- }
-}
+++ /dev/null
-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;
- }
- }
-}
+++ /dev/null
-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<Repository, Repository> repositoryTracker;
-
- public OsgiRepositoryRegister() {
- repositoryTracker = new ServiceTracker<Repository, Repository>(bc, Repository.class, null) {
-
- @Override
- public Repository addingService(ServiceReference<Repository> reference) {
-
- Repository repository = super.addingService(reference);
- Map<String, Object> props = new HashMap<>();
- for (String key : reference.getPropertyKeys()) {
- props.put(key, reference.getProperty(key));
- }
- register(repository, props);
- return repository;
- }
-
- @Override
- public void removedService(ServiceReference<Repository> reference, Repository service) {
- Map<String, Object> 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();
- }
-}
+++ /dev/null
-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<Property> props = new TreeSet<Property>(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);
- }
- }
-}
+++ /dev/null
-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 = "<binary>";
- 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);
- }
- }
-}
+++ /dev/null
-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<String, Repository> getRepositories();
-}
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-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();
- // }
-}
+++ /dev/null
-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);
- }
- }
-
-}
+++ /dev/null
-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<String, Repository> 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;
- }
-}
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-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);
- }
- }
- }
-}
+++ /dev/null
-/** Model for SWT/JFace JCR components. */
-package org.argeo.cms.ui.jcr.model;
\ No newline at end of file
+++ /dev/null
-/** SWT/JFace JCR components. */
-package org.argeo.cms.ui.jcr;
\ No newline at end of file
+++ /dev/null
-/** SWT/JFace components for Argeo CMS. */
-package org.argeo.cms.ui;
\ No newline at end of file
+++ /dev/null
-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("<a style='color:inherit;text-decoration:inherit;' href=\"");
- if (loggedInTarget.equals("")) {
- try {
- Node homeNode = CmsJcrUtils.getUserHome(context.getSession());
- String homePath = homeNode.getPath();
- labelText.append("/#" + homePath);
- } catch (RepositoryException e) {
- throw new JcrException("Cannot get home path", e);
- }
- } else {
- labelText.append(loggedInTarget);
- }
- labelText.append("\">");
- } else if (target != null) {
- labelText.append("<a style='color:inherit;text-decoration:inherit;' href='");
- labelText.append(target).append("'");
- if (openNew) {
- labelText.append(" target='_blank'");
- }
- labelText.append(">");
- }
- if (image != null) {
- registerImageIfNeeded();
- String imageLocation = RWT.getResourceManager().getLocation(image);
- labelText.append("<img");
- if (imageWidth != null)
- labelText.append(" width='").append(imageWidth).append('\'');
- if (imageHeight != null)
- labelText.append(" height='").append(imageHeight).append('\'');
- labelText.append(" src=\"").append(imageLocation).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("</a>");
-
- 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();
- }
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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("<img width='").append(width).append("' height='").append(height)
- .append("' src='").append(src).append("'");
- }
-
- public static String noImg(Cms2DSize size) {
- ResourceManager rm = RWT.getResourceManager();
- return CmsUiUtils.img(rm.getLocation(CmsUiConstants.NO_IMAGE), size);
- }
-
- public static String noImg() {
- return noImg(CmsUiConstants.NO_IMAGE_SIZE);
- }
-
- public static Image noImage(Cms2DSize size) {
- ResourceManager rm = RWT.getResourceManager();
- InputStream in = null;
- try {
- in = rm.getRegisteredContent(CmsUiConstants.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() {
- }
-
-}
+++ /dev/null
-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 static org.argeo.cms.ui.CmsUiConstants.NO_IMAGE_SIZE;
-
-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.api.cms.ux.CmsImageManager;
-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.rap.rwt.widgets.FileUpload;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Control;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Label;
-
-/** Manages only public images so far. */
-public class DefaultImageManager implements CmsImageManager<Control, Node> {
- 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 {
- }
-}
+++ /dev/null
-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);
- }
-
-}
+++ /dev/null
-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<CmsUiProvider> lead = new ArrayList<CmsUiProvider>();
- private List<CmsUiProvider> center = new ArrayList<CmsUiProvider>();
- private List<CmsUiProvider> end = new ArrayList<CmsUiProvider>();
-
- 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<CmsUiProvider> 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<CmsUiProvider> lead) {
- this.lead = lead;
- }
-
- public void setCenter(List<CmsUiProvider> center) {
- this.center = center;
- }
-
- public void setEnd(List<CmsUiProvider> end) {
- this.end = end;
- }
-
- public void setSubPartsSameWidth(Boolean subPartsSameWidth) {
- this.subPartsSameWidth = subPartsSameWidth;
- }
-
- public List<CmsUiProvider> getLead() {
- return lead;
- }
-
- public List<CmsUiProvider> getCenter() {
- return center;
- }
-
- public List<CmsUiProvider> getEnd() {
- return end;
- }
-
-}
+++ /dev/null
-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("<b>" + context.getName() + "</b>");
- 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("<i>Children:</i>");
- // 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("<i>Properties:</i>");
- // 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 = "<binary>";
- 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
+++ /dev/null
-package org.argeo.cms.ui.util;
-
-public class SimpleImageManager extends DefaultImageManager {
-
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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<String, StyleSheet> stylesheets = new LinkedHashMap<String, StyleSheet>();
-
- 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;
- }
-
- }
-}
+++ /dev/null
-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("<a href=\"" + mailToUrl + "\">Send details</a>");
- 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) {
- }
-
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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();
- }
- }
-}
+++ /dev/null
-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<CmsUiProvider> items = new ArrayList<CmsUiProvider>();
-
- @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<CmsUiProvider> getItems() {
- return items;
- }
-
- public void setItems(List<CmsUiProvider> items) {
- this.items = items;
- }
-
-}
+++ /dev/null
-/** Argeo CMS UI utilities. */
-package org.argeo.cms.ui.util;
\ No newline at end of file
+++ /dev/null
-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<Void>) () -> {
- 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
+++ /dev/null
-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<T extends Item> {
- public Item getItem() throws RepositoryException;
-}
+++ /dev/null
-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));
- }
-
-}
+++ /dev/null
-package org.argeo.cms.ui.viewers;
-
-import javax.jcr.Node;
-
-/** An editable part related to a node */
-public interface NodePart extends ItemPart<Node> {
- public Node getNode();
-}
+++ /dev/null
-package org.argeo.cms.ui.viewers;
-
-import javax.jcr.Property;
-
-/** An editable part related to a JCR Property */
-public interface PropertyPart extends ItemPart<Property> {
- public Property getProperty();
-}
+++ /dev/null
-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<String, Section> getSubSections() throws RepositoryException {
- LinkedHashMap<String, Section> result = new LinkedHashMap<String, Section>();
- for (Control child : getChildren()) {
- if (child instanceof Composite) {
- collectDirectSubSections((Composite) child, result);
- }
- }
- return Collections.unmodifiableMap(result);
- }
-
- private void collectDirectSubSections(Composite composite, LinkedHashMap<String, Section> 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());
- }
-}
+++ /dev/null
-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();
-}
+++ /dev/null
-/** Argeo CMS generic viewers, based on JFace. */
-package org.argeo.cms.ui.viewers;
\ No newline at end of file
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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<Control, Node> 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<Control, Node> 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<Control, Node> imageManager) throws RepositoryException {
- super(parent, swtStyle, imgNode, false, preferredImageSize);
- this.section = section;
- this.imageManager = imageManager != null ? imageManager
- : (CmsImageManager<Control, Node>) 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<Control, Node> 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();
- }
-
-}
+++ /dev/null
-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;
-// }
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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";
-
-}
+++ /dev/null
-/** Argeo CMS generic widgets, based on SWT. */
-package org.argeo.cms.ui.widgets;
\ No newline at end of file
+++ /dev/null
-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<Node> filterChildren(List<Node> children)
- throws RepositoryException {
- return children;
- }
-
- protected Object[] getChildren(Node node) throws RepositoryException {
- List<Node> nodes = new ArrayList<Node>();
- 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
+++ /dev/null
-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<Event> events) throws RepositoryException;
-
- /**
- * Whether these events should be processed in the UI or skipped with no UI
- * job created.
- */
- protected Boolean willProcessInUiThread(List<Event> events) throws RepositoryException {
- return true;
- }
-
- protected CmsLog getLog() {
- return logThis;
- }
-
- public final void onEvent(final EventIterator eventIterator) {
- final List<Event> events = new ArrayList<Event>();
- 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();
- }
-}
+++ /dev/null
-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
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-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);
- }
- }
-
-}
+++ /dev/null
-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();
-}
+++ /dev/null
-package org.argeo.eclipse.ui.jcr;\r
-\r
-import javax.jcr.Node;\r
-import javax.jcr.RepositoryException;\r
-\r
-import org.argeo.eclipse.ui.EclipseUiException;\r
-import org.eclipse.jface.viewers.IElementComparer;\r
-\r
-/** Element comparer for JCR node, to be used in JFace viewers. */\r
-public class NodeElementComparer implements IElementComparer {\r
-\r
- public boolean equals(Object a, Object b) {\r
- try {\r
- if ((a instanceof Node) && (b instanceof Node)) {\r
- Node nodeA = (Node) a;\r
- Node nodeB = (Node) b;\r
- return nodeA.getIdentifier().equals(nodeB.getIdentifier());\r
- } else {\r
- return a.equals(b);\r
- }\r
- } catch (RepositoryException e) {\r
- throw new EclipseUiException("Cannot compare nodes", e);\r
- }\r
- }\r
-\r
- public int hashCode(Object element) {\r
- try {\r
- if (element instanceof Node)\r
- return ((Node) element).getIdentifier().hashCode();\r
- return element.hashCode();\r
- } catch (RepositoryException e) {\r
- throw new EclipseUiException("Cannot get hash code", e);\r
- }\r
- }\r
-\r
-}\r
+++ /dev/null
-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<WrappedNode> getWrappedNodes() throws RepositoryException {
- List<WrappedNode> nodes = new ArrayList<WrappedNode>();
- 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;
- }
-}
+++ /dev/null
-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);
- }
- }
-
-}
+++ /dev/null
-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);
- }
- }
-
-}
+++ /dev/null
-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<String> basePaths;
- private Boolean mkdirs = false;
-
- public SimpleNodeContentProvider(Session session, String... basePaths) {
- this(session, Arrays.asList(basePaths));
- }
-
- public SimpleNodeContentProvider(Session session, List<String> 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<Node> baseNodes = new ArrayList<Node>();
- 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<String> getBasePaths() {
- return basePaths;
- }
-
- public void setMkdirs(Boolean mkdirs) {
- this.mkdirs = mkdirs;
- }
-
-}
+++ /dev/null
-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);
- }
- }
-
-}
+++ /dev/null
-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();
- }
-
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-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: <code>
- * // IMPORTANT: initialize comparator before setting it
- * JcrColumnDefinition firstCol = colDefs.get(0);
- * comparator.setColumn(firstCol.getPropertyType(),
- * firstCol.getPropertyName());
- * viewer.setComparator(comparator); </code>
- */
-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;
- }
-}
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-/** Generic SWT/JFace JCR utilities for lists. */
-package org.argeo.eclipse.ui.jcr.lists;
\ No newline at end of file
+++ /dev/null
-/** Generic SWT/JFace JCR utilities. */
-package org.argeo.eclipse.ui.jcr;
\ No newline at end of file
+++ /dev/null
-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);
- }
- }
-}
+++ /dev/null
-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<Item> {
- 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);
- }
- }
-
-}
+++ /dev/null
-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
+++ /dev/null
-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
- * <code> JcrFileProvider </code>, 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);
- }
- }
-}
+++ /dev/null
-/** Generic SWT/JFace JCR helpers. */
-package org.argeo.eclipse.ui.jcr.util;
\ No newline at end of file
--- /dev/null
+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<V, M> implements CmsImageManager<V, M> {
+ 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);
+ }
+
+}
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;
private CmsUxUtils() {
}
+
+ public static StringBuilder imgBuilder(String src, String width, String height) {
+ return new StringBuilder(64).append("<img width='").append(width).append("' height='").append(height)
+ .append("' src='").append(src).append("'");
+ }
+
+ 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()));
+ }
}
--- /dev/null
+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<M> extends AbstractImageManager<Control, M> {
+ 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);
+ }
+
+}
--- /dev/null
+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<Content> {
+
+ @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;
+ }
+
+}
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,\
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;
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
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)",\
*
+++ /dev/null
-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<String, String> 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<String, String> 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;
- }
-
-}
+++ /dev/null
-package org.argeo.cms.ui.script;
-
-import java.util.Map;
-
-public interface Branding {
- public void applyBranding(Map<String, String> properties);
-
- public String getThemeId();
-
- public String getAdditionalHeaders();
-
- public String getBodyHtml();
-
- public String getPageTitle();
-
- public String getPageOverflow();
-
- public String getFavicon();
-
-}
+++ /dev/null
-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<ApplicationConfiguration> 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<String> resources = new ArrayList<>();
-
- private Map<String, AppUi> 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<URL> 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<String, String> 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<String> 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<String, String> 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<String, AppUi> getUi() {
- return ui;
- }
-
- public void setUi(Map<String, AppUi> 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;
- }
-
- }
-
-}
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-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<Repository, Repository> repoSt = new ServiceTracker<Repository, Repository>(context,
- FrameworkUtil.createFilter("(&" + app.getRepo() + "(objectClass=javax.jcr.Repository))"), null) {
-
- @Override
- public Repository addingService(ServiceReference<Repository> 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 {
- }
-
-}
+++ /dev/null
-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;
- }
-
-}
+++ /dev/null
-// 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, '<b>' + text + '</b>')
-}
-
-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__)
+++ /dev/null
-/** Argeo CMS user interface scripting. */
-package org.argeo.cms.ui.script;
\ No newline at end of file
+++ /dev/null
-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<String, String> 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<String, String> factoryProperties) {
- this.repository = repository;
- this.workspace = workspace;
- this.defaultPath = defaultPath;
- this.factoryProperties = new HashMap<String, String>(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<Void>() {
- @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<Void>() {
-
- @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<Void>() {
- @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
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;
public Void run() {
try {
uxContext = new SimpleSwtUxContext();
- imageManager = new DefaultImageManager();
+ imageManager = (CmsImageManager) new AcrSwtImageManager();
CmsSession cmsSession = getCmsSession();
if (cmsSession != null) {
UiContext.setLocale(cmsSession.getLocale());
+++ /dev/null
-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<String, Map<String, String>> branding = new HashMap<String, Map<String, String>>();
- private Map<String, List<String>> styleSheets = new HashMap<String, List<String>>();
-
- private List<String> resources = new ArrayList<String>();
-
- private BundleContext bundleContext;
-
- private Repository repository;
- private String workspace = null;
- private String jcrBasePath = "/";
- private List<String> roPrincipals = Arrays.asList(CmsConstants.ROLE_ANONYMOUS, CmsConstants.ROLE_USER);
- private List<String> rwPrincipals = Arrays.asList(CmsConstants.ROLE_USER);
-
- private CmsUiProvider header;
- private Map<String, CmsUiProvider> pages = new LinkedHashMap<String, CmsUiProvider>();
-
- private Integer headerHeight = 40;
-
- private ServiceRegistration<ApplicationConfiguration> 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<String, String> defaultBranding = null;
- if (branding.containsKey("*"))
- defaultBranding = branding.get("*");
- // String defaultTheme = defaultBranding.get(WebClient.THEME_ID);
-
- // entry points
- for (String page : pages.keySet()) {
- Map<String, String> properties = defaultBranding != null ? new HashMap<String, String>(defaultBranding)
- : new HashMap<String, String>();
- 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<Bundle> 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<String> 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<String, String> props = new Hashtable<String, String>();
- 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<String, CmsUiProvider> pages) {
- this.pages = pages;
- }
-
- public void setJcrBasePath(String basePath) {
- this.jcrBasePath = basePath;
- }
-
- public void setRoPrincipals(List<String> roPrincipals) {
- this.roPrincipals = roPrincipals;
- }
-
- public void setRwPrincipals(List<String> rwPrincipals) {
- this.rwPrincipals = rwPrincipals;
- }
-
- public void setHeaderHeight(Integer headerHeight) {
- this.headerHeight = headerHeight;
- }
-
- public void setBranding(Map<String, Map<String, String>> branding) {
- this.branding = branding;
- }
-
- public void setStyleSheets(Map<String, List<String>> styleSheets) {
- this.styleSheets = styleSheets;
- }
-
- public void setBundleContext(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- public void setResources(List<String> 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<URL> 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<String, String> properties;
-
- public CmsEntryPointFactory(CmsUiProvider page, Repository repository, String workspace,
- Map<String, String> 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 = "<div"
- + " style=\"position: absolute; left: 50%; top: 50%; margin: -32px -32px; width: 64px; height:64px\">"
- + "<img src=\"./rwt-resources/" + LOADING_IMAGE
- + "\" width=\"32\" height=\"32\" style=\"margin: 16px 16px\"/>" + "</div>";
-}
+++ /dev/null
-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<String, String> 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;
- }
-
-}