| /******************************************************************************* |
| * Copyright (c) 2011 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.scout.rt.ui.rap.form.fields.filechooserfield; |
| |
| import java.io.File; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.rap.rwt.RWT; |
| import org.eclipse.rap.rwt.supplemental.fileupload.DiskFileUploadReceiver; |
| import org.eclipse.rap.rwt.supplemental.fileupload.FileUploadEvent; |
| import org.eclipse.rap.rwt.supplemental.fileupload.FileUploadHandler; |
| import org.eclipse.rap.rwt.supplemental.fileupload.FileUploadListener; |
| import org.eclipse.rap.rwt.supplemental.fileupload.FileUploadReceiver; |
| import org.eclipse.scout.commons.CompareUtility; |
| import org.eclipse.scout.commons.StringUtility; |
| import org.eclipse.scout.commons.exception.IProcessingStatus; |
| import org.eclipse.scout.commons.exception.ProcessingStatus; |
| import org.eclipse.scout.commons.holders.Holder; |
| import org.eclipse.scout.commons.job.JobEx; |
| import org.eclipse.scout.commons.logger.IScoutLogger; |
| import org.eclipse.scout.commons.logger.ScoutLogManager; |
| import org.eclipse.scout.rt.client.ui.action.menu.IMenu; |
| import org.eclipse.scout.rt.client.ui.form.fields.IFormField; |
| import org.eclipse.scout.rt.client.ui.form.fields.ScoutFieldStatus; |
| import org.eclipse.scout.rt.client.ui.form.fields.filechooserfield.IFileChooserField; |
| import org.eclipse.scout.rt.ui.rap.LogicalGridLayout; |
| import org.eclipse.scout.rt.ui.rap.RwtMenuUtility; |
| import org.eclipse.scout.rt.ui.rap.ext.IDropDownFileUploadForPatch; |
| import org.eclipse.scout.rt.ui.rap.ext.MenuAdapterEx; |
| import org.eclipse.scout.rt.ui.rap.ext.StatusLabelEx; |
| import org.eclipse.scout.rt.ui.rap.ext.StyledTextEx; |
| import org.eclipse.scout.rt.ui.rap.ext.custom.StyledText; |
| import org.eclipse.scout.rt.ui.rap.form.fields.LogicalGridDataBuilder; |
| import org.eclipse.scout.rt.ui.rap.form.fields.RwtScoutValueFieldComposite; |
| import org.eclipse.scout.rt.ui.rap.internal.TextFieldEditableSupport; |
| import org.eclipse.scout.rt.ui.rap.keystroke.IRwtKeyStroke; |
| import org.eclipse.scout.rt.ui.rap.keystroke.RwtKeyStroke; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.FocusAdapter; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.MenuEvent; |
| 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.Color; |
| import org.eclipse.swt.layout.FormAttachment; |
| import org.eclipse.swt.layout.FormData; |
| import org.eclipse.swt.layout.FormLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.ProgressBar; |
| |
| public class RwtScoutFileUploadField extends RwtScoutValueFieldComposite<IFileChooserField> implements IRwtScoutFileUploadField { |
| private static final IScoutLogger LOG = ScoutLogManager.getLogger(RwtScoutFileUploadField.class); |
| |
| private Composite m_fileContainer; |
| private IDropDownFileUploadForPatch m_browseButton; |
| private ProgressBar m_progressBar; |
| private Menu m_contextMenu; |
| private TextFieldEditableSupport m_editableSupport; |
| |
| private FileUploadHandler m_handler; |
| private FileUploadListener m_uploadListener; |
| private File m_uploadedFile = null; |
| |
| private String m_originalVariant = ""; |
| |
| public RwtScoutFileUploadField() { |
| initializeFileUpload(); |
| } |
| |
| @Override |
| protected void initializeUi(Composite parent) { |
| super.initializeUi(parent); |
| Composite container = getUiEnvironment().getFormToolkit().createComposite(parent); |
| StatusLabelEx label = getUiEnvironment().getFormToolkit().createStatusLabel(container, getScoutObject()); |
| |
| m_fileContainer = getUiEnvironment().getFormToolkit().createComposite(container, SWT.BORDER); |
| m_fileContainer.setData(RWT.CUSTOM_VARIANT, VARIANT_FILECHOOSER); |
| |
| StyledText textField = new StyledTextEx(m_fileContainer, SWT.SINGLE | getUiEnvironment().getFormToolkit().getOrientation()) { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void setBackground(Color color) { |
| if (getUiProgressBar() != null && !getUiProgressBar().isDisposed()) { |
| getUiProgressBar().setBackground(color); |
| } |
| if (getUiBrowseButton() != null && !getUiBrowseButton().isDisposed()) { |
| getUiBrowseButton().setBackground(color); |
| } |
| } |
| }; |
| getUiEnvironment().getFormToolkit().adapt(textField, false, false); |
| // correction to look like a normal text |
| textField.setData(RWT.CUSTOM_VARIANT, VARIANT_FILECHOOSER); |
| //textfield must be disabled. We can't upload the file from it for now. |
| textField.setEnabled(false); |
| |
| setUiContainer(container); |
| setUiLabel(label); |
| setUiField(textField); |
| |
| createProgressBar(); |
| createBrowseButton(); |
| |
| // prevent the button from grabbing focus |
| m_fileContainer.setTabList(new Control[]{textField}); |
| |
| // context menu |
| m_contextMenu = new Menu(getUiBrowseButton().getShell(), SWT.POP_UP); |
| m_contextMenu.addMenuListener(new P_ContextMenuListener()); |
| getUiBrowseButton().setMenu(m_contextMenu); |
| |
| // layout |
| container.setLayout(new LogicalGridLayout(1, 0)); |
| |
| m_fileContainer.setLayoutData(LogicalGridDataBuilder.createField(((IFormField) getScoutObject()).getGridData())); |
| m_fileContainer.setLayout(new FormLayout()); |
| |
| final FormData textLayoutData = new FormData(SWT.DEFAULT, SWT.DEFAULT); |
| textLayoutData.right = new FormAttachment(100, -20); |
| textLayoutData.left = new FormAttachment(0, 0); |
| textLayoutData.bottom = new FormAttachment(textField, -1, SWT.BOTTOM); |
| textField.setLayoutData(textLayoutData); |
| } |
| |
| private void createBrowseButton() { |
| m_browseButton = getUiEnvironment().getFormToolkit().createDropDownFileUpload(m_fileContainer, SWT.DROP_DOWN); |
| m_browseButton.setData(RWT.CUSTOM_VARIANT, VARIANT_FILECHOOSER); |
| // to ensure the text is validated on a context menu call this mouse |
| // listener is used. |
| m_browseButton.addMouseListener(new MouseAdapter() { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void mouseDown(MouseEvent e) { |
| handleUiInputVerifier(true); |
| } |
| }); |
| m_browseButton.addFocusListener(new FocusAdapter() { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void focusGained(FocusEvent e) { |
| getUiField().setFocus(); |
| } |
| }); |
| setBackgroundFromScout(getScoutObject().getBackgroundColor()); |
| |
| getUiBrowseButton().addSelectionListener(new SelectionAdapter() { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void widgetSelected(SelectionEvent event) { |
| String filename = getUiBrowseButton().getFileName(); |
| if (filename == null) { |
| return; |
| } |
| |
| getUiField().setText(filename); |
| handleUpload(); |
| } |
| }); |
| |
| final FormData buttonLayoutData = new FormData(SWT.DEFAULT, SWT.DEFAULT); |
| buttonLayoutData.left = new FormAttachment(getUiField(), 0, SWT.RIGHT); |
| buttonLayoutData.bottom = new FormAttachment((Control) getUiBrowseButton(), 0, SWT.BOTTOM); |
| buttonLayoutData.height = 20; |
| buttonLayoutData.width = 20; |
| getUiBrowseButton().setLayoutData(buttonLayoutData); |
| |
| setEnabledFromScout(getScoutObject().isEnabled()); |
| m_fileContainer.layout(); |
| } |
| |
| private void createProgressBar() { |
| m_progressBar = new ProgressBar(m_fileContainer, SWT.HORIZONTAL | SWT.SMOOTH | getUiEnvironment().getFormToolkit().getOrientation()); |
| getUiEnvironment().getFormToolkit().adapt(m_progressBar, true, true); |
| setBackgroundFromScout(getScoutObject().getBackgroundColor()); |
| |
| final FormData progressLayoutData = new FormData(SWT.DEFAULT, SWT.DEFAULT); |
| progressLayoutData.right = new FormAttachment(100, -20); |
| progressLayoutData.left = new FormAttachment(0, 0); |
| progressLayoutData.top = new FormAttachment(2, 0); |
| progressLayoutData.bottom = new FormAttachment(m_progressBar, 4, SWT.BOTTOM); |
| m_progressBar.setLayoutData(progressLayoutData); |
| |
| m_fileContainer.layout(); |
| } |
| |
| @Override |
| public void disposeImpl() { |
| if (m_uploadListener != null) { |
| m_handler.removeUploadListener(m_uploadListener); |
| m_uploadListener = null; |
| } |
| if (m_handler != null) { |
| m_handler.dispose(); |
| m_handler = null; |
| } |
| if (m_browseButton != null) { |
| m_browseButton.dispose(); |
| m_browseButton = null; |
| } |
| } |
| |
| private void initializeFileUpload() { |
| FileUploadReceiver receiver = new DiskFileUploadReceiver(); |
| m_handler = new FileUploadHandler(receiver); |
| } |
| |
| @Override |
| public IDropDownFileUploadForPatch getUiBrowseButton() { |
| return m_browseButton; |
| } |
| |
| @Override |
| public ProgressBar getUiProgressBar() { |
| return m_progressBar; |
| } |
| |
| @Override |
| public StyledText getUiField() { |
| return (StyledText) super.getUiField(); |
| } |
| |
| @Override |
| protected IRwtKeyStroke[] getUiKeyStrokes() { |
| return new IRwtKeyStroke[]{ |
| new RwtKeyStroke(SWT.ESC) { |
| @Override |
| public void handleUiAction(Event e) { |
| if (cancelUpload()) { |
| e.doit = false; |
| } |
| } |
| }, |
| new RwtKeyStroke(SWT.CR) { |
| @Override |
| public void handleUiAction(Event e) { |
| handleUiInputVerifier(e.doit); |
| } |
| }}; |
| } |
| |
| /* |
| * scout properties |
| */ |
| @Override |
| protected void attachScout() { |
| super.attachScout(); |
| setFileIconIdFromScout(getScoutObject().getFileIconId()); |
| getUiBrowseButton().setDropdownEnabled(getScoutObject().hasMenus()); |
| if (getScoutObject().isFolderMode()) { |
| String msg = "IFileChooserField.isFolderMode() == true is not possible in RAP"; |
| LOG.error(msg); |
| getUiLabel().setStatus(new ProcessingStatus(msg, IProcessingStatus.ERROR)); |
| } |
| } |
| |
| @Override |
| protected void setDisplayTextFromScout(String s) { |
| if (s == null) { |
| s = ""; |
| } |
| getUiField().setText(s); |
| super.handleUiFocusGained(); |
| getUiField().setCaretOffset(0); |
| } |
| |
| @Override |
| protected void setEnabledFromScout(boolean b) { |
| super.setEnabledFromScout(b); |
| getUiBrowseButton().setButtonEnabled(b); |
| //textfield must be disabled. We can't upload the file from it for now. |
| getUiField().setEnabled(false); |
| |
| if (!StringUtility.hasText(m_originalVariant)) { |
| m_originalVariant = (String) m_fileContainer.getData(RWT.CUSTOM_VARIANT); |
| } |
| String customVariant = b ? m_originalVariant : m_originalVariant + VARIANT_DISABLED_SUFFIX; |
| m_fileContainer.setData(RWT.CUSTOM_VARIANT, customVariant); |
| } |
| |
| @Override |
| protected void setFieldEnabled(Control field, boolean enabled) { |
| if (m_editableSupport == null) { |
| m_editableSupport = new TextFieldEditableSupport(getUiField()); |
| } |
| m_editableSupport.setEditable(enabled); |
| } |
| |
| protected void setFileIconIdFromScout(String s) { |
| m_originalVariant = s; |
| m_fileContainer.setData(RWT.CUSTOM_VARIANT, s); |
| getUiField().setData(RWT.CUSTOM_VARIANT, s); |
| getUiBrowseButton().setData(RWT.CUSTOM_VARIANT, s); |
| } |
| |
| @Override |
| protected void handleUiInputVerifier(boolean doit) { |
| if (!doit) { |
| return; |
| } |
| if (m_uploadedFile == null) { |
| return; |
| } |
| // only handle if text has changed |
| if (CompareUtility.equals(m_uploadedFile, getScoutObject().getDisplayText()) && getScoutObject().getErrorStatus() == null) { |
| return; |
| } |
| final Holder<Boolean> result = new Holder<Boolean>(Boolean.class, false); |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| boolean b = getScoutObject().getUIFacade().setTextFromUI(m_uploadedFile.getAbsolutePath()); |
| result.setValue(b); |
| } |
| }; |
| JobEx job = getUiEnvironment().invokeScoutLater(t, 0); |
| try { |
| job.join(2345); |
| } |
| catch (InterruptedException e) { |
| //nop |
| } |
| getUiEnvironment().dispatchImmediateUiJobs(); |
| // end notify |
| return; |
| } |
| |
| @Override |
| protected void handleUiFocusGained() { |
| super.handleUiFocusGained(); |
| getUiField().setSelection(0, getUiField().getText().length()); |
| } |
| |
| @Override |
| protected void handleUiFocusLost() { |
| getUiField().setSelection(0, 0); |
| } |
| |
| private void handleUpload() { |
| m_uploadedFile = null; |
| String url = m_handler.getUploadUrl(); |
| if (m_uploadListener == null) { |
| m_uploadListener = new P_FileUploadListener(); |
| } |
| m_handler.addUploadListener(m_uploadListener); |
| getUiBrowseButton().submit(url); |
| } |
| |
| private boolean cancelUpload() { |
| if (m_uploadedFile != null) { |
| return false; |
| } |
| if (m_uploadListener != null) { |
| m_handler.removeUploadListener(m_uploadListener); |
| m_uploadListener = null; |
| } |
| m_handler.dispose(); |
| getUiBrowseButton().dispose(); |
| getUiProgressBar().dispose(); |
| getUiField().setText(""); |
| |
| initializeFileUpload(); |
| |
| createProgressBar(); |
| createBrowseButton(); |
| return true; |
| } |
| |
| @Override |
| protected void handleScoutPropertyChange(String name, Object newValue) { |
| super.handleScoutPropertyChange(name, newValue); |
| if (name.equals(IFileChooserField.PROP_FILE_ICON_ID)) { |
| setFileIconIdFromScout((String) newValue); |
| } |
| } |
| |
| private class P_ContextMenuListener extends MenuAdapterEx { |
| |
| private static final long serialVersionUID = 1L; |
| |
| public P_ContextMenuListener() { |
| super((Control) RwtScoutFileUploadField.this.getUiBrowseButton(), RwtScoutFileUploadField.this.getUiBrowseButton().getParent()); |
| } |
| |
| @Override |
| public void menuShown(MenuEvent e) { |
| super.menuShown(e); |
| /* |
| * Since the FileUpload widget does fire a MouseEvent when it is clicked on, we do not know if the right mouse button has been clicked or not. |
| * Therefore, a MenuDetect listener cannot be attached. The creation of the menu is done once after initialization. |
| * While handling the menuShown, the menu (event source) might already contain contributed Scout menus from the previous right mouse click. To |
| * avoid the menus being added several times, the menu items are removed first before the Scout menus are contributed. |
| */ |
| Menu contextMenu = (Menu) e.getSource(); |
| cleanUpMenuItems(contextMenu); |
| |
| final AtomicReference<IMenu[]> scoutMenusRef = new AtomicReference<IMenu[]>(); |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| IMenu[] scoutMenus = getScoutObject().getUIFacade().firePopupFromUI(); |
| scoutMenusRef.set(scoutMenus); |
| } |
| }; |
| JobEx job = RwtScoutFileUploadField.this.getUiEnvironment().invokeScoutLater(t, 1200); |
| try { |
| job.join(1200); |
| } |
| catch (InterruptedException ex) { |
| //nop |
| } |
| // grab the actions out of the job, when the actions are providden within |
| // the scheduled time the popup will be handled. |
| if (scoutMenusRef.get() != null) { |
| RwtMenuUtility.fillContextMenu(scoutMenusRef.get(), RwtScoutFileUploadField.this.getUiEnvironment(), contextMenu); |
| } |
| } |
| |
| protected void cleanUpMenuItems(Menu menu) { |
| for (MenuItem menuItem : menu.getItems()) { |
| disposeMenuItem(menuItem); |
| } |
| } |
| |
| protected void disposeMenuItem(MenuItem item) { |
| Menu menu = item.getMenu(); |
| if (menu != null) { |
| for (MenuItem childItem : menu.getItems()) { |
| disposeMenuItem(childItem); |
| } |
| menu.dispose(); |
| } |
| item.dispose(); |
| } |
| } // end class P_ContextMenuListener |
| |
| private class P_FileUploadListener implements FileUploadListener { |
| |
| @Override |
| public void uploadProgress(final FileUploadEvent uploadEvent) { |
| getUiEnvironment().getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| double fraction = uploadEvent.getBytesRead() / (double) uploadEvent.getContentLength(); |
| int percent = (int) Math.floor(fraction * 100); |
| if (getUiProgressBar() != null && !getUiProgressBar().isDisposed()) { |
| getUiProgressBar().setSelection(percent); |
| getUiProgressBar().setToolTipText("Upload progress: " + percent + "%"); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void uploadFinished(final FileUploadEvent uploadEvent) { |
| DiskFileUploadReceiver receiver = (DiskFileUploadReceiver) m_handler.getReceiver(); |
| m_uploadedFile = receiver.getTargetFile(); |
| getUiEnvironment().getDisplay().asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| if (m_uploadedFile != null) { |
| handleUiInputVerifier(true); |
| } |
| } |
| }); |
| getUiEnvironment().getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| int percent = 0; |
| if (getUiProgressBar() != null && !getUiProgressBar().isDisposed()) { |
| getUiProgressBar().setSelection(percent); |
| getUiProgressBar().setToolTipText(""); |
| } |
| m_handler.removeUploadListener(m_uploadListener); |
| } |
| }); |
| } |
| |
| @Override |
| public void uploadFailed(final FileUploadEvent uploadEvent) { |
| getUiEnvironment().getDisplay().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| if (getUiLabel() != null) { |
| getUiLabel().setStatus(new ScoutFieldStatus(uploadEvent.getException().getMessage(), IStatus.ERROR)); |
| } |
| m_handler.removeUploadListener(m_uploadListener); |
| } |
| }); |
| } |
| } |
| } |