/**
*
* 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.note;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import org.jdesktop.application.Action;
import org.jdesktop.application.ApplicationContext;
import org.jdesktop.application.Task;
import org.jdesktop.swingx.JXPanel;
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.object.ObjectBuilderFactory;
import org.qi4j.api.structure.Module;
import se.streamsource.streamflow.api.workspace.cases.general.NoteDTO;
import se.streamsource.streamflow.client.ui.DateFormats;
import se.streamsource.streamflow.client.ui.workspace.WorkspaceResources;
import se.streamsource.streamflow.client.util.ActionBinder;
import se.streamsource.streamflow.client.util.CommandTask;
import se.streamsource.streamflow.client.util.RefreshComponents;
import se.streamsource.streamflow.client.util.RefreshWhenShowing;
import se.streamsource.streamflow.client.util.Refreshable;
import se.streamsource.streamflow.client.util.StreamflowToggleButton;
import se.streamsource.streamflow.client.util.ValueBinder;
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 se.streamsource.streamflow.util.Translator;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.util.Locale;
import static se.streamsource.streamflow.infrastructure.event.domain.source.helper.Events.*;
/**
* A view representing all general notes present on a case
*/
public class CaseNoteView
extends JPanel implements TransactionListener, Refreshable
{
private ValueBinder valueBinder;
private ActionBinder actionBinder;
private CaseNoteModel model;
private JTextPane note;
private JDialog popup;
StreamflowToggleButton allNotesBtn;
public CaseNoteView( @Service ApplicationContext appContext,
@Uses CaseNoteModel model,
@Structure Module module )
{
this.model = model;
setActionMap( appContext.getActionMap( this ) );
ActionMap am = getActionMap();
ObjectBuilderFactory obf = module.objectBuilderFactory();
FormLayout formLayout = new FormLayout( "70dlu, 3dlu, pref, 3dlu, right:70dlu:grow","pref,120:grow" );
this.setLayout( formLayout );
DefaultFormBuilder formBuilder = new DefaultFormBuilder( formLayout, this );
valueBinder = obf.newObject( ValueBinder.class );
actionBinder = obf.newObjectBuilder( ActionBinder.class ).use( am ).newInstance();
actionBinder.setResourceMap( appContext.getResourceMap( this.getClass() ) );
formBuilder.append( i18n.text( WorkspaceResources.note_label ));
allNotesBtn = new StreamflowToggleButton( am.get( "allNotes" ) );
allNotesBtn.addItemListener( new ItemListener()
{
public void itemStateChanged(ItemEvent itemEvent)
{
int state = itemEvent.getStateChange();
if (state == ItemEvent.SELECTED)
{
showPopup( allNotesBtn );
} else if (state == ItemEvent.DESELECTED)
{
killPopup();
}
}
});
formBuilder.nextColumn(2);
formBuilder.append( allNotesBtn );
JScrollPane textScroll = null;
formBuilder.add( textScroll = new JScrollPane( valueBinder.bind( "note", actionBinder.bind( "addNote", note = new JTextPane() ) ) ),
new CellConstraints( 1, 2, 5, 1, CellConstraints.FILL, CellConstraints.FILL ) );
textScroll.setPreferredSize( new Dimension( 210, 120 ) );
textScroll.getViewport().getView().setFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null );
textScroll.getViewport().getView().setFocusTraversalKeys( KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null );
RefreshComponents refreshComponents = new RefreshComponents();
model.addObserver( refreshComponents );
refreshComponents.enabledOn( "addnote", note );
new RefreshWhenShowing( this, this );
}
@Action(block = Task.BlockingScope.COMPONENT)
public Task addNote( final ActionEvent event )
{
return new CommandTask()
{
@Override
public void command()
throws Exception
{
model.addNote( ((JTextPane) event.getSource()).getText() );
}
};
}
@Action(block = Task.BlockingScope.COMPONENT)
public void allNotes( final ActionEvent event )
{
}
public void refresh()
{
model.refresh();
if(Translator.HTML.equalsIgnoreCase( model.getNote().contentType().get() ) )
{
note.setContentType( Translator.HTML );
note.getDocument().putProperty( "IgnoreCharsetDirective", Boolean.TRUE );
note.setEditable( true );
} else
{
note.setContentType( Translator.PLAIN );
note.setEditable( true );
}
valueBinder.update( model.getNote() );
}
public void notifyTransactions( Iterable<TransactionDomainEvents> transactions )
{
if (matches( withNames( "addedNote", "changedStatus" ), transactions ))
{
refresh();
}
}
private void showPopup( JComponent component)
{
JXPanel panel = new JXPanel();
panel.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
JScrollPane scroll = new JScrollPane( );
panel.add( scroll );
JList notes = new JList( );
scroll.setViewportView( notes );
notes.addListSelectionListener( new ListSelectionListener()
{
public void valueChanged( ListSelectionEvent e )
{
if(!e.getValueIsAdjusting())
{
NoteDTO selectedNote = (NoteDTO)((JList)e.getSource()).getSelectedValue();
if( !selectedNote.createdOn().get().equals( model.getNote().createdOn().get() ) )
{
note.setEnabled( false );
} else
{
note.setEnabled( model.checkNoteEnabled() );
}
note.setText( selectedNote.note().get() );
model.setSelectedNoteIndex(e.getLastIndex());
}
}
} );
notes.addFocusListener( new FocusAdapter()
{
@Override
public void focusLost( FocusEvent e )
{
if( !(e.getOppositeComponent() instanceof StreamflowToggleButton) )
allNotesBtn.doClick();
else if( (e.getOppositeComponent() instanceof StreamflowToggleButton) )
killPopup();
}
} );
notes.setModel( model.getNotes() );
notes.setSelectedIndex( model.getSelectedNoteIndex() );
notes.setCellRenderer( new DefaultListCellRenderer()
{
public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus )
{
NoteDTO note = (NoteDTO) value;
JPanel itemPanel = new JPanel();
itemPanel.setLayout( new BoxLayout( itemPanel, BoxLayout.X_AXIS ) );
JLabel date = new JLabel(DateFormats.getProgressiveDateTimeValue( note.createdOn().get(),
Locale.getDefault() ) + " ", JLabel.LEFT );
JLabel name = new JLabel( note.creator().get(), JLabel.RIGHT);
itemPanel.add( date, Box.LEFT_ALIGNMENT);
itemPanel.add( Box.createHorizontalGlue() );
itemPanel.add( name, Box.RIGHT_ALIGNMENT );
itemPanel.setBorder(BorderFactory.createEmptyBorder(1,3,1,3));
if(isSelected)
{
itemPanel.setBackground( list.getSelectionBackground() );
date.setForeground( list.getSelectionForeground() );
name.setForeground( list.getSelectionForeground() );
} else
{
itemPanel.setBackground( list.getBackground() );
date.setForeground( list.getForeground() );
name.setForeground( list.getForeground() );
}
return itemPanel;
}
} );
final JFrame frame = (JFrame) SwingUtilities.getAncestorOfClass( JFrame.class, CaseNoteView.this );
popup = new JDialog( frame );
popup.getRootPane().registerKeyboardAction( new ActionListener()
{
public void actionPerformed( ActionEvent e )
{
killPopup();
}
}, KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 ), JComponent.WHEN_IN_FOCUSED_WINDOW);
popup.setUndecorated( true );
popup.setModal( false );
popup.setLayout( new BorderLayout() );
popup.add( panel, BorderLayout.CENTER );
Point location = component.getLocationOnScreen();
int diff = (int) panel.getPreferredSize().getWidth() - component.getWidth();
popup.setBounds( (int) location.getX() - diff , (int) location.getY() + component.getHeight(),
(int) panel.getPreferredSize().getWidth(), (int) panel.getPreferredSize().getHeight() );
popup.pack();
popup.setVisible( true );
frame.addComponentListener( new ComponentAdapter()
{
@Override
public void componentMoved( ComponentEvent e )
{
if (popup != null)
{
killPopup();
frame.removeComponentListener( this );
}
}
} );
}
private void killPopup()
{
if( popup != null )
{
popup.setVisible(false);
popup.dispose();
popup = null;
}
}
}