1 package org
.argeo
.cms
.ui
.fs
;
3 import java
.io
.ByteArrayInputStream
;
4 import java
.io
.IOException
;
5 import java
.io
.InputStream
;
6 import java
.lang
.reflect
.Method
;
8 import java
.nio
.file
.Files
;
9 import java
.nio
.file
.Path
;
10 import java
.nio
.file
.Paths
;
11 import java
.util
.ArrayList
;
12 import java
.util
.HashMap
;
13 import java
.util
.Iterator
;
14 import java
.util
.List
;
17 import org
.apache
.commons
.io
.IOUtils
;
18 import org
.argeo
.api
.cms
.CmsLog
;
19 import org
.argeo
.cms
.swt
.CmsException
;
20 import org
.argeo
.cms
.swt
.CmsSwtUtils
;
21 import org
.argeo
.eclipse
.ui
.EclipseUiUtils
;
22 import org
.argeo
.eclipse
.ui
.dialogs
.SingleValue
;
23 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
24 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
25 import org
.eclipse
.swt
.SWT
;
26 import org
.eclipse
.swt
.events
.SelectionAdapter
;
27 import org
.eclipse
.swt
.events
.SelectionEvent
;
28 import org
.eclipse
.swt
.events
.ShellEvent
;
29 import org
.eclipse
.swt
.graphics
.Point
;
30 import org
.eclipse
.swt
.layout
.GridData
;
31 import org
.eclipse
.swt
.widgets
.Button
;
32 import org
.eclipse
.swt
.widgets
.Composite
;
33 import org
.eclipse
.swt
.widgets
.Control
;
34 import org
.eclipse
.swt
.widgets
.FileDialog
;
35 import org
.eclipse
.swt
.widgets
.Label
;
36 import org
.eclipse
.swt
.widgets
.Shell
;
38 /** Generic popup context menu to manage NIO Path in a Viewer. */
39 public class FsContextMenu
extends Shell
{
40 private static final long serialVersionUID
= -9120261153509855795L;
42 private final static CmsLog log
= CmsLog
.getLog(FsContextMenu
.class);
44 // Default known actions
45 public final static String ACTION_ID_CREATE_FOLDER
= "createFolder";
46 public final static String ACTION_ID_BOOKMARK_FOLDER
= "bookmarkFolder";
47 public final static String ACTION_ID_SHARE_FOLDER
= "shareFolder";
48 public final static String ACTION_ID_DOWNLOAD_FOLDER
= "downloadFolder";
49 public final static String ACTION_ID_DELETE
= "delete";
50 public final static String ACTION_ID_UPLOAD_FILE
= "uploadFiles";
51 public final static String ACTION_ID_OPEN
= "open";
54 private final CmsFsBrowser browser
;
55 // private final Viewer viewer;
56 private final static String KEY_ACTION_ID
= "actionId";
57 private final static String
[] DEFAULT_ACTIONS
= { ACTION_ID_CREATE_FOLDER
, ACTION_ID_BOOKMARK_FOLDER
,
58 ACTION_ID_SHARE_FOLDER
, ACTION_ID_DOWNLOAD_FOLDER
, ACTION_ID_DELETE
, ACTION_ID_UPLOAD_FILE
,
60 private Map
<String
, Button
> actionButtons
= new HashMap
<String
, Button
>();
62 private Path currFolderPath
;
64 public FsContextMenu(CmsFsBrowser browser
) { // Viewer viewer, Display
66 super(browser
.getDisplay(), SWT
.NO_TRIM
| SWT
.BORDER
| SWT
.ON_TOP
);
67 this.browser
= browser
;
68 setLayout(EclipseUiUtils
.noSpaceGridLayout());
70 Composite boxCmp
= new Composite(this, SWT
.NO_FOCUS
| SWT
.BORDER
);
71 boxCmp
.setLayout(EclipseUiUtils
.noSpaceGridLayout());
72 CmsSwtUtils
.style(boxCmp
, FsStyles
.CONTEXT_MENU_BOX
);
73 createContextMenu(boxCmp
);
75 addShellListener(new ActionsShellListener());
78 protected void createContextMenu(Composite boxCmp
) {
79 ActionsSelListener asl
= new ActionsSelListener();
80 for (String actionId
: DEFAULT_ACTIONS
) {
81 Button btn
= new Button(boxCmp
, SWT
.FLAT
| SWT
.PUSH
| SWT
.LEAD
);
82 btn
.setText(getLabel(actionId
));
83 btn
.setLayoutData(EclipseUiUtils
.fillWidth());
84 CmsSwtUtils
.markup(btn
);
85 CmsSwtUtils
.style(btn
, actionId
+ FsStyles
.BUTTON_SUFFIX
);
86 btn
.setData(KEY_ACTION_ID
, actionId
);
87 btn
.addSelectionListener(asl
);
88 actionButtons
.put(actionId
, btn
);
92 protected String
getLabel(String actionId
) {
94 case ACTION_ID_CREATE_FOLDER
:
95 return "Create Folder";
96 case ACTION_ID_BOOKMARK_FOLDER
:
97 return "Bookmark Folder";
98 case ACTION_ID_SHARE_FOLDER
:
99 return "Share Folder";
100 case ACTION_ID_DOWNLOAD_FOLDER
:
101 return "Download as zip archive";
102 case ACTION_ID_DELETE
:
104 case ACTION_ID_UPLOAD_FILE
:
105 return "Upload Files";
109 throw new IllegalArgumentException("Unknown action ID " + actionId
);
113 protected void aboutToShow(Control source
, Point location
) {
114 IStructuredSelection selection
= ((IStructuredSelection
) browser
.getViewer().getSelection());
115 boolean emptySel
= true;
116 boolean multiSel
= false;
117 boolean isFolder
= true;
118 if (selection
!= null && !selection
.isEmpty()) {
120 multiSel
= selection
.size() > 1;
121 if (!multiSel
&& selection
.getFirstElement() instanceof Path
) {
122 isFolder
= Files
.isDirectory((Path
) selection
.getFirstElement());
126 setVisible(true, ACTION_ID_CREATE_FOLDER
, ACTION_ID_UPLOAD_FILE
);
127 setVisible(false, ACTION_ID_SHARE_FOLDER
, ACTION_ID_DOWNLOAD_FOLDER
, ACTION_ID_DELETE
, ACTION_ID_OPEN
,
129 ACTION_ID_BOOKMARK_FOLDER
);
130 } else if (multiSel
) {
131 setVisible(true, ACTION_ID_CREATE_FOLDER
, ACTION_ID_UPLOAD_FILE
, ACTION_ID_DELETE
);
132 setVisible(false, ACTION_ID_SHARE_FOLDER
, ACTION_ID_DOWNLOAD_FOLDER
, ACTION_ID_OPEN
,
134 ACTION_ID_BOOKMARK_FOLDER
);
135 } else if (isFolder
) {
136 setVisible(true, ACTION_ID_CREATE_FOLDER
, ACTION_ID_UPLOAD_FILE
, ACTION_ID_DELETE
);
137 setVisible(false, ACTION_ID_OPEN
,
139 ACTION_ID_SHARE_FOLDER
, ACTION_ID_DOWNLOAD_FOLDER
, ACTION_ID_BOOKMARK_FOLDER
);
141 setVisible(true, ACTION_ID_CREATE_FOLDER
, ACTION_ID_UPLOAD_FILE
, ACTION_ID_OPEN
, ACTION_ID_DELETE
);
142 setVisible(false, ACTION_ID_SHARE_FOLDER
, ACTION_ID_DOWNLOAD_FOLDER
,
144 ACTION_ID_BOOKMARK_FOLDER
);
148 private void setVisible(boolean visible
, String
... buttonIds
) {
149 for (String id
: buttonIds
) {
150 Button button
= actionButtons
.get(id
);
151 button
.setVisible(visible
);
152 GridData gd
= (GridData
) button
.getLayoutData();
153 gd
.heightHint
= visible ? SWT
.DEFAULT
: 0;
157 public void show(Control source
, Point location
, Path currFolderPath
) {
160 // TODO find a better way to retrieve the parent path (cannot be deduced
161 // from table content because it will fail on an empty folder)
162 this.currFolderPath
= currFolderPath
;
163 aboutToShow(source
, location
);
166 if (source
instanceof Control
)
167 setLocation(((Control
) source
).toDisplay(location
.x
, location
.y
));
171 class StyleButton
extends Label
{
172 private static final long serialVersionUID
= 7731102609123946115L;
174 public StyleButton(Composite parent
, int swtStyle
) {
175 super(parent
, swtStyle
);
180 // class ActionsMouseListener extends MouseAdapter {
181 // private static final long serialVersionUID = -1041871937815812149L;
184 // public void mouseDown(MouseEvent e) {
185 // Object eventSource = e.getSource();
186 // if (e.button == 1) {
187 // if (eventSource instanceof Button) {
188 // Button pressedBtn = (Button) eventSource;
189 // String actionId = (String) pressedBtn.getData(KEY_ACTION_ID);
190 // switch (actionId) {
191 // case ACTION_ID_CREATE_FOLDER:
194 // case ACTION_ID_DELETE:
198 // throw new IllegalArgumentException("Unimplemented action " + actionId);
199 // // case ACTION_ID_SHARE_FOLDER:
200 // // return "Share Folder";
201 // // case ACTION_ID_DOWNLOAD_FOLDER:
202 // // return "Download as zip archive";
203 // // case ACTION_ID_UPLOAD_FILE:
204 // // return "Upload Files";
205 // // case ACTION_ID_OPEN:
210 // viewer.getControl().setFocus();
211 // // setVisible(false);
215 class ActionsSelListener
extends SelectionAdapter
{
216 private static final long serialVersionUID
= -1041871937815812149L;
219 public void widgetSelected(SelectionEvent e
) {
220 Object eventSource
= e
.getSource();
221 if (eventSource
instanceof Button
) {
222 Button pressedBtn
= (Button
) eventSource
;
223 String actionId
= (String
) pressedBtn
.getData(KEY_ACTION_ID
);
225 case ACTION_ID_CREATE_FOLDER
:
228 case ACTION_ID_DELETE
:
234 case ACTION_ID_UPLOAD_FILE
:
238 throw new IllegalArgumentException("Unimplemented action " + actionId
);
239 // case ACTION_ID_SHARE_FOLDER:
240 // return "Share Folder";
241 // case ACTION_ID_DOWNLOAD_FOLDER:
242 // return "Download as zip archive";
243 // case ACTION_ID_OPEN:
248 // viewer.getControl().setFocus();
249 // setVisible(false);
254 class ActionsShellListener
extends org
.eclipse
.swt
.events
.ShellAdapter
{
255 private static final long serialVersionUID
= -5092341449523150827L;
258 public void shellDeactivated(ShellEvent e
) {
263 private void openFile() {
264 log
.warn("Implement single sourced, workbench independant \"Open File\" action");
267 private void deleteItems() {
268 IStructuredSelection selection
= ((IStructuredSelection
) browser
.getViewer().getSelection());
269 if (selection
.isEmpty())
272 StringBuilder builder
= new StringBuilder();
273 @SuppressWarnings("unchecked")
274 Iterator
<Object
> iterator
= selection
.iterator();
275 List
<Path
> paths
= new ArrayList
<>();
277 while (iterator
.hasNext()) {
278 Path path
= (Path
) iterator
.next();
279 builder
.append(path
.getFileName() + ", ");
282 String msg
= "You are about to delete following elements: " + builder
.substring(0, builder
.length() - 2)
284 if (MessageDialog
.openConfirm(this, "Confirm deletion", msg
)) {
285 for (Path path
: paths
) {
287 // Might have already been deleted if we are in a tree
288 Files
.deleteIfExists(path
);
289 } catch (IOException e
) {
290 throw new CmsException("Cannot delete path " + path
, e
);
297 private void createFolder() {
298 String msg
= "Please provide a name.";
299 String name
= SingleValue
.ask("Create folder", msg
);
300 // TODO enhance check of name validity
301 if (EclipseUiUtils
.notEmpty(name
)) {
303 Path child
= currFolderPath
.resolve(name
);
304 if (Files
.exists(child
))
305 throw new CmsException("An item with name " + name
+ " already exists at "
306 + currFolderPath
.toString() + ", cannot create");
308 Files
.createDirectories(child
);
310 } catch (IOException e
) {
311 throw new CmsException("Cannot create folder " + name
+ " at " + currFolderPath
.toString(), e
);
316 private void uploadFiles() {
318 FileDialog dialog
= new FileDialog(browser
.getShell(), SWT
.MULTI
);
319 dialog
.setText("Choose one or more files to upload");
321 if (EclipseUiUtils
.notEmpty(dialog
.open())) {
322 String
[] names
= dialog
.getFileNames();
323 // Workaround small differences between RAP and RCP
324 // 1. returned names are absolute path on RAP and
326 // 2. in RCP we must use getFilterPath that does not
328 Method filterMethod
= null;
331 filterMethod
= dialog
.getClass().getDeclaredMethod("getFilterPath");
332 String filterPath
= (String
) filterMethod
.invoke(dialog
);
333 parPath
= Paths
.get(filterPath
);
334 } catch (NoSuchMethodException nsme
) { // RAP
336 if (names
.length
== 0)
339 loop
: for (String name
: names
) {
340 Path tmpPath
= Paths
.get(name
);
342 tmpPath
= parPath
.resolve(tmpPath
);
343 if (Files
.exists(tmpPath
)) {
344 URI uri
= tmpPath
.toUri();
345 String uriStr
= uri
.toString();
347 if (Files
.isDirectory(tmpPath
)) {
348 MessageDialog
.openError(browser
.getShell(), "Unimplemented directory import",
349 "Upload of directories in the system is not yet implemented");
352 Path targetPath
= currFolderPath
.resolve(tmpPath
.getFileName().toString());
353 InputStream in
= null;
355 in
= new ByteArrayInputStream(Files
.readAllBytes(tmpPath
));
356 Files
.copy(in
, targetPath
);
357 Files
.delete(tmpPath
);
359 IOUtils
.closeQuietly(in
);
361 if (log
.isDebugEnabled())
362 log
.debug("copied uploaded file " + uriStr
+ " to " + targetPath
.toString());
364 String msg
= "Cannot copy tmp file from " + tmpPath
.toString();
366 msg
+= "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS";
367 MessageDialog
.openError(browser
.getShell(), "Missing file", msg
);
374 } catch (Exception e
) {
376 MessageDialog
.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath
);
380 public void setCurrFolderPath(Path currFolderPath
) {
381 this.currFolderPath
= currFolderPath
;