/** * */ package org.nightlabs.jfire.issuetracking.ui.issue.editor; import java.awt.Desktop; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import javax.jdo.FetchPlan; import javax.jdo.JDOHelper; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.ui.forms.editor.FormPage; import org.nightlabs.base.ui.composite.XComposite; import org.nightlabs.base.ui.resource.SharedImages; import org.nightlabs.base.ui.util.RCPUtil; import org.nightlabs.jdo.NLJDOHelper; import org.nightlabs.jfire.idgenerator.IDGenerator; import org.nightlabs.jfire.issue.Issue; import org.nightlabs.jfire.issue.IssueFileAttachment; import org.nightlabs.jfire.issue.dao.IssueFileAttachmentDAO; import org.nightlabs.jfire.issue.id.IssueFileAttachmentID; import org.nightlabs.jfire.issuetracking.ui.resource.Messages; import org.nightlabs.progress.NullProgressMonitor; import org.nightlabs.util.IOUtil; /** * A section for the {@link IssueEditorGeneralPage} to handle the interface mechanisms for the * {@link IssueFileAttachment}s. * * @author Chairat Kongarayawetchakun <!-- chairat [AT] nightlabs [DOT] de --> * @author Khaireel Mohamed - khaireel at nightlabs dot de */ public class IssueFileAttachmentSection extends AbstractIssueEditorGeneralSection { private IssueFileAttachmentTable issueFileAttachmentTable; private DownloadFileToolbarAction downloadFileToolbarAction; private AddFileToolbarAction addFileToolbarAction; private RemoveFileToolbarAction removeFileToolbarAction; private EditFileToolbarAction editFileToolbarAction; /** * Creates a new instance of an IssueFileAttachmentSection. */ public IssueFileAttachmentSection(FormPage page, Composite parent, final IssueEditorPageController controller) { super(page, parent, controller); // getClient().getGridLayout().numColumns = 2; // getClient().getGridLayout().makeColumnsEqualWidth = false; getSection().setText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.section.text")); //$NON-NLS-1$ // Label fileLabel = new Label(getClient(), SWT.NONE); // fileLabel.setText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.label.file.text")); //$NON-NLS-1$ // Top set of Action buttons. editFileToolbarAction = new EditFileToolbarAction(); getToolBarManager().add(editFileToolbarAction); downloadFileToolbarAction = new DownloadFileToolbarAction(); getToolBarManager().add(downloadFileToolbarAction); addFileToolbarAction = new AddFileToolbarAction(); getToolBarManager().add(addFileToolbarAction); removeFileToolbarAction = new RemoveFileToolbarAction(); getToolBarManager().add(removeFileToolbarAction); // Create the TableComposite. Since 05.06.2009. issueFileAttachmentTable = new IssueFileAttachmentTable(getClient()); issueFileAttachmentTable.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { handleSectionButtons(); } }); issueFileAttachmentTable.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { editFileToolbarAction.run(); } }); // Place the TableComposite in this Section, ah, nicely? new IssueFileAttachmentTableComposite(getClient(), SWT.NONE, issueFileAttachmentTable); hookContextMenu(); // <-- FIXME This is not functioning? Kai updateToolBarManager(); } /* (non-Javadoc) * @see org.nightlabs.jfire.issuetracking.ui.issue.editor.AbstractIssueEditorGeneralSection#doSetIssue(org.nightlabs.jfire.issue.Issue) */ @Override protected void doSetIssue(Issue issue) { Collection<IssueFileAttachment> issueFileAttachments = issue.getIssueFileAttachments(); boolean isFileAttachmentExists = issueFileAttachments != null && !issueFileAttachments.isEmpty(); if (isFileAttachmentExists) issueFileAttachmentTable.setInput( issueFileAttachments ); getSection().setExpanded( isFileAttachmentExists ); handleSectionButtons(); } /** * Based on the contents of the filenames in the list, control the 'enabled' states of the * toolbar buttons accordingly. */ protected void handleSectionButtons() { assert downloadFileToolbarAction != null && removeFileToolbarAction != null; boolean isItemSelected = issueFileAttachmentTable.getSelectionIndex() >= 0; downloadFileToolbarAction.setEnabled( isItemSelected ); removeFileToolbarAction.setEnabled( isItemSelected ); editFileToolbarAction.setEnabled( isItemSelected ); } /** * Saves a file indicated by fileName to the given {@link InputStream}. */ public void saveFile(InputStream io, String fileName) throws IOException { // Should this method be here? FileOutputStream fos = new FileOutputStream(fileName); try { byte[] buf = new byte[256]; int read = 0; while ((read = io.read(buf)) > 0) { fos.write(buf, 0, read); } } finally { fos.close(); } } private File downloadAttachment(final IssueFileAttachment issueFileAttachment) { if (issueFileAttachment == null) return null; boolean alreadyPersistent = JDOHelper.getObjectId(issueFileAttachment) != null; IssueFileAttachment iFA = issueFileAttachment; if (alreadyPersistent) { iFA = IssueFileAttachmentDAO.sharedInstance().getIssueFileAttachment( (IssueFileAttachmentID)JDOHelper.getObjectId(issueFileAttachment), new String[] {FetchPlan.DEFAULT, IssueFileAttachment.FETCH_GROUP_DATA}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new NullProgressMonitor() ); } InputStream inputStream = iFA.createFileAttachmentInputStream(); if (inputStream != null) { try { // tmpFile = File.createTempFile(iFA.getFileName(), ""); File tmpDir = IOUtil.getTempDir(); File file = new File(tmpDir, iFA.getFileName()); file.setWritable(true); FileOutputStream fos = new FileOutputStream(file); try { IOUtil.transferStreamData(inputStream, fos); } finally { inputStream.close(); fos.close(); } // saveFile(inputStream, file.getName()); return file; } catch (IOException e) { throw new RuntimeException(e); } } return null; } // -----------------------------------------------------------------------------------------------------------------------------------| // --->> FIXME On context menu: [These doesnt seem to be functioning? Kai] private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { IssueFileAttachmentSection.this.fillContextMenu(manager); } }); // Menu menu = menuMgr.createContextMenu(fileComposite.getFileListWidget()); // fileComposite.getFileListWidget().setMenu(menu); } private void fillContextMenu(IMenuManager manager) { manager.add(downloadFileToolbarAction); manager.add(addFileToolbarAction); manager.add(removeFileToolbarAction); } // -----------------------------------------------------------------------------------------------------------------------------------| // -----------------------------------------------------------------------------------------------------------------------------------| /** * Setup more control for the TableComposite in this Section. * This seems enough, and we dont have to have anything more elaborate(?) */ private class IssueFileAttachmentTableComposite extends XComposite { // This solution is a proposed improvement over the IssueFileAttachmentComposite, which previously, // in addition to the composite, encapsulates the ListComposite<IssueFileAttachment>. // Changes: // (1) From ListComposite<IssueFileAttachment> to AbstractTableComposite<IssueFileAttachment> // -- So that we can have multiple columns (at least 2 to display the fileName and the fileSize). // // (2) Once the UI is set up, we only need to use the reference to the TableComposite for displaying contents. // -- All 'actions' should be relegated to the corresponding 'Action' classes instead. public IssueFileAttachmentTableComposite(Composite parent, int style, IssueFileAttachmentTable issueFileAttachmentTable) { super(parent, style, LayoutMode.TIGHT_WRAPPER); getGridLayout().numColumns = 2; getGridLayout().makeColumnsEqualWidth = false; getGridData().grabExcessHorizontalSpace = true; GridData gridData = new GridData(GridData.FILL_BOTH); gridData.heightHint = 200; assert issueFileAttachmentTable != null; issueFileAttachmentTable.setLayoutData(gridData); } } // -----------------------------------------------------------------------------------------------------------------------------------| /** * Handles the action to save (or download to a local copy) of an {@link IssueFileAttachment}. */ public class DownloadFileToolbarAction extends Action { public DownloadFileToolbarAction() { setId(DownloadFileToolbarAction.class.getName()); setImageDescriptor(SharedImages.SAVE_16x16); setToolTipText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.DownloadFileToolbarAction.toolTipText")); //$NON-NLS-1$ setText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.DownloadFileToolbarAction.text")); //$NON-NLS-1$ } @Override public void run() { // Note 1: We should suggest the same filename as is listed in the CompositeTable for the download filename. // Note 2: There were several commented-out original codes in this method, prior to these newly written ones. // One can check out Revision #14707 from the SVN if one is interested. Kai Collection<IssueFileAttachment> items = issueFileAttachmentTable.getSelectedElements(); if (items == null || items.isEmpty()) return; // For now, we shall assume NO multiple selection from the CompositeTable; and that only ONE IssueFileAttachment // is available for download, per selection-action. final IssueFileAttachment issueFileAttachment = items.iterator().next(); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { IssueFileAttachmentID iFAID = (IssueFileAttachmentID)JDOHelper.getObjectId(issueFileAttachment); IssueFileAttachment iFA = issueFileAttachment; if (iFAID != null) { // The fileAttachment was saved before and thus might // have been detached without data, re-query it from the // server (or cache) iFA = IssueFileAttachmentDAO.sharedInstance().getIssueFileAttachment( (IssueFileAttachmentID)JDOHelper.getObjectId(issueFileAttachment), new String[] {FetchPlan.DEFAULT, IssueFileAttachment.FETCH_GROUP_DATA}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new NullProgressMonitor() ); } InputStream inputStream = iFA.createFileAttachmentInputStream(); if (inputStream != null) { try { FileDialog fileDialog = new FileDialog(RCPUtil.getActiveWorkbenchShell(), SWT.SAVE); fileDialog.setFileName( issueFileAttachment.getFileName() ); String selectedFile = fileDialog.open(); if (selectedFile != null) { saveFile(inputStream, selectedFile); } } catch (Exception ex) { throw new RuntimeException(ex); } } } }); } } // -----------------------------------------------------------------------------------------------------------------------------------| /** * Handles the action to add a file to the {@link IssueFileAttachment}. * TODO Check to see if the newly selected file already exists in the CompositeTable. */ public class AddFileToolbarAction extends Action { private Issue issue; public AddFileToolbarAction() { setId(AddFileToolbarAction.class.getName()); setImageDescriptor(SharedImages.ADD_16x16); setToolTipText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.AddFileToolbarAction.toolTipText")); //$NON-NLS-1$ setText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.AddFileToolbarAction.text")); //$NON-NLS-1$ } @Override public void run() { issue = getIssue(); FileDialog fileDialog = new FileDialog(RCPUtil.getActiveShell(), SWT.OPEN); final String selectedFile = fileDialog.open(); if (selectedFile != null) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { File file = new File(selectedFile); IssueFileAttachment issueFileAttachment = new IssueFileAttachment(issue.getOrganisationID(), IDGenerator.nextID(IssueFileAttachment.class), issue); try { issueFileAttachment.loadFile(file); } catch (IOException e) { throw new RuntimeException(e); } // Update the current Issue with the newly instantiated IssueFileAttachment. issue.addIssueFileAttachment(issueFileAttachment); // Note: We will be adding the newly instantiated IssueFileAttachment to the collection in the current Issue, // and so, we should expand the section if it was previously not expanded. if (issueFileAttachmentTable.getItemCount() == 0) { //(issue.getIssueFileAttachments().size() == 0) { issueFileAttachmentTable.setInput(issue.getIssueFileAttachments()); // <-- Only on first populate. getSection().setExpanded(true); } // Update the UI. issueFileAttachmentTable.refresh(true); markDirty(); } }); } } } // -----------------------------------------------------------------------------------------------------------------------------------| /** * Handles the action to remove a 'selected' file from the {@link IssueFileAttachment}. */ public class RemoveFileToolbarAction extends Action { public RemoveFileToolbarAction() { setId(RemoveFileToolbarAction.class.getName()); setImageDescriptor(SharedImages.DELETE_16x16); setToolTipText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.RemoveFileToolbarAction.toolTipText")); //$NON-NLS-1$ setText(Messages.getString("org.nightlabs.jfire.issuetracking.ui.issue.editor.IssueFileAttachmentSection.RemoveFileToolbarAction.text")); //$NON-NLS-1$ } @Override public void run() { Collection<IssueFileAttachment> items = issueFileAttachmentTable.getSelectedElements(); if (items == null || items.isEmpty()) return; Issue issue = getIssue(); for(IssueFileAttachment issueFileAttachment : items) { issueFileAttachmentTable.removeElement(issueFileAttachment); issue.removeIssueFileAttachment(issueFileAttachment); } // Done! markDirty(); } } public class EditFileToolbarAction extends Action { public EditFileToolbarAction() { setId(EditFileToolbarAction.class.getName()); setImageDescriptor(SharedImages.EDIT_16x16); setToolTipText("Open File"); setText("Open File"); } @Override public void run() { Collection<IssueFileAttachment> items = issueFileAttachmentTable.getSelectedElements(); if (items == null || items.isEmpty()) return; // For now, we shall assume NO multiple selection from the CompositeTable; and that only ONE IssueFileAttachment // is available for download, per selection-action. final IssueFileAttachment issueFileAttachment = items.iterator().next(); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { File file = downloadAttachment(issueFileAttachment); if (Desktop.isDesktopSupported()) { try { // Desktop.edit() sometimes fails with Error message: Falscher Parameter. Desktop.open() always worked for me. Daniel // Desktop.getDesktop().edit(file); Desktop.getDesktop().open(file); } catch (IOException e) { throw new RuntimeException(e); } } } }); } } }