/** * * Copyright * 2009-2015 Jayway Products AB * 2016-2017 Föreningen Sambruk * * Licensed under AGPL, Version 3.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.gnu.org/licenses/agpl.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package se.streamsource.streamflow.client.ui.workspace.cases; import org.jdesktop.application.Action; import org.jdesktop.application.Application; import org.jdesktop.application.ApplicationContext; import org.jdesktop.application.Task; import org.jdesktop.swingx.util.WindowUtils; import org.netbeans.api.wizard.WizardDisplayer; import org.netbeans.spi.wizard.Wizard; import org.netbeans.spi.wizard.WizardException; import org.netbeans.spi.wizard.WizardPage; import org.qi4j.api.injection.scope.Service; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.injection.scope.Uses; import org.qi4j.api.structure.Module; import org.qi4j.api.util.Iterables; import se.streamsource.dci.restlet.client.CommandQueryClient; import se.streamsource.dci.value.StringValue; import se.streamsource.dci.value.link.LinkValue; import se.streamsource.streamflow.api.workspace.cases.CaseOutputConfigDTO; import se.streamsource.streamflow.api.workspace.cases.general.FormDraftDTO; import se.streamsource.streamflow.api.workspace.cases.general.PageSubmissionDTO; import se.streamsource.streamflow.client.MacOsUIWrapper; import se.streamsource.streamflow.client.StreamflowApplication; import se.streamsource.streamflow.client.StreamflowResources; import se.streamsource.streamflow.client.ui.administration.AdministrationResources; import se.streamsource.streamflow.client.ui.workspace.WorkspaceResources; import se.streamsource.streamflow.client.ui.workspace.cases.general.forms.FormDraftModel; import se.streamsource.streamflow.client.ui.workspace.cases.general.forms.FormSubmissionWizardPageView; import se.streamsource.streamflow.client.util.CommandTask; import se.streamsource.streamflow.client.util.StreamflowButton; import se.streamsource.streamflow.client.util.dialog.ConfirmationDialog; import se.streamsource.streamflow.client.util.dialog.DialogService; import se.streamsource.streamflow.client.util.dialog.SelectLinkDialog; import se.streamsource.streamflow.client.util.i18n; import se.streamsource.streamflow.infrastructure.event.domain.TransactionDomainEvents; import se.streamsource.streamflow.infrastructure.event.domain.source.TransactionListener; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.io.File; import java.io.IOException; import java.util.*; import java.util.List; import static se.streamsource.streamflow.infrastructure.event.domain.source.helper.Events.*; /** * JAVADOC */ public class CaseActionsView extends JPanel implements TransactionListener, Observer { @Service DialogService dialogs; @Structure Module module; @Service StreamflowApplication main; private CaseModel model; private JPanel actionsPanel = new JPanel(); private ApplicationContext context; private enum CaseActionButtonTemplate { open, sendto, assign, assignto, unassign, onhold, resume, formondelete, reopen, delete, exportpdf, reinstate, restrict, unrestrict, markunread, markread, requirecasetype, resolve, formonclose, close } public CaseActionsView( @Service ApplicationContext context, @Uses CaseModel model ) { this.model = model; this.context = context; model.addObserver( this ); setLayout( new BorderLayout() ); setBorder( new EmptyBorder( 5, 5, 5, 5 ) ); actionsPanel.setLayout( new GridLayout( 0, 1, 5, 5 ) ); add( actionsPanel, BorderLayout.NORTH ); setActionMap( context.getActionMap( this ) ); MacOsUIWrapper.convertAccelerators( context.getActionMap( CaseActionsView.class, this ) ); } @Action(block = Task.BlockingScope.COMPONENT) public Task open() { return new CommandTask() { @Override public void command() throws Exception { model.open(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task assign() { return new CommandTask() { @Override public void command() throws Exception { model.assignToMe(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task assignto() { final SelectLinkDialog dialog = module.objectBuilderFactory().newObjectBuilder(SelectLinkDialog.class).use( model.getPossibleAssignTo() ).newInstance(); dialogs.showOkCancelHelpDialog( this, dialog, i18n.text( WorkspaceResources.choose_assignee_title ) ); if (dialog.getSelectedLink() != null) { return new CommandTask() { @Override public void command() throws Exception { model.assignTo( dialog.getSelectedLink() ); } }; } else return null; } @Action(block = Task.BlockingScope.COMPONENT) public Task close() { // TODO very odd hack - how to solve state binder update issue during use of accelerator keys. Component focusOwner = WindowUtils.findWindow( this ).getFocusOwner(); if (focusOwner != null) focusOwner.transferFocus(); return new CommandTask() { @Override public void command() throws Exception { model.close(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task resolve() { // TODO very odd hack - how to solve state binder update issue during use of accelerator keys. Component focusOwner = WindowUtils.findWindow( this ).getFocusOwner(); if (focusOwner != null) focusOwner.transferFocus(); final SelectLinkDialog dialog = module.objectBuilderFactory().newObjectBuilder( SelectLinkDialog.class ) .use( model.getPossibleResolutions() ).newInstance(); dialogs.showOkCancelHelpDialog( WindowUtils.findWindow( this ), dialog, i18n.text( AdministrationResources.resolve ) ); if (dialog.getSelectedLink() != null) { return new CommandTask() { @Override public void command() throws Exception { model.resolve( dialog.getSelectedLink() ); } }; } else return null; } @Action(block = Task.BlockingScope.COMPONENT) public Task formonclose() { // TODO very odd hack - how to solve state binder update issue during use of accelerator keys. Component focusOwner = WindowUtils.findWindow( this ).getFocusOwner(); if (focusOwner != null) focusOwner.transferFocus(); CommandQueryClient formOnCloseClient = model.getClient().getClient( "submitformonclose/" ); formOnCloseClient.postCommand( "create" ); LinkValue formDraftLink = formOnCloseClient.query( "formdraft", LinkValue.class ); if( formWizard( formDraftLink ) ) { return new CommandTask() { @Override protected void command() throws Exception { model.formOnClose(); } }; } else return null; } @Action(block = Task.BlockingScope.COMPONENT) public Task formondelete() { // TODO very odd hack - how to solve state binder update issue during use of accelerator keys. Component focusOwner = WindowUtils.findWindow( this ).getFocusOwner(); if (focusOwner != null) focusOwner.transferFocus(); StringValue name = model.getClient().query( "formondeletename", StringValue.class ); ConfirmationDialog dialog = module.objectBuilderFactory().newObject(ConfirmationDialog.class); dialog.setCustomMessage( i18n.text( WorkspaceResources.formondelete_confirmation, name.string().get() )); dialogs.showOkCancelHelpDialog( this, dialog, i18n.text( StreamflowResources.confirmation ) ); if (dialog.isConfirmed()) { CommandQueryClient formOnRemoveClient = model.getClient().getClient( "submitformondelete/" ); formOnRemoveClient.postCommand( "create" ); LinkValue formDraftLink = formOnRemoveClient.query( "formdraft", LinkValue.class ); if( formWizard( formDraftLink ) ) { return new CommandTask() { @Override protected void command() throws Exception { model.formOnRemove(); } }; } else return null; } else { return null; } } @Action(block = Task.BlockingScope.COMPONENT) public Task delete() { ConfirmationDialog dialog = module.objectBuilderFactory().newObject(ConfirmationDialog.class); dialog.setRemovalMessage( i18n.text( WorkspaceResources.caze ) ); dialogs.showOkCancelHelpDialog( this, dialog, i18n.text( StreamflowResources.confirmation ) ); if (dialog.isConfirmed()) { return new CommandTask() { @Override public void command() throws Exception { model.delete(); } }; } else return null; } @Action(block = Task.BlockingScope.COMPONENT) public Task sendto() { final SelectLinkDialog dialog = module.objectBuilderFactory().newObjectBuilder(SelectLinkDialog.class).use( model.getPossibleSendTo() ).newInstance(); dialogs.showOkCancelHelpDialog( this, dialog, i18n.text( WorkspaceResources.choose_owner_title ) ); if (dialog.getSelectedLink() != null) { return new CommandTask() { @Override public void command() throws Exception { model.sendTo( dialog.getSelectedLink() ); } }; } else return null; } @Action(block = Task.BlockingScope.COMPONENT) public Task onhold( ActionEvent event ) { return new CommandTask() { @Override public void command() throws Exception { model.onHold(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task reopen() { return new CommandTask() { @Override public void command() throws Exception { model.reopen(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task resume() { return new CommandTask() { @Override public void command() throws Exception { model.resume(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task unassign() { return new CommandTask() { @Override public void command() throws Exception { model.unassign(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public void exportpdf() { PdfPrintingDialog dialog = module.objectBuilderFactory().newObjectBuilder( PdfPrintingDialog.class ).use( context ).newInstance(); dialogs.showOkCancelHelpDialog( (StreamflowButton)getActionMap().get( "exportpdf" ).getValue( "sourceButton" ), dialog, i18n.text( WorkspaceResources.printing_configuration ), DialogService.Orientation.right ); if( dialog.getCaseOutputConfig() != null ) new PrintCaseTask( dialog.getCaseOutputConfig() ).execute(); } @Action(block = Task.BlockingScope.COMPONENT) public Task reinstate() { ConfirmationDialog dialog = module.objectBuilderFactory().newObject(ConfirmationDialog.class); dialog.setCustomMessage( i18n.text( WorkspaceResources.caze_reinstate ) ); dialogs.showOkCancelHelpDialog( this, dialog, i18n.text( StreamflowResources.confirmation ) ); if (dialog.isConfirmed()) { return new CommandTask() { @Override public void command() throws Exception { model.reinstate(); } }; } else return null; } @Action(block = Task.BlockingScope.COMPONENT ) public Task restrict() { return new CommandTask() { @Override protected void command() throws Exception { model.restrict(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task unrestrict() { ConfirmationDialog dialog = module.objectBuilderFactory().newObject(ConfirmationDialog.class); dialog.setCustomMessage( i18n.text( WorkspaceResources.unrestrict_case ) ); dialogs.showOkCancelHelpDialog( this, dialog, i18n.text( StreamflowResources.confirmation ) ); if (dialog.isConfirmed()) { return new CommandTask() { @Override protected void command() throws Exception { model.unrestrict(); } }; } else { return null; } } @Action(block = Task.BlockingScope.COMPONENT) public Task markunread( ActionEvent event ) { return new CommandTask() { @Override public void command() throws Exception { model.markunread(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task markread( ActionEvent event ) { return new CommandTask() { @Override public void command() throws Exception { model.markread(); } }; } @Action(block = Task.BlockingScope.COMPONENT) public Task requirecasetype(){ ConfirmationDialog dialog = module.objectBuilderFactory().newObject(ConfirmationDialog.class); dialog.setCustomMessage( i18n.text( WorkspaceResources.caze_requirescasetype ) ); dialogs.showOkCancelHelpDialog( this, dialog, i18n.text( StreamflowResources.confirmation ) ); if (dialog.isConfirmed()) { return new CommandTask() { @Override public void command() throws Exception { model.requirecasetype(); } }; } else return null; } public void notifyTransactions( Iterable<TransactionDomainEvents> transactions ) { if (matches( withUsecases( "sendto", "open", "assign", "close", "onhold", "reopen", "resume", "unassign", "resolved", "formonclose", "formondelete", "reinstate", "restrict", "unrestrict", "read" ), transactions ) || matches( withNames("changedCaseType"), transactions )) { model.refresh(); } } public void update( Observable o, Object arg ) { // Update list of action buttons actionsPanel.removeAll(); ActionMap am = getActionMap(); for (CaseActionButtonTemplate buttonOrder : CaseActionButtonTemplate.values()) { for (LinkValue commandLink : Iterables.flatten( model.getCommands(), model.getQueries() )) { if (buttonOrder.toString().equals( commandLink.rel().get() )) { javax.swing.Action action1 = am.get( commandLink.rel().get() ); if (action1 != null) { StreamflowButton button = new StreamflowButton( action1 ); button.registerKeyboardAction( action1, (KeyStroke) action1 .getValue( javax.swing.Action.ACCELERATOR_KEY ), JComponent.WHEN_IN_FOCUSED_WINDOW ); button.setHorizontalAlignment( SwingConstants.LEFT ); actionsPanel.add( button ); action1.putValue( "sourceButton", button ); // NotificationGlassPane.registerButton(button); } } } } revalidate(); repaint(); } private boolean formWizard( LinkValue formDraftLink ) { // get the form submission value; final CommandQueryClient formDraftClient = model.getClient().getClient( formDraftLink ); final FormDraftModel formDraftModel = module.objectBuilderFactory().newObjectBuilder(FormDraftModel.class).use(formDraftClient).newInstance(); FormDraftDTO formDraftDTO = (FormDraftDTO) formDraftModel.getFormDraftDTO().buildWith().prototype(); final WizardPage[] wizardPages = new WizardPage[ formDraftDTO.pages().get().size() ]; for (int i = 0; i < formDraftDTO.pages().get().size(); i++) { PageSubmissionDTO page = formDraftDTO.pages().get().get( i ); if ( page.fields().get() != null && page.fields().get().size() >0 ) { wizardPages[i] = module.objectBuilderFactory().newObjectBuilder(FormSubmissionWizardPageView.class). use( formDraftModel, page ).newInstance(); } } Map initialProperties = new HashMap( ); Wizard wizard = WizardPage.createWizard( formDraftDTO.description().get(), wizardPages, new WizardPage.WizardResultProducer() { public Object finish( Map map ) throws WizardException { // Force focus move before submit Component focusOwner = WindowUtils.findWindow( wizardPages[ wizardPages.length - 1 ] ).getFocusOwner(); if (focusOwner != null) { focusOwner.transferFocus(); formDraftModel.submit(); map.put( "success", true ); } return map; } public boolean cancel( Map map ) { formDraftModel.delete(); return true; } } ); Point onScreen = main.getMainFrame().getLocationOnScreen(); Map result = (Map)WizardDisplayer.showWizard( wizard, new Rectangle( onScreen, new Dimension( 800, 600 ) ), null, initialProperties ); return (result == null ||result.get( "success" ) == null) ? false : (Boolean)result.get( "success" ); } private class PrintCaseTask extends Task<File, Void> { private CaseOutputConfigDTO config; public PrintCaseTask( CaseOutputConfigDTO config ) { super( Application.getInstance() ); this.config = config; setUserCanCancel( false ); } @Override protected File doInBackground() throws Exception { setMessage( getResourceMap().getString( "description" ) ); File file = model.export(config); return file; } @Override protected void succeeded( File file ) { // Open file Desktop desktop = Desktop.getDesktop(); try { edit(file); } catch (Exception e) { try { open(file); } catch (Exception e1) { dialogs.showMessageDialog( CaseActionsView.this, i18n.text( WorkspaceResources.could_not_print ), "" ); } } } } public boolean open(File file) { if (openSystemSpecific(file.getPath())) return true; if (openDESKTOP(file)) return true; return false; } public boolean edit(File file) { // you can try something like // runCommand("gimp", "%s", file.getPath()) // based on user preferences. if (openSystemSpecific(file.getPath())) return true; if (editDESKTOP(file)) return true; return false; } private boolean openSystemSpecific(String what) { EnumOS os = getOs(); if (os.isLinux()) { if (runCommand("kde-open", "%s", what)) return true; if (runCommand("gnome-open", "%s", what)) return true; if (runCommand("xdg-open", "%s", what)) return true; } if (os.isMac()) { if (runCommand("open", "%s", what)) return true; } if (os.isWindows()) { if (runCommand("explorer", "%s", what)) return true; } return false; } private boolean openDESKTOP(File file) { logOut("Trying to use Desktop.getDesktop().open() with " + file.toString()); try { if (!Desktop.isDesktopSupported()) { logErr("Platform is not supported."); return false; } if (!Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) { logErr("OPEN is not supported."); return false; } Desktop.getDesktop().open(file); return true; } catch (Throwable t) { logErr("Error using desktop open.", t); return false; } } private boolean editDESKTOP(File file) { logOut("Trying to use Desktop.getDesktop().edit() with " + file); try { if (!Desktop.isDesktopSupported()) { logErr("Platform is not supported."); return false; } if (!Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) { logErr("EDIT is not supported."); return false; } Desktop.getDesktop().edit(file); return true; } catch (Throwable t) { logErr("Error using desktop edit.", t); return false; } } private boolean runCommand(String command, String args, String file) { logOut("Trying to exec:\n cmd = " + command + "\n args = " + args + "\n %s = " + file); String[] parts = prepareCommand(command, args, file); try { Process p = Runtime.getRuntime().exec(parts); if (p == null) return false; try { int retval = p.exitValue(); if (retval == 0) { logErr("Process ended immediately."); return false; } else { logErr("Process crashed."); return false; } } catch (IllegalThreadStateException itse) { logErr("Process is running."); return true; } } catch (IOException e) { logErr("Error running command.", e); return false; } } private String[] prepareCommand(String command, String args, String file) { List<String> parts = new ArrayList<String>(); parts.add(command); if (args != null) { for (String s : args.split(" ")) { s = String.format(s, file); // put in the filename thing parts.add(s.trim()); } } return parts.toArray(new String[parts.size()]); } private static void logErr(String msg, Throwable t) { System.err.println(msg); t.printStackTrace(); } private static void logErr(String msg) { System.err.println(msg); } private static void logOut(String msg) { System.out.println(msg); } public static enum EnumOS { linux, macos, solaris, unknown, windows; public boolean isLinux() { return this == linux || this == solaris; } public boolean isMac() { return this == macos; } public boolean isWindows() { return this == windows; } } public static EnumOS getOs() { String s = System.getProperty("os.name").toLowerCase(); if (s.contains("win")) { return EnumOS.windows; } if (s.contains("mac")) { return EnumOS.macos; } if (s.contains("solaris")) { return EnumOS.solaris; } if (s.contains("sunos")) { return EnumOS.solaris; } if (s.contains("linux")) { return EnumOS.linux; } if (s.contains("unix")) { return EnumOS.linux; } else { return EnumOS.unknown; } } }