1 package org.argeo.cms.ui.fs;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.lang.reflect.Method;
7 import java.net.URI;
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;
15 import java.util.Map;
16
17 import org.apache.commons.io.IOUtils;
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.argeo.cms.CmsException;
21 import org.argeo.cms.ui.util.CmsUiUtils;
22 import org.argeo.eclipse.ui.EclipseUiUtils;
23 import org.argeo.eclipse.ui.dialogs.SingleValue;
24 import org.eclipse.jface.dialogs.MessageDialog;
25 import org.eclipse.jface.viewers.IStructuredSelection;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.events.SelectionAdapter;
28 import org.eclipse.swt.events.SelectionEvent;
29 import org.eclipse.swt.events.ShellEvent;
30 import org.eclipse.swt.graphics.Point;
31 import org.eclipse.swt.layout.GridData;
32 import org.eclipse.swt.widgets.Button;
33 import org.eclipse.swt.widgets.Composite;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.swt.widgets.FileDialog;
36 import org.eclipse.swt.widgets.Label;
37 import org.eclipse.swt.widgets.Shell;
38
39
40 public class FsContextMenu extends Shell {
41 private static final long serialVersionUID = -9120261153509855795L;
42
43 private final static Log log = LogFactory.getLog(FsContextMenu.class);
44
45
46 public final static String ACTION_ID_CREATE_FOLDER = "createFolder";
47 public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder";
48 public final static String ACTION_ID_SHARE_FOLDER = "shareFolder";
49 public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder";
50 public final static String ACTION_ID_DELETE = "delete";
51 public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles";
52 public final static String ACTION_ID_OPEN = "open";
53
54
55 private final CmsFsBrowser browser;
56
57 private final static String KEY_ACTION_ID = "actionId";
58 private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER,
59 ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_UPLOAD_FILE,
60 ACTION_ID_OPEN };
61 private Map<String, Button> actionButtons = new HashMap<String, Button>();
62
63 private Path currFolderPath;
64
65 public FsContextMenu(CmsFsBrowser browser) {
66
67 super(browser.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
68 this.browser = browser;
69 setLayout(EclipseUiUtils.noSpaceGridLayout());
70
71 Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER);
72 boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout());
73 CmsUiUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX);
74 createContextMenu(boxCmp);
75
76 addShellListener(new ActionsShellListener());
77 }
78
79 protected void createContextMenu(Composite boxCmp) {
80 ActionsSelListener asl = new ActionsSelListener();
81 for (String actionId : DEFAULT_ACTIONS) {
82 Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD);
83 btn.setText(getLabel(actionId));
84 btn.setLayoutData(EclipseUiUtils.fillWidth());
85 CmsUiUtils.markup(btn);
86 CmsUiUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX);
87 btn.setData(KEY_ACTION_ID, actionId);
88 btn.addSelectionListener(asl);
89 actionButtons.put(actionId, btn);
90 }
91 }
92
93 protected String getLabel(String actionId) {
94 switch (actionId) {
95 case ACTION_ID_CREATE_FOLDER:
96 return "Create Folder";
97 case ACTION_ID_BOOKMARK_FOLDER:
98 return "Bookmark Folder";
99 case ACTION_ID_SHARE_FOLDER:
100 return "Share Folder";
101 case ACTION_ID_DOWNLOAD_FOLDER:
102 return "Download as zip archive";
103 case ACTION_ID_DELETE:
104 return "Delete";
105 case ACTION_ID_UPLOAD_FILE:
106 return "Upload Files";
107 case ACTION_ID_OPEN:
108 return "Open";
109 default:
110 throw new IllegalArgumentException("Unknown action ID " + actionId);
111 }
112 }
113
114 protected void aboutToShow(Control source, Point location) {
115 IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
116 boolean emptySel = true;
117 boolean multiSel = false;
118 boolean isFolder = true;
119 if (selection != null && !selection.isEmpty()) {
120 emptySel = false;
121 multiSel = selection.size() > 1;
122 if (!multiSel && selection.getFirstElement() instanceof Path) {
123 isFolder = Files.isDirectory((Path) selection.getFirstElement());
124 }
125 }
126 if (emptySel) {
127 setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE);
128 setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_OPEN,
129
130 ACTION_ID_BOOKMARK_FOLDER);
131 } else if (multiSel) {
132 setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE);
133 setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_OPEN,
134
135 ACTION_ID_BOOKMARK_FOLDER);
136 } else if (isFolder) {
137 setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE);
138 setVisible(false, ACTION_ID_OPEN,
139
140 ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER);
141 } else {
142 setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_OPEN, ACTION_ID_DELETE);
143 setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER,
144
145 ACTION_ID_BOOKMARK_FOLDER);
146 }
147 }
148
149 private void setVisible(boolean visible, String... buttonIds) {
150 for (String id : buttonIds) {
151 Button button = actionButtons.get(id);
152 button.setVisible(visible);
153 GridData gd = (GridData) button.getLayoutData();
154 gd.heightHint = visible ? SWT.DEFAULT : 0;
155 }
156 }
157
158 public void show(Control source, Point location, Path currFolderPath) {
159 if (isVisible())
160 setVisible(false);
161
162
163 this.currFolderPath = currFolderPath;
164 aboutToShow(source, location);
165 pack();
166 layout();
167 if (source instanceof Control)
168 setLocation(((Control) source).toDisplay(location.x, location.y));
169 open();
170 }
171
172 class StyleButton extends Label {
173 private static final long serialVersionUID = 7731102609123946115L;
174
175 public StyleButton(Composite parent, int swtStyle) {
176 super(parent, swtStyle);
177 }
178
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 class ActionsSelListener extends SelectionAdapter {
217 private static final long serialVersionUID = -1041871937815812149L;
218
219 @Override
220 public void widgetSelected(SelectionEvent e) {
221 Object eventSource = e.getSource();
222 if (eventSource instanceof Button) {
223 Button pressedBtn = (Button) eventSource;
224 String actionId = (String) pressedBtn.getData(KEY_ACTION_ID);
225 switch (actionId) {
226 case ACTION_ID_CREATE_FOLDER:
227 createFolder();
228 break;
229 case ACTION_ID_DELETE:
230 deleteItems();
231 break;
232 case ACTION_ID_OPEN:
233 openFile();
234 break;
235 case ACTION_ID_UPLOAD_FILE:
236 uploadFiles();
237 break;
238 default:
239 throw new IllegalArgumentException("Unimplemented action " + actionId);
240
241
242
243
244
245
246 }
247 }
248 browser.setFocus();
249
250
251
252 }
253 }
254
255 class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter {
256 private static final long serialVersionUID = -5092341449523150827L;
257
258 @Override
259 public void shellDeactivated(ShellEvent e) {
260 setVisible(false);
261 }
262 }
263
264 private void openFile() {
265 log.warn("Implement single sourced, workbench independant \"Open File\" action");
266 }
267
268 private void deleteItems() {
269 IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection());
270 if (selection.isEmpty())
271 return;
272
273 StringBuilder builder = new StringBuilder();
274 @SuppressWarnings("unchecked")
275 Iterator<Object> iterator = selection.iterator();
276 List<Path> paths = new ArrayList<>();
277
278 while (iterator.hasNext()) {
279 Path path = (Path) iterator.next();
280 builder.append(path.getFileName() + ", ");
281 paths.add(path);
282 }
283 String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2)
284 + ". Are you sure?";
285 if (MessageDialog.openConfirm(this, "Confirm deletion", msg)) {
286 for (Path path : paths) {
287 try {
288
289 Files.deleteIfExists(path);
290 } catch (IOException e) {
291 throw new CmsException("Cannot delete path " + path, e);
292 }
293 }
294 browser.refresh();
295 }
296 }
297
298 private void createFolder() {
299 String msg = "Please provide a name.";
300 String name = SingleValue.ask("Create folder", msg);
301
302 if (EclipseUiUtils.notEmpty(name)) {
303 try {
304 Path child = currFolderPath.resolve(name);
305 if (Files.exists(child))
306 throw new CmsException("An item with name " + name + " already exists at "
307 + currFolderPath.toString() + ", cannot create");
308 else
309 Files.createDirectories(child);
310 browser.refresh();
311 } catch (IOException e) {
312 throw new CmsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e);
313 }
314 }
315 }
316
317 private void uploadFiles() {
318 try {
319 FileDialog dialog = new FileDialog(browser.getShell(), SWT.MULTI);
320 dialog.setText("Choose one or more files to upload");
321
322 if (EclipseUiUtils.notEmpty(dialog.open())) {
323 String[] names = dialog.getFileNames();
324
325
326
327
328
329 Method filterMethod = null;
330 Path parPath = null;
331 try {
332 filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath");
333 String filterPath = (String) filterMethod.invoke(dialog);
334 parPath = Paths.get(filterPath);
335 } catch (NoSuchMethodException nsme) {
336 }
337 if (names.length == 0)
338 return;
339 else {
340 loop: for (String name : names) {
341 Path tmpPath = Paths.get(name);
342 if (parPath != null)
343 tmpPath = parPath.resolve(tmpPath);
344 if (Files.exists(tmpPath)) {
345 URI uri = tmpPath.toUri();
346 String uriStr = uri.toString();
347
348 if (Files.isDirectory(tmpPath)) {
349 MessageDialog.openError(browser.getShell(), "Unimplemented directory import",
350 "Upload of directories in the system is not yet implemented");
351 continue loop;
352 }
353 Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString());
354 InputStream in = null;
355 try {
356 in = new ByteArrayInputStream(Files.readAllBytes(tmpPath));
357 Files.copy(in, targetPath);
358 Files.delete(tmpPath);
359 } finally {
360 IOUtils.closeQuietly(in);
361 }
362 if (log.isDebugEnabled())
363 log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString());
364 } else {
365 String msg = "Cannot copy tmp file from " + tmpPath.toString();
366 if (parPath != null)
367 msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS";
368 MessageDialog.openError(browser.getShell(), "Missing file", msg);
369 continue loop;
370 }
371 }
372 browser.refresh();
373 }
374 }
375 } catch (Exception e) {
376 e.printStackTrace();
377 MessageDialog.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath);
378 }
379 }
380
381 public void setCurrFolderPath(Path currFolderPath) {
382 this.currFolderPath = currFolderPath;
383 }
384 }