Minimal desktop utilities
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 14 Apr 2019 20:00:47 +0000 (22:00 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 14 Apr 2019 20:00:47 +0000 (22:00 +0200)
17 files changed:
rcp/dep/org.argeo.dep.cms.e4.rcp/pom.xml
rcp/org.argeo.cms.desktop/.classpath [new file with mode: 0644]
rcp/org.argeo.cms.desktop/.gitignore [new file with mode: 0644]
rcp/org.argeo.cms.desktop/.project [new file with mode: 0644]
rcp/org.argeo.cms.desktop/META-INF/.gitignore [new file with mode: 0644]
rcp/org.argeo.cms.desktop/bnd.bnd [new file with mode: 0644]
rcp/org.argeo.cms.desktop/build.properties [new file with mode: 0644]
rcp/org.argeo.cms.desktop/pom.xml [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/Activator.java [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/CmsDesktopManager.java [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/DesktopLayer.java [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniBrowser.java [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniExplorer.java [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniImageViewer.java [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniTerminal.java [new file with mode: 0644]
rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniTextEditor.java [new file with mode: 0644]
rcp/pom.xml

index 9083e328ca6e853eca84601a245ba06d68ee1b16..e52bdaa96741fd4c8ffce9dc5f4a29d11d2cfae5 100644 (file)
@@ -9,6 +9,73 @@
        <artifactId>org.argeo.dep.cms.e4.rcp</artifactId>
        <name>Node Eclipse RCP</name>
        <dependencies>
+               <!-- Argeo -->
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.eclipse.ui</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.rcp</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms.ui</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms.ui.theme</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+               
+               <!-- Desktop -->
+               <dependency>
+                       <groupId>org.argeo.commons.rcp</groupId>
+                       <artifactId>org.argeo.cms.desktop</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+
+               <!-- E4 specific -->
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms.e4</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.argeo.commons.rcp</groupId>
+                       <artifactId>org.argeo.cms.e4.rcp</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+
+               <!-- RCP specific -->
+               <!-- <dependency> -->
+               <!-- <groupId>org.argeo.commons.rcp</groupId> -->
+               <!-- <artifactId>org.argeo.cms.ui.workbench.rcp</artifactId> -->
+               <!-- <version>2.1.18-SNAPSHOT</version> -->
+               <!-- </dependency> -->
+               <!-- <dependency> -->
+               <!-- <groupId>org.argeo.commons</groupId> -->
+               <!-- <artifactId>org.argeo.cms.ui.workbench</artifactId> -->
+               <!-- <version>${version.argeo-commons}</version> -->
+               <!-- </dependency> -->
+
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.dep.cms.node</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+                       <type>pom</type>
+               </dependency>
+
+               <!-- SDK -->
+               <dependency>
+                       <groupId>org.argeo.tp.equinox</groupId>
+                       <artifactId>org.eclipse.osgi</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
                <!-- SWT for ARM -->
                <dependency>
                        <groupId>org.argeo.tp.rcp.e4</groupId>
                </dependency>
 
 
-               <!-- Eclipse -->
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.eclipse.ui</artifactId>
-                       <version>2.1.77-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons.rcp</groupId>
-                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
-                       <version>2.1.77-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms.ui</artifactId>
-                       <version>2.1.77-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms.ui.theme</artifactId>
-                       <version>2.1.77-SNAPSHOT</version>
-               </dependency>
-
-               <!-- E4 specific -->
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.cms.e4</artifactId>
-                       <version>2.1.77-SNAPSHOT</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.argeo.commons.rcp</groupId>
-                       <artifactId>org.argeo.cms.e4.rcp</artifactId>
-                       <version>2.1.77-SNAPSHOT</version>
-               </dependency>
-
-               <!-- RCP specific -->
-               <!-- <dependency> -->
-               <!-- <groupId>org.argeo.commons.rcp</groupId> -->
-               <!-- <artifactId>org.argeo.cms.ui.workbench.rcp</artifactId> -->
-               <!-- <version>2.1.18-SNAPSHOT</version> -->
-               <!-- </dependency> -->
-               <!-- <dependency> -->
-               <!-- <groupId>org.argeo.commons</groupId> -->
-               <!-- <artifactId>org.argeo.cms.ui.workbench</artifactId> -->
-               <!-- <version>${version.argeo-commons}</version> -->
-               <!-- </dependency> -->
-
-               <dependency>
-                       <groupId>org.argeo.commons</groupId>
-                       <artifactId>org.argeo.dep.cms.node</artifactId>
-                       <version>2.1.77-SNAPSHOT</version>
-                       <type>pom</type>
-               </dependency>
-
-               <!-- SDK -->
-               <dependency>
-                       <groupId>org.argeo.tp.equinox</groupId>
-                       <artifactId>org.eclipse.osgi</artifactId>
-                       <scope>test</scope>
-               </dependency>
-
        </dependencies>
        <dependencyManagement>
                <dependencies>
diff --git a/rcp/org.argeo.cms.desktop/.classpath b/rcp/org.argeo.cms.desktop/.classpath
new file mode 100644 (file)
index 0000000..eca7bdb
--- /dev/null
@@ -0,0 +1,7 @@
+<?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-1.8"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/rcp/org.argeo.cms.desktop/.gitignore b/rcp/org.argeo.cms.desktop/.gitignore
new file mode 100644 (file)
index 0000000..09e3bc9
--- /dev/null
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/rcp/org.argeo.cms.desktop/.project b/rcp/org.argeo.cms.desktop/.project
new file mode 100644 (file)
index 0000000..c501452
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.cms.desktop</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>
diff --git a/rcp/org.argeo.cms.desktop/META-INF/.gitignore b/rcp/org.argeo.cms.desktop/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/rcp/org.argeo.cms.desktop/bnd.bnd b/rcp/org.argeo.cms.desktop/bnd.bnd
new file mode 100644 (file)
index 0000000..f3c13be
--- /dev/null
@@ -0,0 +1,2 @@
+Import-Package: org.eclipse.swt,\
+*
diff --git a/rcp/org.argeo.cms.desktop/build.properties b/rcp/org.argeo.cms.desktop/build.properties
new file mode 100644 (file)
index 0000000..34d2e4d
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/rcp/org.argeo.cms.desktop/pom.xml b/rcp/org.argeo.cms.desktop/pom.xml
new file mode 100644 (file)
index 0000000..65a02b2
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.commons.rcp</groupId>
+               <artifactId>argeo-rcp</artifactId>
+               <version>2.1.77-SNAPSHOT</version>
+               <relativePath>..</relativePath>
+       </parent>
+       <artifactId>org.argeo.cms.desktop</artifactId>
+       <name>CMS Desktop</name>
+       <packaging>jar</packaging>
+       <dependencies>
+               <!-- Base Argeo UI -->
+               <dependency>
+                       <groupId>org.argeo.commons</groupId>
+                       <artifactId>org.argeo.cms.ui</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+               <!-- RCP specific -->
+               <dependency>
+                       <groupId>org.argeo.commons.rcp</groupId>
+                       <artifactId>org.argeo.eclipse.ui.rcp</artifactId>
+                       <version>2.1.77-SNAPSHOT</version>
+               </dependency>
+       </dependencies>
+</project>
\ No newline at end of file
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/Activator.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/Activator.java
new file mode 100644 (file)
index 0000000..4975bee
--- /dev/null
@@ -0,0 +1,30 @@
+package org.argeo.cms.desktop;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class Activator implements BundleActivator {
+
+       private static BundleContext context;
+
+       static BundleContext getContext() {
+               return context;
+       }
+
+       /*
+        * (non-Javadoc)
+        * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+        */
+       public void start(BundleContext bundleContext) throws Exception {
+               Activator.context = bundleContext;
+       }
+
+       /*
+        * (non-Javadoc)
+        * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+        */
+       public void stop(BundleContext bundleContext) throws Exception {
+               Activator.context = null;
+       }
+
+}
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/CmsDesktopManager.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/CmsDesktopManager.java
new file mode 100644 (file)
index 0000000..737fdef
--- /dev/null
@@ -0,0 +1,132 @@
+package org.argeo.cms.desktop;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+public class CmsDesktopManager {
+       private Display display;
+
+       private Shell rootShell;
+       private Shell toolBarShell;
+
+       public void init() {
+               display = Display.getCurrent();
+               if (display != null)
+                       throw new IllegalStateException("Already a display " + display);
+               display = new Display();
+
+               int toolBarSize = 48;
+
+               if (isFullScreen()) {
+                       rootShell = new Shell(display, SWT.NO_TRIM);
+                       // rootShell.setMaximized(true);
+                       rootShell.setFullScreen(true);
+                       Rectangle bounds = display.getBounds();
+
+                       rootShell.setSize(bounds.width, bounds.height);
+//                     Point realSize = rootShell.getSize();
+//                     rootShell.setBounds(bounds);
+//                     Rectangle realBounds = rootShell.getBounds();
+               } else {
+                       rootShell = new Shell(display, SWT.SHELL_TRIM);
+                       Rectangle shellArea = rootShell.computeTrim(200, 200, 800, 480);
+                       rootShell.setSize(shellArea.width, shellArea.height);
+               }
+
+               rootShell.setLayout(new GridLayout(2, false));
+               Composite toolBarArea = new Composite(rootShell, SWT.NONE);
+               toolBarArea.setLayoutData(new GridData(toolBarSize, rootShell.getSize().y));
+
+               ToolBar toolBar;
+               if (isFullScreen()) {
+                       toolBarShell = new Shell(rootShell, SWT.NO_TRIM | SWT.ON_TOP);
+                       toolBar = new ToolBar(toolBarShell, SWT.VERTICAL | SWT.FLAT | SWT.BORDER);
+                       createDock(toolBar);
+                       toolBarShell.pack();
+                       toolBarArea.setLayoutData(new GridData(toolBar.getSize().x, toolBar.getSize().y));
+               } else {
+                       toolBar = new ToolBar(toolBarArea, SWT.VERTICAL | SWT.FLAT | SWT.BORDER);
+                       createDock(toolBar);
+                       toolBarArea.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
+               }
+
+               Composite backgroundArea = new Composite(rootShell, SWT.NONE);
+               backgroundArea.setLayout(new GridLayout(6, true));
+               DesktopLayer desktopLayer = new DesktopLayer();
+               desktopLayer.init(backgroundArea);
+               rootShell.open();
+               // rootShell.layout(true, true);
+
+               if (toolBarShell != null) {
+                       toolBarShell.setLocation(new Point(0, 0));
+                       toolBarShell.open();
+               }
+       }
+
+       protected void createDock(ToolBar toolBar) {
+
+               // toolBar.setLocation(clientArea.x, clientArea.y);
+
+               ToolItem closeI = new ToolItem(toolBar, SWT.PUSH);
+               closeI.setImage(display.getSystemImage(SWT.ICON_ERROR));
+               closeI.setToolTipText("Close");
+               closeI.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               rootShell.dispose();
+                       }
+
+               });
+
+               ToolItem searchI = new ToolItem(toolBar, SWT.PUSH);
+               searchI.setImage(display.getSystemImage(SWT.ICON_QUESTION));
+               searchI.setToolTipText("Search");
+               searchI.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               // rootShell.dispose();
+                       }
+
+               });
+               // toolBar.setSize(48, toolBar.getSize().y);
+               toolBar.pack();
+       }
+
+       public void run() {
+               while (!rootShell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+       }
+
+       public void dispose() {
+               if (!rootShell.isDisposed())
+                       rootShell.dispose();
+       }
+
+       protected boolean isFullScreen() {
+               return true;
+       }
+
+       public static void main(String[] args) {
+               CmsDesktopManager desktopManager = new CmsDesktopManager();
+               desktopManager.init();
+               // Runtime.getRuntime().addShutdownHook(new Thread(() ->
+               // desktopManager.dispose(), "Dispose desktop manager"));
+               desktopManager.run();
+               desktopManager.dispose();
+       }
+
+}
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/DesktopLayer.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/DesktopLayer.java
new file mode 100644 (file)
index 0000000..0565e73
--- /dev/null
@@ -0,0 +1,96 @@
+package org.argeo.cms.desktop;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.FilenameUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.program.Program;
+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.Label;
+
+public class DesktopLayer {
+       // TODO make it configurable
+       private Path desktopDir = Paths.get(System.getProperty("user.home"), "tmp");
+
+       public void init(Composite parentShell) {
+//             Decorations shell = new Decorations(parentShell, SWT.CLOSE);
+//             shell.setLayoutData(new GridData(GridData.FILL_BOTH));
+               createUi(parentShell, desktopDir);
+               // shell.open();
+       }
+
+       public Control createUi(Composite parent, Path context) {
+               // parent.setLayout(new FillLayout());
+               try {
+                       DirectoryStream<Path> ds = Files.newDirectoryStream(context);
+                       ds.forEach((path) -> createIcon(parent, path));
+               } catch (IOException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+               return parent;
+
+       }
+
+       protected void createIcon(Composite parent, Path path) {
+               String ext = FilenameUtils.getExtension(path.getFileName().toString());
+               Program program = Program.findProgram(ext);
+               if (program == null) {
+                       createDefaultIcon(parent, path);
+                       return;
+               }
+
+               Display display = parent.getDisplay();
+               ImageData iconData = program.getImageData();
+
+               Image iconImage;
+               if (iconData == null) {
+                       iconImage = display.getSystemImage(SWT.ICON_INFORMATION);
+                       iconData = iconImage.getImageData();
+               } else {
+                       iconImage = new Image(display, iconData);
+               }
+
+               Composite icon = new Composite(parent, SWT.NONE);
+               icon.setLayoutData(new GridData(48, 72));
+               icon.setLayout(new GridLayout());
+               // Button
+               Button iconB = new Button(icon, SWT.FLAT);
+               iconB.setImage(iconImage);
+               // iconB.setLayoutData(new GridData(iconData.width, iconData.height));
+               iconB.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
+               iconB.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               program.execute(path.toString());
+                       }
+
+               });
+               // Label
+               Label iconL = new Label(icon, SWT.WRAP);
+               iconL.setText(path.getFileName().toString());
+               iconL.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
+       }
+
+       protected void createDefaultIcon(Composite parent, Path path) {
+               Composite icon = new Composite(parent, SWT.NONE);
+               icon.setLayout(new GridLayout());
+               Label iconL = new Label(icon, SWT.NONE);
+               iconL.setText(path.getFileName().toString());
+               iconL.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+       }
+}
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniBrowser.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniBrowser.java
new file mode 100644 (file)
index 0000000..43b3d78
--- /dev/null
@@ -0,0 +1,109 @@
+package org.argeo.cms.desktop.mini;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.LocationEvent;
+import org.eclipse.swt.browser.LocationListener;
+import org.eclipse.swt.browser.TitleListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+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.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class MiniBrowser {
+       private URL url;
+       private Text addressT;
+       private Browser browser;
+
+       public MiniBrowser(Composite parent, int style) {
+               parent.setLayout(new GridLayout());
+
+               Composite toolBar = new Composite(parent, SWT.NONE);
+               toolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               toolBar.setLayout(new FillLayout());
+               addressT = new Text(toolBar, SWT.SINGLE | SWT.BORDER);
+               // addressT.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               addressT.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                               setUrl(addressT.getText().trim());
+                       }
+               });
+
+               browser = new Browser(parent, SWT.WEBKIT);
+               browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               browser.addLocationListener(new LocationListener() {
+
+                       @Override
+                       public void changing(LocationEvent event) {
+                       }
+
+                       @Override
+                       public void changed(LocationEvent event) {
+                               try {
+                                       MiniBrowser.this.url = new URL(browser.getUrl());
+                                       addressT.setText(url.toString());
+                               } catch (MalformedURLException e) {
+                                       addressT.setText(e.getMessage());
+                                       throw new IllegalArgumentException("Cannot interpet new URL", e);
+
+                               }
+                       }
+               });
+       }
+
+       public void setUrl(URL url) {
+               this.url = url;
+               if (addressT != null)
+                       addressT.setText(url.toString());
+               if (browser != null)
+                       browser.setUrl(url.toString());
+       }
+
+       public void setUrl(String url) {
+               try {
+                       setUrl(new URL(url));
+               } catch (MalformedURLException e) {
+                       // try with http
+                       try {
+                               setUrl(new URL("http://"+url));
+                               return;
+                       } catch (MalformedURLException e1) {
+                               // nevermind...
+                       }
+                       throw new IllegalArgumentException("Cannot interpret URL " + url, e);
+               }
+       }
+
+       public void addTitleListener(TitleListener titleListener) {
+               browser.addTitleListener(titleListener);
+       }
+
+       public static void main(String[] args) {
+               Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent();
+               Shell shell = new Shell(display, SWT.SHELL_TRIM);
+
+               MiniBrowser miniBrowser = new MiniBrowser(shell, SWT.NONE);
+               miniBrowser.addTitleListener(e -> shell.setText(e.title));
+               String url = args.length > 0 ? args[0] : "http://www.argeo.org";
+               miniBrowser.setUrl(url);
+
+               shell.open();
+               shell.setSize(new Point(800, 480));
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+       }
+
+}
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniExplorer.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniExplorer.java
new file mode 100644 (file)
index 0000000..d541574
--- /dev/null
@@ -0,0 +1,142 @@
+package org.argeo.cms.desktop.mini;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.eclipse.swt.SWT;
+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.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.program.Program;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+
+public class MiniExplorer {
+       private Path url;
+       private Text addressT;
+       private Table browser;
+
+       private boolean showHidden = false;
+
+       public MiniExplorer(Composite parent, int style) {
+               parent.setLayout(new GridLayout());
+
+               Composite toolBar = new Composite(parent, SWT.NONE);
+               toolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               toolBar.setLayout(new FillLayout());
+               addressT = new Text(toolBar, SWT.SINGLE | SWT.BORDER);
+               // addressT.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               addressT.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetDefaultSelected(SelectionEvent e) {
+                               setUrl(addressT.getText().trim());
+                       }
+               });
+               browser = createTable(parent, this.url);
+
+       }
+
+       public void setUrl(Path url) {
+               this.url = url;
+               if (addressT != null)
+                       addressT.setText(url.toString());
+               if (browser != null) {
+                       Composite parent = browser.getParent();
+                       browser.dispose();
+                       browser = createTable(parent, this.url);
+                       parent.layout(true, true);
+               }
+       }
+
+       protected Table createTable(Composite parent, Path path) {
+               Table table = new Table(parent, SWT.BORDER);
+               table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               table.addMouseListener(new MouseAdapter() {
+
+                       @Override
+                       public void mouseDoubleClick(MouseEvent e) {
+                               Point pt = new Point(e.x, e.y);
+                               TableItem item = table.getItem(pt);
+                               Path path = (Path) item.getData();
+                               if (Files.isDirectory(path)) {
+                                       setUrl(path);
+                               } else {
+                                       Program.launch(path.toString());
+                               }
+                       }
+               });
+
+               if (path != null) {
+                       if (path.getParent() != null) {
+                               TableItem parentTI = new TableItem(table, SWT.NONE);
+                               parentTI.setText("..");
+                               parentTI.setData(path.getParent());
+                       }
+
+                       try {
+                               // directories
+                               DirectoryStream<Path> ds = Files.newDirectoryStream(url, p -> Files.isDirectory(p) && isShown(p));
+                               ds.forEach(p -> {
+                                       TableItem ti = new TableItem(table, SWT.NONE);
+                                       ti.setText(p.getFileName().toString() + "/");
+                                       ti.setData(p);
+                               });
+                               // files
+                               ds = Files.newDirectoryStream(url, p -> !Files.isDirectory(p) && isShown(p));
+                               ds.forEach(p -> {
+                                       TableItem ti = new TableItem(table, SWT.NONE);
+                                       ti.setText(p.getFileName().toString());
+                                       ti.setData(p);
+                               });
+                       } catch (IOException e1) {
+                               // TODO Auto-generated catch block
+                               e1.printStackTrace();
+                       }
+               }
+               return table;
+       }
+
+       protected boolean isShown(Path path) {
+               if (showHidden)
+                       return true;
+               try {
+                       return !Files.isHidden(path);
+               } catch (IOException e) {
+                       throw new IllegalArgumentException("Cannot check " + path, e);
+               }
+       }
+
+       public void setUrl(String url) {
+               setUrl(Paths.get(url));
+       }
+
+       public static void main(String[] args) {
+               Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent();
+               Shell shell = new Shell(display, SWT.SHELL_TRIM);
+
+               MiniExplorer miniBrowser = new MiniExplorer(shell, SWT.NONE);
+               String url = args.length > 0 ? args[0] : System.getProperty("user.home");
+               miniBrowser.setUrl(url);
+
+               shell.open();
+               shell.setSize(new Point(800, 480));
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+       }
+
+}
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniImageViewer.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniImageViewer.java
new file mode 100644 (file)
index 0000000..21adbc3
--- /dev/null
@@ -0,0 +1,129 @@
+package org.argeo.cms.desktop.mini;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.graphics.Point;
+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.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class MiniImageViewer implements PaintListener {
+       private URL url;
+       private Canvas area;
+
+       private Image image;
+
+       public MiniImageViewer(Composite parent, int style) {
+               parent.setLayout(new GridLayout());
+
+               Composite toolBar = new Composite(parent, SWT.NONE);
+               toolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               toolBar.setLayout(new RowLayout());
+               Button load = new Button(toolBar, SWT.FLAT);
+               load.setText("\u2191");// up arrow
+               load.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               FileDialog fileDialog = new FileDialog(area.getShell());
+                               String path = fileDialog.open();
+                               if (path != null) {
+                                       setUrl(path);
+                               }
+                       }
+
+               });
+
+               area = new Canvas(parent, SWT.NONE);
+               area.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               area.addPaintListener(this);
+       }
+
+       protected void load(URL url) {
+               try {
+                       ImageLoader imageLoader = new ImageLoader();
+                       ImageData[] data = imageLoader.load(url.openStream());
+                       image = new Image(area.getDisplay(), data[0]);
+               } catch (IOException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+       }
+
+       @Override
+       public void paintControl(PaintEvent e) {
+               e.gc.drawImage(image, 0, 0);
+
+       }
+
+       protected Path url2path(URL url) {
+               try {
+                       Path path = Paths.get(url.toURI());
+                       return path;
+               } catch (URISyntaxException e) {
+                       throw new IllegalStateException("Cannot convert " + url + " to uri", e);
+               }
+       }
+
+       public void setUrl(URL url) {
+               this.url = url;
+               if (area != null)
+                       load(this.url);
+       }
+
+       public void setUrl(String url) {
+               try {
+                       setUrl(new URL(url));
+               } catch (MalformedURLException e) {
+                       // try with http
+                       try {
+                               setUrl(new URL("file://" + url));
+                               return;
+                       } catch (MalformedURLException e1) {
+                               // nevermind...
+                       }
+                       throw new IllegalArgumentException("Cannot interpret URL " + url, e);
+               }
+       }
+
+       public static void main(String[] args) {
+               Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent();
+               Shell shell = new Shell(display, SWT.SHELL_TRIM);
+
+               MiniImageViewer miniBrowser = new MiniImageViewer(shell, SWT.NONE);
+               String url = args.length > 0 ? args[0] : "";
+               if (!url.trim().equals("")) {
+                       miniBrowser.setUrl(url);
+                       shell.setText(url);
+               } else {
+                       shell.setText("*");
+               }
+
+               shell.open();
+               shell.setSize(new Point(800, 480));
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+       }
+
+}
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniTerminal.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniTerminal.java
new file mode 100644 (file)
index 0000000..78a8872
--- /dev/null
@@ -0,0 +1,314 @@
+package org.argeo.cms.desktop.mini;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Caret;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class MiniTerminal implements KeyListener, PaintListener {
+
+       private Canvas area;
+       private Caret caret;
+
+       private StringBuffer buf = new StringBuffer("");
+       private StringBuffer userInput = new StringBuffer("");
+       private List<String> history = new ArrayList<>();
+
+       private Point charExtent = null;
+       private int charsPerLine = 0;
+       private String[] lines = new String[0];
+       private List<String> logicalLines = new ArrayList<>();
+
+       private Font mono;
+       private Charset charset;
+
+       private Path currentDir;
+       private Path homeDir;
+       private String host = "localhost";
+       private String username;
+
+       private boolean running = false;
+       private OutputStream stdIn = null;
+
+       public MiniTerminal(Composite parent, int style) {
+               charset = StandardCharsets.UTF_8;
+
+               Display display = parent.getDisplay();
+               // Linux-specific
+               mono = new Font(display, "Monospace", 10, SWT.NONE);
+
+               parent.setLayout(new GridLayout());
+               area = new Canvas(parent, SWT.NONE);
+               area.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               caret = new Caret(area, SWT.NONE);
+               area.setCaret(caret);
+
+               area.addKeyListener(this);
+               area.addPaintListener(this);
+
+               username = System.getProperty("user.name");
+               try {
+                       host = InetAddress.getLocalHost().getHostName();
+                       if (host.indexOf('.') > 0)
+                               host = host.substring(0, host.indexOf('.'));
+               } catch (UnknownHostException e) {
+                       host = "localhost";
+               }
+               homeDir = Paths.get(System.getProperty("user.home"));
+               currentDir = homeDir;
+
+               buf = new StringBuffer(prompt());
+       }
+
+       @Override
+       public void keyPressed(KeyEvent e) {
+       }
+
+       @Override
+       public void keyReleased(KeyEvent e) {
+               if (e.keyLocation != 0)
+                       return;// weird characters
+               // System.out.println(e.character);
+               if (e.keyCode == 0xd) {// return
+                       markLogicalLine();
+                       if (!running)
+                               processUserInput();
+                       // buf.append(prompt());
+               } else if (e.keyCode == 0x8) {// delete
+                       if (userInput.length() == 0)
+                               return;
+                       userInput.setLength(userInput.length() - 1);
+                       if (!running && buf.length() > 0)
+                               buf.setLength(buf.length() - 1);
+               } else {
+                       // if (!running)
+                       buf.append(e.character);
+                       userInput.append(e.character);
+               }
+
+               if (area.isDisposed())
+                       return;
+               area.redraw();
+               // System.out.println("Append " + e);
+
+               if (running) {
+                       if (stdIn != null) {
+                               try {
+                                       stdIn.write(Character.toString(e.character).getBytes(charset));
+                               } catch (IOException e1) {
+                                       // TODO Auto-generated catch block
+                                       e1.printStackTrace();
+                               }
+                       }
+               }
+       }
+
+       protected String prompt() {
+               String fileName = currentDir.equals(homeDir) ? "~" : currentDir.getFileName().toString();
+               String end = username.equals("root") ? "]# " : "]$ ";
+               return "[" + username + "@" + host + " " + fileName + end;
+       }
+
+       private void displayPrompt() {
+               buf.append(prompt() + userInput);
+       }
+
+       protected void markLogicalLine() {
+               String str = buf.toString().trim();
+               logicalLines.add(str);
+               buf = new StringBuffer("");
+       }
+
+       private void processUserInput() {
+               String cmd = userInput.toString();
+               userInput = new StringBuffer("");
+               processUserInput(cmd);
+               history.add(cmd);
+       }
+
+       protected void processUserInput(String input) {
+               try {
+                       StringTokenizer st = new StringTokenizer(input);
+                       List<String> args = new ArrayList<>();
+                       while (st.hasMoreTokens())
+                               args.add(st.nextToken());
+                       if (args.size() == 0) {
+                               displayPrompt();
+                               return;
+                       }
+
+                       // change directory
+                       if (args.get(0).equals("cd")) {
+                               if (args.size() == 1) {
+                                       setPath(homeDir);
+                               } else {
+                                       Path newPath = currentDir.resolve(args.get(1));
+                                       if (!Files.exists(newPath) || !Files.isDirectory(newPath)) {
+                                               println(newPath + ": No such file or directory");
+                                               return;
+                                       }
+                                       setPath(newPath);
+                               }
+                               displayPrompt();
+                               return;
+                       }
+                       // show current directory
+                       else if (args.get(0).equals("pwd")) {
+                               println(currentDir);
+                               displayPrompt();
+                               return;
+                       }
+                       // exit
+                       else if (args.get(0).equals("exit")) {
+                               println("logout");
+                               area.getShell().dispose();
+                               return;
+                       }
+
+                       ProcessBuilder pb = new ProcessBuilder(args);
+                       pb.redirectErrorStream(true);
+                       pb.directory(currentDir.toFile());
+//                     Process process = Runtime.getRuntime().exec(input, null, currentPath.toFile());
+                       Process process = pb.start();
+
+                       stdIn = process.getOutputStream();
+                       Thread readOut = new Thread("Read out") {
+                               @Override
+                               public void run() {
+                                       running = true;
+                                       try (BufferedReader in = new BufferedReader(
+                                                       new InputStreamReader(process.getInputStream(), charset))) {
+                                               String line = null;
+                                               while ((line = in.readLine()) != null) {
+                                                       println(line);
+                                               }
+                                       } catch (IOException e) {
+                                               println(e.getMessage());
+                                       }
+                                       stdIn = null;
+                                       displayPrompt();
+                                       running = false;
+                               }
+                       };
+                       readOut.start();
+               } catch (IOException e) {
+                       println(e.getMessage());
+                       displayPrompt();
+               }
+       }
+
+       protected int linesForLogicalLine(char[] line) {
+               return line.length / charsPerLine + 1;
+       }
+
+       protected void println(Object line) {
+               buf.append(line);
+               markLogicalLine();
+       }
+
+       protected void refreshLines(int charPerLine, int nbrOfLines) {
+               if (lines.length != nbrOfLines) {
+                       lines = new String[nbrOfLines];
+                       Arrays.fill(lines, null);
+               }
+               if (this.charsPerLine != charPerLine)
+                       this.charsPerLine = charPerLine;
+
+               int currentLine = nbrOfLines - 1;
+               // current line
+               if (buf.length() > 0) {
+                       lines[currentLine] = buf.toString();
+               } else {
+                       lines[currentLine] = "";
+               }
+               currentLine--;
+
+               logicalLines: for (int i = logicalLines.size() - 1; i >= 0; i--) {
+                       char[] logicalLine = logicalLines.get(i).toCharArray();
+                       int linesNeeded = linesForLogicalLine(logicalLine);
+                       for (int j = linesNeeded - 1; j >= 0; j--) {
+                               int from = j * charPerLine;
+                               int to = j == linesNeeded - 1 ? from + charPerLine : Math.min(from + charPerLine, logicalLine.length);
+                               lines[currentLine] = new String(Arrays.copyOfRange(logicalLine, from, to));
+//                             System.out.println("Set line " + currentLine + " to : " + lines[currentLine]);
+                               currentLine--;
+                               if (currentLine < 0)
+                                       break logicalLines;
+                       }
+               }
+       }
+
+       @Override
+       public void paintControl(PaintEvent e) {
+               GC gc = e.gc;
+               gc.setFont(mono);
+               if (charExtent == null)
+                       charExtent = gc.textExtent("a");
+
+               Point areaSize = area.getSize();
+               int charPerLine = areaSize.x / charExtent.x;
+               int nbrOfLines = areaSize.y / charExtent.y;
+               refreshLines(charPerLine, nbrOfLines);
+
+               for (int i = 0; i < lines.length; i++) {
+                       String line = lines[i];
+                       if (line != null)
+                               gc.drawString(line, 0, i * charExtent.y);
+               }
+//             String toDraw = buf.toString();
+//             gc.drawString(toDraw, 0, 0);
+//             area.setCaret(caret);
+       }
+
+       public void setPath(String path) {
+               this.currentDir = Paths.get(path);
+       }
+
+       public void setPath(Path path) {
+               this.currentDir = path;
+       }
+
+       public static void main(String[] args) {
+               Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent();
+               Shell shell = new Shell(display, SWT.SHELL_TRIM);
+
+               MiniTerminal miniBrowser = new MiniTerminal(shell, SWT.NONE);
+               String url = args.length > 0 ? args[0] : System.getProperty("user.home");
+               miniBrowser.setPath(url);
+
+               shell.open();
+               shell.setSize(new Point(800, 480));
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+       }
+
+}
diff --git a/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniTextEditor.java b/rcp/org.argeo.cms.desktop/src/org/argeo/cms/desktop/mini/MiniTextEditor.java
new file mode 100644 (file)
index 0000000..cbedb20
--- /dev/null
@@ -0,0 +1,161 @@
+package org.argeo.cms.desktop.mini;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.eclipse.swt.SWT;
+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.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class MiniTextEditor {
+       private URL url;
+       private Text text;
+
+       public MiniTextEditor(Composite parent, int style) {
+               parent.setLayout(new GridLayout());
+
+               Composite toolBar = new Composite(parent, SWT.NONE);
+               toolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               toolBar.setLayout(new RowLayout());
+               Button load = new Button(toolBar, SWT.FLAT);
+               load.setText("\u2191");// up arrow
+               load.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               FileDialog fileDialog = new FileDialog(text.getShell());
+                               String path = fileDialog.open();
+                               if (path != null) {
+                                       setUrl(path);
+                               }
+                       }
+
+               });
+
+               Button save = new Button(toolBar, SWT.FLAT);
+               save.setText("\u2193");// down arrow
+               // save.setText("\u1F609");// emoji
+               save.addSelectionListener(new SelectionAdapter() {
+
+                       @Override
+                       public void widgetSelected(SelectionEvent e) {
+                               save(url);
+                       }
+
+               });
+
+               text = new Text(parent, SWT.WRAP | SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
+               text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+       }
+
+       protected void load(URL url) {
+               text.setText("");
+               // TODO deal with encoding and binary data
+               try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
+                       String line = null;
+                       while ((line = in.readLine()) != null) {
+                               text.append(line + "\n");
+                       }
+                       text.setEditable(true);
+               } catch (IOException e) {
+                       if (e instanceof FileNotFoundException) {
+                               Path path = url2path(url);
+                               try {
+                                       Files.createFile(path);
+                                       load(url);
+                                       return;
+                               } catch (IOException e1) {
+                                       e = e1;
+                               }
+                       }
+                       text.setText(e.getMessage());
+                       text.setEditable(false);
+                       e.printStackTrace();
+                       // throw new IllegalStateException("Cannot load " + url, e);
+               }
+       }
+
+       protected Path url2path(URL url) {
+               try {
+                       Path path = Paths.get(url.toURI());
+                       return path;
+               } catch (URISyntaxException e) {
+                       throw new IllegalStateException("Cannot convert " + url + " to uri", e);
+               }
+       }
+
+       protected void save(URL url) {
+               if (!url.getProtocol().equals("file"))
+                       throw new IllegalArgumentException(url.getProtocol() + " protocol is not supported for write");
+               Path path = url2path(url);
+               try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(path)))) {
+                       out.write(text.getText());
+               } catch (IOException e) {
+                       throw new IllegalStateException("Cannot save " + url + " to " + path, e);
+               }
+       }
+
+       public void setUrl(URL url) {
+               this.url = url;
+               if (text != null)
+                       load(url);
+       }
+
+       public void setUrl(String url) {
+               try {
+                       setUrl(new URL(url));
+               } catch (MalformedURLException e) {
+                       // try with http
+                       try {
+                               setUrl(new URL("file://" + url));
+                               return;
+                       } catch (MalformedURLException e1) {
+                               // nevermind...
+                       }
+                       throw new IllegalArgumentException("Cannot interpret URL " + url, e);
+               }
+       }
+
+       public static void main(String[] args) {
+               Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent();
+               Shell shell = new Shell(display, SWT.SHELL_TRIM);
+
+               MiniTextEditor miniBrowser = new MiniTextEditor(shell, SWT.NONE);
+               String url = args.length > 0 ? args[0] : "";
+               if (!url.trim().equals("")) {
+                       miniBrowser.setUrl(url);
+                       shell.setText(url);
+               } else {
+                       shell.setText("*");
+               }
+
+               shell.open();
+               shell.setSize(new Point(800, 480));
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+       }
+
+}
index 90704583dbdbdf2b4865ed2de6a476e8a4e56f5a..5ec8d78e3fe30f65683019364ea9c5a9f3287eb9 100644 (file)
@@ -13,6 +13,7 @@
        <packaging>pom</packaging>
        <modules>
                <module>org.argeo.eclipse.ui.rcp</module>
+               <module>org.argeo.cms.desktop</module>
                <module>org.argeo.cms.e4.rcp</module>
                <module>dep</module>
                <!-- <module>demo</module> -->