r57 - in trunk/tutti-ui-swing/src/main: java/fr/ifremer/tutti/ui/swing/content/operation/species java/fr/ifremer/tutti/ui/swing/util java/fr/ifremer/tutti/ui/swing/util/editor resources/i18n
Author: kmorin Date: 2012-12-13 18:13:46 +0100 (Thu, 13 Dec 2012) New Revision: 57 Url: http://forge.codelutin.com/projects/tutti/repository/revisions/57 Log: create comment editor Added: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentMover.java trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentResizer.java trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextCellComponent.java trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.css trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.jaxx trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUIHandler.java Modified: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUI.jaxx trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUIHandler.java trunk/tutti-ui-swing/src/main/resources/i18n/tutti-ui-swing_fr_FR.properties Modified: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUI.jaxx =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUI.jaxx 2012-12-13 15:37:44 UTC (rev 56) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUI.jaxx 2012-12-13 17:13:46 UTC (rev 57) @@ -30,6 +30,7 @@ fr.ifremer.tutti.ui.swing.TuttiUIContext fr.ifremer.tutti.ui.swing.content.operation.FishingOperationsUI fr.ifremer.tutti.ui.swing.content.operation.species.SpeciesFrequencyUI + fr.ifremer.tutti.ui.swing.util.editor.LongTextEditorUI org.jdesktop.swingx.JXTable @@ -76,6 +77,8 @@ </BeanValidator> <SpeciesFrequencyUI id='frequencyUI' constructorParams='handler.getContext()'/> + + <LongTextEditorUI id='longTextEditorUI'/> <Table id='form' fill='both' constraints='BorderLayout.NORTH'> Modified: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUIHandler.java =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUIHandler.java 2012-12-13 15:37:44 UTC (rev 56) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesBatchUIHandler.java 2012-12-13 17:13:46 UTC (rev 57) @@ -41,6 +41,7 @@ import fr.ifremer.tutti.ui.swing.content.operation.FishingOperationsUI; import fr.ifremer.tutti.ui.swing.util.TuttiBeanMonitor; import fr.ifremer.tutti.ui.swing.util.TuttiUIUtil; +import fr.ifremer.tutti.ui.swing.util.editor.LongTextCellComponent; import fr.ifremer.tutti.ui.swing.util.table.AbstractTuttiTableUIHandler; import fr.ifremer.tutti.ui.swing.util.table.TableRowModificationListener; import jaxx.runtime.SwingUtil; @@ -299,6 +300,8 @@ { // Comment column addColumnToModel(columnModel, + LongTextCellComponent.newEditor(ui.getLongTextEditorUI()), + LongTextCellComponent.newRender(), SpeciesBatchTableModel.COMMENT); } Added: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentMover.java =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentMover.java (rev 0) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentMover.java 2012-12-13 17:13:46 UTC (rev 57) @@ -0,0 +1,375 @@ +package fr.ifremer.tutti.ui.swing.util; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +/** + * This class allows you to move a Component by using a mouse. The Component + * moved can be a high level Window (ie. Window, Frame, Dialog) in which case + * the Window is moved within the desktop. Or the Component can belong to a + * Container in which case the Component is moved within the Container. + * + * When moving a Window, the listener can be added to a child Component of + * the Window. In this case attempting to move the child will result in the + * Window moving. For example, you might create a custom "Title Bar" for an + * undecorated Window and moving of the Window is accomplished by moving the + * title bar only. Multiple components can be registered as "window movers". + * + * Components can be registered when the class is created. Additional + * components can be added at any time using the registerComponent() method. + */ +public class ComponentMover extends MouseAdapter +{ + private Insets dragInsets = new Insets(0, 0, 0, 0); + private Dimension snapSize = new Dimension(1, 1); + private Insets edgeInsets = new Insets(0, 0, 0, 0); + private boolean changeCursor = true; + private boolean autoLayout = false; + + private Class destinationClass; + private Component destinationComponent; + private Component destination; + private Component source; + + private Point pressed; + private Point location; + + private Cursor originalCursor; + private boolean autoscrolls; + private boolean potentialDrag; + + + /** + * Constructor for moving individual components. The components must be + * regisetered using the registerComponent() method. + */ + public ComponentMover() + { + } + + /** + * Constructor to specify a Class of Component that will be moved when + * drag events are generated on a registered child component. The events + * will be passed to the first ancestor of this specified class. + * + * @param destinationClass the Class of the ancestor component + * @param component the Components to be registered for forwarding + * drag events to the ancestor Component. + */ + public ComponentMover(Class destinationClass, Component... components) + { + this.destinationClass = destinationClass; + registerComponent( components ); + } + + /** + * Constructor to specify a parent component that will be moved when drag + * events are generated on a registered child component. + * + * @param destinationComponent the component drage events should be forwareded to + * @param components the Components to be registered for forwarding drag + * events to the parent component to be moved + */ + public ComponentMover(Component destinationComponent, Component... components) + { + this.destinationComponent = destinationComponent; + registerComponent( components ); + } + + /** + * Get the auto layout property + * + * @return the auto layout property + */ + public boolean isAutoLayout() + { + return autoLayout; + } + + /** + * Set the auto layout property + * + * @param autoLayout when true layout will be invoked on the parent container + */ + public void setAutoLayout(boolean autoLayout) + { + this.autoLayout = autoLayout; + } + + /** + * Get the change cursor property + * + * @return the change cursor property + */ + public boolean isChangeCursor() + { + return changeCursor; + } + + /** + * Set the change cursor property + * + * @param changeCursor when true the cursor will be changed to the + * Cursor.MOVE_CURSOR while the mouse is pressed + */ + public void setChangeCursor(boolean changeCursor) + { + this.changeCursor = changeCursor; + } + + /** + * Get the drag insets + * + * @return the drag insets + */ + public Insets getDragInsets() + { + return dragInsets; + } + + /** + * Set the drag insets. The insets specify an area where mouseDragged + * events should be ignored and therefore the component will not be moved. + * This will prevent these events from being confused with a + * MouseMotionListener that supports component resizing. + * + * @param dragInsets + */ + public void setDragInsets(Insets dragInsets) + { + this.dragInsets = dragInsets; + } + + /** + * Get the bounds insets + * + * @return the bounds insets + */ + public Insets getEdgeInsets() + { + return edgeInsets; + } + + /** + * Set the edge insets. The insets specify how close to each edge of the parent + * component that the child component can be moved. Positive values means the + * component must be contained within the parent. Negative values means the + * component can be moved outside the parent. + * + * @param edgeInsets + */ + public void setEdgeInsets(Insets edgeInsets) + { + this.edgeInsets = edgeInsets; + } + + /** + * Remove listeners from the specified component + * + * @param component the component the listeners are removed from + */ + public void deregisterComponent(Component... components) + { + for (Component component : components) + component.removeMouseListener( this ); + } + + /** + * Add the required listeners to the specified component + * + * @param component the component the listeners are added to + */ + public void registerComponent(Component... components) + { + for (Component component : components) + component.addMouseListener( this ); + } + + /** + * Get the snap size + * + * @return the snap size + */ + public Dimension getSnapSize() + { + return snapSize; + } + + /** + * Set the snap size. Forces the component to be snapped to + * the closest grid position. Snapping will occur when the mouse is + * dragged half way. + */ + public void setSnapSize(Dimension snapSize) + { + if (snapSize.width < 1 + || snapSize.height < 1) + throw new IllegalArgumentException("Snap sizes must be greater than 0"); + + this.snapSize = snapSize; + } + + /** + * Setup the variables used to control the moving of the component: + * + * source - the source component of the mouse event + * destination - the component that will ultimately be moved + * pressed - the Point where the mouse was pressed in the destination + * component coordinates. + */ + @Override + public void mousePressed(MouseEvent e) + { + source = e.getComponent(); + int width = source.getSize().width - dragInsets.left - dragInsets.right; + int height = source.getSize().height - dragInsets.top - dragInsets.bottom; + Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width, height); + + if (r.contains(e.getPoint())) + setupForDragging(e); + } + + private void setupForDragging(MouseEvent e) + { + source.addMouseMotionListener( this ); + potentialDrag = true; + + // Determine the component that will ultimately be moved + + if (destinationComponent != null) + { + destination = destinationComponent; + } + else if (destinationClass == null) + { + destination = source; + } + else // forward events to destination component + { + destination = SwingUtilities.getAncestorOfClass(destinationClass, source); + } + + pressed = e.getLocationOnScreen(); + location = destination.getLocation(); + + if (changeCursor) + { + originalCursor = source.getCursor(); + source.setCursor( Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR) ); + } + + // Making sure autoscrolls is false will allow for smoother dragging of + // individual components + + if (destination instanceof JComponent) + { + JComponent jc = (JComponent)destination; + autoscrolls = jc.getAutoscrolls(); + jc.setAutoscrolls( false ); + } + } + + /** + * Move the component to its new location. The dragged Point must be in + * the destination coordinates. + */ + @Override + public void mouseDragged(MouseEvent e) + { + Point dragged = e.getLocationOnScreen(); + int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width); + int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height); + + int locationX = location.x + dragX; + int locationY = location.y + dragY; + + // Mouse dragged events are not generated for every pixel the mouse + // is moved. Adjust the location to make sure we are still on a + // snap value. + + while (locationX < edgeInsets.left) + locationX += snapSize.width; + + while (locationY < edgeInsets.top) + locationY += snapSize.height; + + Dimension d = getBoundingSize( destination ); + + while (locationX + destination.getSize().width + edgeInsets.right > d.width) + locationX -= snapSize.width; + + while (locationY + destination.getSize().height + edgeInsets.bottom > d.height) + locationY -= snapSize.height; + + // Adjustments are finished, move the component + + destination.setLocation(locationX, locationY); + } + + /* + * Determine how far the mouse has moved from where dragging started + * (Assume drag direction is down and right for positive drag distance) + */ + private int getDragDistance(int larger, int smaller, int snapSize) + { + int halfway = snapSize / 2; + int drag = larger - smaller; + drag += (drag < 0) ? -halfway : halfway; + drag = (drag / snapSize) * snapSize; + + return drag; + } + + /* + * Get the bounds of the parent of the dragged component. + */ + private Dimension getBoundingSize(Component source) + { + if (source instanceof Window) + { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Rectangle bounds = env.getMaximumWindowBounds(); + return new Dimension(bounds.width, bounds.height); + } + else + { + return source.getParent().getSize(); + } + } + + /** + * Restore the original state of the Component + */ + @Override + public void mouseReleased(MouseEvent e) + { + if (!potentialDrag) return; + + source.removeMouseMotionListener( this ); + potentialDrag = false; + + if (changeCursor) + source.setCursor( originalCursor ); + + if (destination instanceof JComponent) + { + ((JComponent)destination).setAutoscrolls( autoscrolls ); + } + + // Layout the components on the parent container + + if (autoLayout) + { + if (destination instanceof JComponent) + { + ((JComponent)destination).revalidate(); + } + else + { + destination.validate(); + } + } + } +} Added: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentResizer.java =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentResizer.java (rev 0) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/ComponentResizer.java 2012-12-13 17:13:46 UTC (rev 57) @@ -0,0 +1,458 @@ +package fr.ifremer.tutti.ui.swing.util; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; + +/** + * The ComponentResizer allows you to resize a component by dragging a border + * of the component. + */ +public class ComponentResizer extends MouseAdapter +{ + private final static Dimension MINIMUM_SIZE = new Dimension(10, 10); + private final static Dimension MAXIMUM_SIZE = + new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + + private static Map<Integer, Integer> cursors = new HashMap<Integer, Integer>(); + { + cursors.put(1, Cursor.N_RESIZE_CURSOR); + cursors.put(2, Cursor.W_RESIZE_CURSOR); + cursors.put(4, Cursor.S_RESIZE_CURSOR); + cursors.put(8, Cursor.E_RESIZE_CURSOR); + cursors.put(3, Cursor.NW_RESIZE_CURSOR); + cursors.put(9, Cursor.NE_RESIZE_CURSOR); + cursors.put(6, Cursor.SW_RESIZE_CURSOR); + cursors.put(12, Cursor.SE_RESIZE_CURSOR); + } + + private Insets dragInsets; + private Dimension snapSize; + + private int direction; + protected static final int NORTH = 1; + protected static final int WEST = 2; + protected static final int SOUTH = 4; + protected static final int EAST = 8; + + private Cursor sourceCursor; + private boolean resizing; + private Rectangle bounds; + private Point pressed; + private boolean autoscrolls; + + private Dimension minimumSize = MINIMUM_SIZE; + private Dimension maximumSize = MAXIMUM_SIZE; + + /** + * Convenience contructor. All borders are resizable in increments of + * a single pixel. Components must be registered separately. + */ + public ComponentResizer() + { + this(new Insets(5, 5, 5, 5), new Dimension(1, 1)); + } + + /** + * Convenience contructor. All borders are resizable in increments of + * a single pixel. Components can be registered when the class is created + * or they can be registered separately afterwards. + * + * @param components components to be automatically registered + */ + public ComponentResizer(Component... components) + { + this(new Insets(5, 5, 5, 5), new Dimension(1, 1), components); + } + + /** + * Convenience contructor. Eligible borders are resisable in increments of + * a single pixel. Components can be registered when the class is created + * or they can be registered separately afterwards. + * + * @param dragInsets Insets specifying which borders are eligible to be + * resized. + * @param components components to be automatically registered + */ + public ComponentResizer(Insets dragInsets, Component... components) + { + this(dragInsets, new Dimension(1, 1), components); + } + + /** + * Create a ComponentResizer. + * + * @param dragInsets Insets specifying which borders are eligible to be + * resized. + * @param snapSize Specify the dimension to which the border will snap to + * when being dragged. Snapping occurs at the halfway mark. + * @param components components to be automatically registered + */ + public ComponentResizer(Insets dragInsets, Dimension snapSize, Component... components) + { + setDragInsets( dragInsets ); + setSnapSize( snapSize ); + registerComponent( components ); + } + + /** + * Get the drag insets + * + * @return the drag insets + */ + public Insets getDragInsets() + { + return dragInsets; + } + + /** + * Set the drag dragInsets. The insets specify an area where mouseDragged + * events are recognized from the edge of the border inwards. A value of + * 0 for any size will imply that the border is not resizable. Otherwise + * the appropriate drag cursor will appear when the mouse is inside the + * resizable border area. + * + * @param dragInsets Insets to control which borders are resizeable. + */ + public void setDragInsets(Insets dragInsets) + { + validateMinimumAndInsets(minimumSize, dragInsets); + + this.dragInsets = dragInsets; + } + + /** + * Get the components maximum size. + * + * @return the maximum size + */ + public Dimension getMaximumSize() + { + return maximumSize; + } + + /** + * Specify the maximum size for the component. The component will still + * be constrained by the size of its parent. + * + * @param maximumSize the maximum size for a component. + */ + public void setMaximumSize(Dimension maximumSize) + { + this.maximumSize = maximumSize; + } + + /** + * Get the components minimum size. + * + * @return the minimum size + */ + public Dimension getMinimumSize() + { + return minimumSize; + } + + /** + * Specify the minimum size for the component. The minimum size is + * constrained by the drag insets. + * + * @param minimumSize the minimum size for a component. + */ + public void setMinimumSize(Dimension minimumSize) + { + validateMinimumAndInsets(minimumSize, dragInsets); + + this.minimumSize = minimumSize; + } + + /** + * Remove listeners from the specified component + * + * @param component the component the listeners are removed from + */ + public void deregisterComponent(Component... components) + { + for (Component component : components) + { + component.removeMouseListener( this ); + component.removeMouseMotionListener( this ); + } + } + + /** + * Add the required listeners to the specified component + * + * @param component the component the listeners are added to + */ + public void registerComponent(Component... components) + { + for (Component component : components) + { + component.addMouseListener( this ); + component.addMouseMotionListener( this ); + } + } + + /** + * Get the snap size. + * + * @return the snap size. + */ + public Dimension getSnapSize() + { + return snapSize; + } + + /** + * Control how many pixels a border must be dragged before the size of + * the component is changed. The border will snap to the size once + * dragging has passed the halfway mark. + * + * @param snapSize Dimension object allows you to separately spcify a + * horizontal and vertical snap size. + */ + public void setSnapSize(Dimension snapSize) + { + this.snapSize = snapSize; + } + + /** + * When the components minimum size is less than the drag insets then + * we can't determine which border should be resized so we need to + * prevent this from happening. + */ + private void validateMinimumAndInsets(Dimension minimum, Insets drag) + { + int minimumWidth = drag.left + drag.right; + int minimumHeight = drag.top + drag.bottom; + + if (minimum.width < minimumWidth + || minimum.height < minimumHeight) + { + String message = "Minimum size cannot be less than drag insets"; + throw new IllegalArgumentException( message ); + } + } + + /** + */ + @Override + public void mouseMoved(MouseEvent e) + { + Component source = e.getComponent(); + Point location = e.getPoint(); + direction = 0; + + if (location.x < dragInsets.left) + direction += WEST; + + if (location.x > source.getWidth() - dragInsets.right - 1) + direction += EAST; + + if (location.y < dragInsets.top) + direction += NORTH; + + if (location.y > source.getHeight() - dragInsets.bottom - 1) + direction += SOUTH; + + // Mouse is no longer over a resizable border + + if (direction == 0) + { + source.setCursor( sourceCursor ); + } + else // use the appropriate resizable cursor + { + int cursorType = cursors.get( direction ); + Cursor cursor = Cursor.getPredefinedCursor( cursorType ); + source.setCursor( cursor ); + } + } + + @Override + public void mouseEntered(MouseEvent e) + { + if (! resizing) + { + Component source = e.getComponent(); + sourceCursor = source.getCursor(); + } + } + + @Override + public void mouseExited(MouseEvent e) + { + if (! resizing) + { + Component source = e.getComponent(); + source.setCursor( sourceCursor ); + } + } + + @Override + public void mousePressed(MouseEvent e) + { + // The mouseMoved event continually updates this variable + + if (direction == 0) return; + + // Setup for resizing. All future dragging calculations are done based + // on the original bounds of the component and mouse pressed location. + + resizing = true; + + Component source = e.getComponent(); + pressed = e.getPoint(); + SwingUtilities.convertPointToScreen(pressed, source); + bounds = source.getBounds(); + + // Making sure autoscrolls is false will allow for smoother resizing + // of components + + if (source instanceof JComponent) + { + JComponent jc = (JComponent)source; + autoscrolls = jc.getAutoscrolls(); + jc.setAutoscrolls( false ); + } + } + + /** + * Restore the original state of the Component + */ + @Override + public void mouseReleased(MouseEvent e) + { + resizing = false; + + Component source = e.getComponent(); + source.setCursor( sourceCursor ); + + if (source instanceof JComponent) + { + ((JComponent)source).setAutoscrolls( autoscrolls ); + } + } + + /** + * Resize the component ensuring location and size is within the bounds + * of the parent container and that the size is within the minimum and + * maximum constraints. + * + * All calculations are done using the bounds of the component when the + * resizing started. + */ + @Override + public void mouseDragged(MouseEvent e) + { + if (resizing == false) return; + + Component source = e.getComponent(); + Point dragged = e.getPoint(); + SwingUtilities.convertPointToScreen(dragged, source); + + changeBounds(source, direction, bounds, pressed, dragged); + } + + protected void changeBounds(Component source, int direction, Rectangle bounds, Point pressed, Point current) + { + // Start with original locaton and size + + int x = bounds.x; + int y = bounds.y; + int width = bounds.width; + int height = bounds.height; + + // Resizing the West or North border affects the size and location + + if (WEST == (direction & WEST)) + { + int drag = getDragDistance(pressed.x, current.x, snapSize.width); + int maximum = Math.min(width + x, maximumSize.width); + drag = getDragBounded(drag, snapSize.width, width, minimumSize.width, maximum); + + x -= drag; + width += drag; + } + + if (NORTH == (direction & NORTH)) + { + int drag = getDragDistance(pressed.y, current.y, snapSize.height); + int maximum = Math.min(height + y, maximumSize.height); + drag = getDragBounded(drag, snapSize.height, height, minimumSize.height, maximum); + + y -= drag; + height += drag; + } + + // Resizing the East or South border only affects the size + + if (EAST == (direction & EAST)) + { + int drag = getDragDistance(current.x, pressed.x, snapSize.width); + Dimension boundingSize = getBoundingSize( source ); + int maximum = Math.min(boundingSize.width - x, maximumSize.width); + drag = getDragBounded(drag, snapSize.width, width, minimumSize.width, maximum); + width += drag; + } + + if (SOUTH == (direction & SOUTH)) + { + int drag = getDragDistance(current.y, pressed.y, snapSize.height); + Dimension boundingSize = getBoundingSize( source ); + int maximum = Math.min(boundingSize.height - y, maximumSize.height); + drag = getDragBounded(drag, snapSize.height, height, minimumSize.height, maximum); + height += drag; + } + + source.setBounds(x, y, width, height); + source.validate(); + } + + /* + * Determine how far the mouse has moved from where dragging started + */ + private int getDragDistance(int larger, int smaller, int snapSize) + { + int halfway = snapSize / 2; + int drag = larger - smaller; + drag += (drag < 0) ? -halfway : halfway; + drag = (drag / snapSize) * snapSize; + + return drag; + } + + /* + * Adjust the drag value to be within the minimum and maximum range. + */ + private int getDragBounded(int drag, int snapSize, int dimension, int minimum, int maximum) + { + while (dimension + drag < minimum) + drag += snapSize; + + while (dimension + drag > maximum) + drag -= snapSize; + + + return drag; + } + + /* + * Keep the size of the component within the bounds of its parent. + */ + private Dimension getBoundingSize(Component source) + { + if (source instanceof Window) + { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Rectangle bounds = env.getMaximumWindowBounds(); + return new Dimension(bounds.width, bounds.height); + } + else + { + return source.getParent().getSize(); + } + } +} Added: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextCellComponent.java =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextCellComponent.java (rev 0) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextCellComponent.java 2012-12-13 17:13:46 UTC (rev 57) @@ -0,0 +1,268 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package fr.ifremer.tutti.ui.swing.util.editor; + +import fr.ifremer.tutti.ui.swing.util.ComponentResizer; +import com.google.common.base.Preconditions; +import fr.ifremer.tutti.ui.swing.AbstractTuttiBeanUIModel; +import fr.ifremer.tutti.ui.swing.util.ComponentMover; +import fr.ifremer.tutti.ui.swing.util.TuttiUIUtil; +import fr.ifremer.tutti.ui.swing.util.table.AbstractSelectTableAction; +import fr.ifremer.tutti.ui.swing.util.table.AbstractTuttiTableModel; +import fr.ifremer.tutti.ui.swing.util.table.ColumnIdentifier; +import java.awt.event.WindowAdapter; +import java.awt.Color; +import java.awt.Component; +import java.awt.Frame; +import java.awt.Insets; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; +import javax.swing.AbstractCellEditor; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JTable; +import javax.swing.border.LineBorder; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import jaxx.runtime.JAXXUtil; +import jaxx.runtime.SwingUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import static org.nuiton.i18n.I18n._; + +/** + * Component to edit a cell in a popup with a text area + * + * @author kmorin + * @since 0.2 + */ +public class LongTextCellComponent extends JButton{ + + /** Logger. */ + private static final Log log = LogFactory.getLog(LongTextCellComponent.class); + + public LongTextCellComponent() { + setContentAreaFilled(false); + setOpaque(false); + setIcon(SwingUtil.createActionIcon("edit")); + } + + public static TableCellRenderer newRender() { + return new LongTextCellRenderer(); + } + + public static TableCellEditor newEditor(LongTextEditorUI ui) { + return new LongTextCellEditor(ui); + } + + + public static class LongTextCellEditor extends AbstractCellEditor implements TableCellEditor { + + public static final int DEFAULT_EDITOR_WIDTH = 400; + public static final int DEFAULT_EDITOR_HEIGHT = 200; + + protected final LongTextCellComponent component; + + protected final LongTextEditorUI ui; + + protected Frame frame; + + protected JTable table; + + protected AbstractTuttiTableModel<AbstractTuttiBeanUIModel> tableModel; + + protected ColumnIdentifier<AbstractTuttiBeanUIModel> columnIdentifier; + + protected AbstractTuttiBeanUIModel editRow; + + protected Integer rowIndex; + + protected Integer columnIndex; + + public LongTextCellEditor(LongTextEditorUI ui) { + this.ui = ui; + + component = new LongTextCellComponent(); + component.setBorder(new LineBorder(Color.BLACK)); + component.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER || + e.getKeyCode() == KeyEvent.VK_SPACE) { + e.consume(); + startEdit(); + } + } + }); + + component.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + e.consume(); + startEdit(); + } + }); + } + + protected void startEdit() { + if (frame == null) { + frame = SwingUtil.getParentContainer(ui, Frame.class); + } + + ui.setBorder(BorderFactory.createTitledBorder(_(columnIdentifier.getHeaderI18nKey()))); + ui.setBean(editRow); + ui.setProperty(columnIdentifier.getPropertyName()); + + JDialog editor = new JDialog(frame, true); + editor.setUndecorated(true); + editor.add(ui); + editor.setSize(DEFAULT_EDITOR_WIDTH, DEFAULT_EDITOR_HEIGHT); + editor.setResizable(true); + + ComponentResizer cr = new ComponentResizer(); + cr.registerComponent(editor); + ComponentMover cm = new ComponentMover(); + cm.setDragInsets(cr.getDragInsets()); + cm.registerComponent(editor); + + editor.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosed(WindowEvent e) { + Component ui = (Component) e.getSource(); + if (log.isInfoEnabled()) { + log.info("Destroy ui " + ui); + } + JAXXUtil.destroy(ui); + } + }); + + // Computes the location of bottom left corner of the cell + Component comp = component; + int x = 0; + int y = component.getHeight(); + while (comp != null) { + x += comp.getX(); + y += comp.getY(); + comp = comp.getParent(); + } + // if the editor is too big on the right, + // then align its right side to the right side of the cell + if (x + editor.getWidth() > frame.getX() + frame.getWidth()) { + x = x - editor.getWidth() + component.getWidth(); + } + editor.setLocation(x, y); + editor.setVisible(true); + + int r = rowIndex; + int c = columnIndex; + + // stop edition + stopCellEditing(); + + // reselect this cell + AbstractSelectTableAction.doSelectCell(table, r, c); + } + + @Override + public Component getTableCellEditorComponent(JTable table, + Object value, + boolean isSelected, + int row, + int column) { + if (tableModel == null) { + tableModel = (AbstractTuttiTableModel<AbstractTuttiBeanUIModel>) table.getModel(); + this.table = table; + columnIdentifier = tableModel.getPropertyName(column); + } + + rowIndex = row; + columnIndex = column; + + editRow = tableModel.getEntry(row); + + return component; + } + + @Override + public Object getCellEditorValue() { + + Preconditions.checkNotNull(editRow, "No editRow found in editor."); + + String propertyName = columnIdentifier.getPropertyName(); + Object result = TuttiUIUtil.getProperty(editRow, propertyName); + if (log.isInfoEnabled()) { + log.info("editor value (" + propertyName + "): " + result); + } + + return result; + } + + @Override + public boolean stopCellEditing() { + boolean b = super.stopCellEditing(); + if (b) { + rowIndex = null; + editRow = null; + columnIndex = null; + } + return b; + } + + @Override + public void cancelCellEditing() { + super.cancelCellEditing(); + rowIndex = null; + columnIndex = null; + editRow = null; + } + } + + public static class LongTextCellRenderer implements TableCellRenderer { + + protected final LongTextCellComponent component; + + protected String propertyName; + + public LongTextCellRenderer() { + component = new LongTextCellComponent(); + } + + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + + String text; + if (value == null) { + if (propertyName == null) { + AbstractTuttiTableModel tableModel = + (AbstractTuttiTableModel) table.getModel(); + ColumnIdentifier columnIdentifier = tableModel.getPropertyName(column); + propertyName = columnIdentifier.getPropertyName(); + } + // use HTML to show the tooltip in italic + text = "<html><body><i>" + _("tutti.tooltip." + propertyName + ".none") + "</i></body></html>"; + + } else { + // use html to display the tooltip on several lines + text = "<html><body>" + String.valueOf(value).replace("\n", "<br/>") + "</body></html>"; + } + + boolean editable = table.isCellEditable(row, column); + component.setEnabled(editable); + component.setToolTipText(text); + return component; + } + } +} Added: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.css =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.css (rev 0) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.css 2012-12-13 17:13:46 UTC (rev 57) @@ -0,0 +1,4 @@ +#closeButton { + actionIcon: "close"; + text: "tutti.action.close"; +} Copied: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.jaxx (from rev 55, trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/content/operation/species/SpeciesFrequencyUI.jaxx) =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.jaxx (rev 0) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUI.jaxx 2012-12-13 17:13:46 UTC (rev 57) @@ -0,0 +1,48 @@ +<!-- + #%L + Tutti :: UI + $Id$ + $HeadURL$ + %% + Copyright (C) 2012 Ifremer + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program. If not, see + <http://www.gnu.org/licenses/gpl-3.0.html>. + #L% + --> +<Table id='mainPanel'> + + <!-- bean property linked state --> + <String id='property' javaBean='""'/> + + <!-- bean property --> + <fr.ifremer.tutti.ui.swing.AbstractTuttiBeanUIModel id='bean' javaBean='null'/> + + <LongTextEditorUIHandler id='handler' constructorParams='this'/> + + <row weighty='1' fill='both'> + <cell fill='both' weightx='1'> + <JScrollPane> + <JTextArea id='textContent' + onKeyReleased='handler.setText()'/> + </JScrollPane> + </cell> + </row> + <row fill='both'> + <!-- actions --> + <cell fill='both' weightx='1'> + <JButton id='closeButton' onActionPerformed='handler.close()'/> + </cell> + </row> +</Table> \ No newline at end of file Added: trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUIHandler.java =================================================================== --- trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUIHandler.java (rev 0) +++ trunk/tutti-ui-swing/src/main/java/fr/ifremer/tutti/ui/swing/util/editor/LongTextEditorUIHandler.java 2012-12-13 17:13:46 UTC (rev 57) @@ -0,0 +1,75 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package fr.ifremer.tutti.ui.swing.util.editor; + +import fr.ifremer.tutti.ui.swing.AbstractTuttiBeanUIModel; +import fr.ifremer.tutti.ui.swing.util.TuttiUIUtil; +import java.awt.Window; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import jaxx.runtime.SwingUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Handler of the LongTextEditorUI + * @author kmorin + * @since 0.2 + */ +public class LongTextEditorUIHandler { + + private static final Log log = LogFactory.getLog(LongTextEditorUIHandler.class); + + private final LongTextEditorUI ui; + + public LongTextEditorUIHandler(LongTextEditorUI longTextEditorUI) { + ui = longTextEditorUI; + + ui.addPropertyChangeListener(new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + AbstractTuttiBeanUIModel bean = ui.getBean(); + String property = ui.getProperty(); + if (bean != null && !StringUtils.isEmpty(property)) { + Object value = TuttiUIUtil.getProperty(bean, property); + if (value == null) { + value = ""; + } + ui.getTextContent().setText(value.toString()); + } + } + }); + } + + /** + * pushes the typed text in the property of the bean + */ + public void setText() { + String value = ui.getTextContent().getText(); + AbstractTuttiBeanUIModel bean = ui.getBean(); + String property = ui.getProperty(); + TuttiUIUtil.setProperty(bean, property, value); + } + + public void resetText() { + AbstractTuttiBeanUIModel bean = ui.getBean(); + String property = ui.getProperty(); + TuttiUIUtil.setProperty(bean, property, null); + + ui.getTextContent().setText(""); + } + + /** + * closes the editor + */ + public void close() { + if (log.isInfoEnabled()) { + log.info("Will close UI " + ui); + } + SwingUtil.getParentContainer(ui, Window.class).dispose(); + } + +} Modified: trunk/tutti-ui-swing/src/main/resources/i18n/tutti-ui-swing_fr_FR.properties =================================================================== --- trunk/tutti-ui-swing/src/main/resources/i18n/tutti-ui-swing_fr_FR.properties 2012-12-13 15:37:44 UTC (rev 56) +++ trunk/tutti-ui-swing/src/main/resources/i18n/tutti-ui-swing_fr_FR.properties 2012-12-13 17:13:46 UTC (rev 57) @@ -1,7 +1,7 @@ tutti.about.bottomText=Copyright %s - %s - version %s -tutti.about.message=<h3>Tutti</h3><p><strong>Outil de saisie de données d'opérations et de captures au cours des campagnes halieutiques.</strong></p><br/><p>Ce logiciel permettra la saisie en mer des données d'opération de pêche (positions, environnement, engin, etc) et des captures associées (composition de la capture en espèces scientifiques avec poids, nombres, tailles etc) pour l'ensemble des campagnes halieutiques réalisées par l'Ifremer.</p><p>Ce projet a été initiée en 2012 par l'<a href\="http\://www.ifremer.fr">Ifremer</a> et réalisé par la société <a href\="http\://codelutin.com">Codelutin</a>.</p><hr/><br/><p>Pour plus d'informations, vous pouvez visiter le <a href\="http\://maven-site.forge.codelutin.com/tutti">site du projet</a>.</p><p>Projet hébergé sur la forge <a href\="http\://forge.codelutin.com/projects/tutti">Forge.codelutin.com</a>.</p> -tutti.action.about=À propos -tutti.action.about.tip=À Propos +tutti.about.message=<h3>Tutti</h3><p><strong>Outil de saisie de donn\u00c3\u00a9es d'op\u00c3\u00a9rations et de captures au cours des campagnes halieutiques.</strong></p><br/><p>Ce logiciel permettra la saisie en mer des donn\u00c3\u00a9es d'op\u00c3\u00a9ration de p\u00c3\u00aache (positions, environnement, engin, etc) et des captures associ\u00c3\u00a9es (composition de la capture en esp\u00c3\u00a8ces scientifiques avec poids, nombres, tailles etc) pour l'ensemble des campagnes halieutiques r\u00c3\u00a9alis\u00c3\u00a9es par l'Ifremer.</p><p>Ce projet a \u00c3\u00a9t\u00c3\u00a9 initi\u00c3\u00a9e en 2012 par l'<a href\="http\://www.ifremer.fr">Ifremer</a> et r\u00c3\u00a9alis\u00c3\u00a9 par la soci\u00c3\u00a9t\u00c3\u00a9 <a href\="http\://codelutin.com">Codelutin</a>.</p><hr/><br/><p>Pour plus d'informations, vous pouvez visiter le <a href\="http\://maven-site.forge.codelutin.com/tutti">site du projet</a>.</p><p>Projet h\u00c3\u00a9berg\u00c3\u00a9 sur la forge <a href\="http\://forge.codelutin.com/projects/tutti">Forge.codelutin.com</a>.</p> +tutti.action.about=\u00c3\u0080 propos +tutti.action.about.tip=\u00c3\u0080 Propos tutti.action.cancel=Annuler tutti.action.casino-import=Import Casino tutti.action.catches=Captures @@ -13,21 +13,21 @@ tutti.action.exit.tip=Quitter l'application tutti.action.fillCatches=Captures tutti.action.fillCatches.tip=Saisir des captures -tutti.action.generate=Générer -tutti.action.generateCampaignName=Générer le nom +tutti.action.generate=G\u00c3\u00a9n\u00c3\u00a9rer +tutti.action.generateCampaignName=G\u00c3\u00a9n\u00c3\u00a9rer le nom tutti.action.manageProtocol=Protocole -tutti.action.manageProtocol.tip=Gérer les protocoles +tutti.action.manageProtocol.tip=G\u00c3\u00a9rer les protocoles tutti.action.new=Nouveau tutti.action.pupitri-import=Import PUPITRI tutti.action.reload.actions=Recharger les actions tutti.action.reload.home=Reload home screen tutti.action.reload.ui=Recharger l'interface graphique -tutti.action.reset.fishingOperationValidState=Réinitialiser +tutti.action.reset.fishingOperationValidState=R\u00c3\u00a9initialiser tutti.action.save=Enregistrer tutti.action.selectCampaign=Campagne -tutti.action.selectCampaign.tip=Sélectionner la campagne à utiliser +tutti.action.selectCampaign.tip=S\u00c3\u00a9lectionner la campagne \u00c3\u00a0 utiliser tutti.action.site=Site -tutti.action.site.tip=Accéder au site du projet Tutti +tutti.action.site.tip=Acc\u00c3\u00a9der au site du projet Tutti tutti.application.config=Configuration de l'application Tutti tutti.application.name=Tutti tutti.config.category.applications=Application @@ -36,84 +36,84 @@ tutti.config.category.other.description=Autres options tutti.config.category.shortcuts=Raccourcis tutti.config.category.shortcuts.description=Liste des raccourcis clavier -tutti.config.cruiseId=Identifiant de la dernière campagne utilisée -tutti.config.programId=Identifiant de la dernière série de campagne utilisée -tutti.config.ui.autoPopupNumberEditor=Toujours afficher le pavé numérique lors de l'édition d'un nombre +tutti.config.cruiseId=Identifiant de la derni\u00c3\u00a8re campagne utilis\u00c3\u00a9e +tutti.config.programId=Identifiant de la derni\u00c3\u00a8re s\u00c3\u00a9rie de campagne utilis\u00c3\u00a9e +tutti.config.ui.autoPopupNumberEditor=Toujours afficher le pav\u00c3\u00a9 num\u00c3\u00a9rique lors de l'\u00c3\u00a9dition d'un nombre tutti.config.ui.config=Chemin du fichier de configuration des interfaces graphiques -tutti.config.ui.showNumberEditorButton=Afficher le pavé numérique de saisie -tutti.label.benthos.sampleTotalWeight=Poids total échantillonné +tutti.config.ui.showNumberEditorButton=Afficher le pav\u00c3\u00a9 num\u00c3\u00a9rique de saisie +tutti.label.benthos.sampleTotalWeight=Poids total \u00c3\u00a9chantillonn\u00c3\u00a9 tutti.label.benthos.totalWeight=Poids total tutti.label.comment=Commentaire tutti.label.cruise=Campagne -tutti.label.cruise.beginDate=Date de début +tutti.label.cruise.beginDate=Date de d\u00c3\u00a9but tutti.label.cruise.country=Pays tutti.label.cruise.endDate=Date de fin tutti.label.cruise.name=Nom tutti.label.cruise.poche=Nombre de poches -tutti.label.cruise.program=Série -tutti.label.cruise.surveyPart=Série partielle -tutti.label.cruise.year=Année -tutti.label.fishingOperation.averageBottomSalinity=Salinité de fond moyenne -tutti.label.fishingOperation.averageBottomTemperature=Température de fond moyenne +tutti.label.cruise.program=S\u00c3\u00a9rie +tutti.label.cruise.surveyPart=S\u00c3\u00a9rie partielle +tutti.label.cruise.year=Ann\u00c3\u00a9e +tutti.label.fishingOperation.averageBottomSalinity=Salinit\u00c3\u00a9 de fond moyenne +tutti.label.fishingOperation.averageBottomTemperature=Temp\u00c3\u00a9rature de fond moyenne tutti.label.fishingOperation.beaufortScale=Force du vent tutti.label.fishingOperation.date=Date -tutti.label.fishingOperation.distanceChalutee=Distance chalutée -tutti.label.fishingOperation.duree=Durée +tutti.label.fishingOperation.distanceChalutee=Distance chalut\u00c3\u00a9e +tutti.label.fishingOperation.duree=Dur\u00c3\u00a9e tutti.label.fishingOperation.fishingOperationInvalid=Trait invalide -tutti.label.fishingOperation.fishingOperationNumber=Numéro de Trait +tutti.label.fishingOperation.fishingOperationNumber=Num\u00c3\u00a9ro de Trait tutti.label.fishingOperation.fishingOperationRectiligne=Trait rectiligne tutti.label.fishingOperation.fishingOperationValid=Trait valide tutti.label.fishingOperation.gearDate=Date tutti.label.fishingOperation.gearLatitude=Latitude tutti.label.fishingOperation.gearLongitude=Longitude tutti.label.fishingOperation.gearShootingEnd=Fin de traine -tutti.label.fishingOperation.gearShootingEndBottomSalinity=Salinité de fond fin de traine -tutti.label.fishingOperation.gearShootingEndBottomTemperature=Température de fond fin de traine +tutti.label.fishingOperation.gearShootingEndBottomSalinity=Salinit\u00c3\u00a9 de fond fin de traine +tutti.label.fishingOperation.gearShootingEndBottomTemperature=Temp\u00c3\u00a9rature de fond fin de traine tutti.label.fishingOperation.gearShootingEndDepth=Profondeur fin de traine -tutti.label.fishingOperation.gearShootingEndSurfaceSalinity=Salinité de surface fin de traine +tutti.label.fishingOperation.gearShootingEndSurfaceSalinity=Salinit\u00c3\u00a9 de surface fin de traine tutti.label.fishingOperation.gearShootingEndSurfaceTemperature=Temperature de surface fin de traine -tutti.label.fishingOperation.gearShootingStart=Début de traine -tutti.label.fishingOperation.gearShootingStartBottomSalinity=Salinité de fond début de traine -tutti.label.fishingOperation.gearShootingStartBottomTemperature=Température de fond début de traine -tutti.label.fishingOperation.gearShootingStartDepth=Profondeur début de traine -tutti.label.fishingOperation.gearShootingStartSurfaceSalinity=Salinité de surface début de traine -tutti.label.fishingOperation.gearShootingStartSurfaceTemperature=Temperature de surface début de traine +tutti.label.fishingOperation.gearShootingStart=D\u00c3\u00a9but de traine +tutti.label.fishingOperation.gearShootingStartBottomSalinity=Salinit\u00c3\u00a9 de fond d\u00c3\u00a9but de traine +tutti.label.fishingOperation.gearShootingStartBottomTemperature=Temp\u00c3\u00a9rature de fond d\u00c3\u00a9but de traine +tutti.label.fishingOperation.gearShootingStartDepth=Profondeur d\u00c3\u00a9but de traine +tutti.label.fishingOperation.gearShootingStartSurfaceSalinity=Salinit\u00c3\u00a9 de surface d\u00c3\u00a9but de traine +tutti.label.fishingOperation.gearShootingStartSurfaceTemperature=Temperature de surface d\u00c3\u00a9but de traine tutti.label.fishingOperation.gearTime=Heure -tutti.label.fishingOperation.geometrieMesuree=Géométrie mesurée -tutti.label.fishingOperation.localite=Localité +tutti.label.fishingOperation.geometrieMesuree=G\u00c3\u00a9om\u00c3\u00a9trie mesur\u00c3\u00a9e +tutti.label.fishingOperation.localite=Localit\u00c3\u00a9 tutti.label.fishingOperation.longueurBras=Longueur des funes tutti.label.fishingOperation.longueurFunes=Longueur des bras tutti.label.fishingOperation.ouvertureHorizontale=Ouverture horizontale tutti.label.fishingOperation.ouvertureVerticale=Ouverture verticale tutti.label.fishingOperation.seaState=Etat de la mer -tutti.label.fishingOperation.stationNumber=Numéro de la station +tutti.label.fishingOperation.stationNumber=Num\u00c3\u00a9ro de la station tutti.label.fishingOperation.strata=Strate tutti.label.fishingOperation.subStrata=Sous strate -tutti.label.fishingOperation.systemeFermetureCul=Système de fermeture de cul +tutti.label.fishingOperation.systemeFermetureCul=Syst\u00c3\u00a8me de fermeture de cul tutti.label.fishingOperation.windDirection=Direction du vent tutti.label.frequencyConfiguration.maxStep=Classe max tutti.label.frequencyConfiguration.minStep=Classe min -tutti.label.frequencyConfiguration.mode.autoGen=Génération des classes -tutti.label.frequencyConfiguration.mode.autoGen.tip=Mode où toutes les classes de taille sont générées +tutti.label.frequencyConfiguration.mode.autoGen=G\u00c3\u00a9n\u00c3\u00a9ration des classes +tutti.label.frequencyConfiguration.mode.autoGen.tip=Mode o\u00c3\u00b9 toutes les classes de taille sont g\u00c3\u00a9n\u00c3\u00a9r\u00c3\u00a9es tutti.label.frequencyConfiguration.mode.rafale=Mode "rafale" -tutti.label.frequencyConfiguration.mode.rafale.tip=Mode où on ne saisit uniquement la classe de taille (les nombres seront alors incrémentés...) +tutti.label.frequencyConfiguration.mode.rafale.tip=Mode o\u00c3\u00b9 on ne saisit uniquement la classe de taille (les nombres seront alors incr\u00c3\u00a9ment\u00c3\u00a9s...) tutti.label.frequencyConfiguration.mode.simple=Mode simple -tutti.label.frequencyConfiguration.mode.simple.tip=Mode par défaut on on doit tout saisir (les classes de tailles et les nombres) +tutti.label.frequencyConfiguration.mode.simple.tip=Mode par d\u00c3\u00a9faut on on doit tout saisir (les classes de tailles et les nombres) tutti.label.frequencyConfiguration.no.configuration=< Pas de configuration > -tutti.label.frequencyConfiguration.rafaleStep=Classe de taille à incrémenter +tutti.label.frequencyConfiguration.rafaleStep=Classe de taille \u00c3\u00a0 incr\u00c3\u00a9menter tutti.label.frequencyConfiguration.step=Pas de la classe de taille tutti.label.list.gear=Engin(s) tutti.label.list.headOfMission=Chef(s) de mission tutti.label.list.headOfSortRoom=Reponsable(s) de salle de tri tutti.label.list.vessel=Navire(s) tutti.label.macroWaste.totalWeight=Poids total -tutti.label.no.fishingOperation.selected=< Aucun trait sélectionné > -tutti.label.plankton.sampleTotalWeight=Poids total échantillonné +tutti.label.no.fishingOperation.selected=< Aucun trait s\u00c3\u00a9lectionn\u00c3\u00a9 > +tutti.label.plankton.sampleTotalWeight=Poids total \u00c3\u00a9chantillonn\u00c3\u00a9 tutti.label.plankton.totalWeight=Poids total -tutti.label.program=Série de campagne +tutti.label.program=S\u00c3\u00a9rie de campagne tutti.label.program.name=Nom tutti.label.program.zone=Zone -tutti.label.species.sampleVracWeight=Poids total vrac échant. +tutti.label.species.sampleVracWeight=Poids total vrac \u00c3\u00a9chant. tutti.label.species.totalHorsVracWeight=Poids total hors vrac tutti.label.species.totalVracWeight=Poids total vrac tutti.label.species.totalWeight=Poids total @@ -122,12 +122,12 @@ tutti.label.tab.fishingOperation=Trait tutti.label.tab.fishingOperation.environment=Environnement tutti.label.tab.fishingOperation.gearShooting=Mise en oeuvre de l'engin -tutti.label.tab.fishingOperation.general=Caractéristiques générales -tutti.label.tab.fishingOperation.hydrology=Paramètres hydrologiques -tutti.label.tab.macroDechet=Macro déchets +tutti.label.tab.fishingOperation.general=Caract\u00c3\u00a9ristiques g\u00c3\u00a9n\u00c3\u00a9rales +tutti.label.tab.fishingOperation.hydrology=Param\u00c3\u00a8tres hydrologiques +tutti.label.tab.macroDechet=Macro d\u00c3\u00a9chets tutti.label.tab.observationIndividuel=Observations individuelles tutti.label.tab.plancton=Plancton -tutti.label.tab.species=Espèces +tutti.label.tab.species=Esp\u00c3\u00a8ces tutti.legend.frequencyConfiguration=Configuration tutti.menu.actions=Actions tutti.menu.actions.tip=Actions @@ -135,8 +135,8 @@ tutti.menu.file.tip=Fichier tutti.menu.help=Aide tutti.menu.help.tip=Aide -tutti.menu.synchronisationAllegro=Allégro -tutti.menu.synchronisationAllegro.tip=Synchronisation Allégro +tutti.menu.synchronisationAllegro=All\u00c3\u00a9gro +tutti.menu.synchronisationAllegro.tip=Synchronisation All\u00c3\u00a9gro tutti.menu.synchronisationExport=Export tutti.menu.synchronisationExport.tip=Effectuer des exports tutti.menu.synchronisationImport=Import @@ -144,71 +144,72 @@ tutti.menu.synchronisations=Synchronisation tutti.menu.synchronisations.tip=Import/Export tutti.table.benthos.batch.header.comment=Commentaire -tutti.table.benthos.batch.header.elevationRate=Fraction d'élévation -tutti.table.benthos.batch.header.file=Pièces jointes -tutti.table.benthos.batch.header.sampleWeight=Poids échantillonné -tutti.table.benthos.batch.header.speciesByCode=Espèce -tutti.table.benthos.batch.header.speciesByGenusCode=Espèce +tutti.table.benthos.batch.header.elevationRate=Fraction d'\u00c3\u00a9l\u00c3\u00a9vation +tutti.table.benthos.batch.header.file=Pi\u00c3\u00a8ces jointes +tutti.table.benthos.batch.header.sampleWeight=Poids \u00c3\u00a9chantillonn\u00c3\u00a9 +tutti.table.benthos.batch.header.speciesByCode=Esp\u00c3\u00a8ce +tutti.table.benthos.batch.header.speciesByGenusCode=Esp\u00c3\u00a8ce tutti.table.benthos.batch.header.toConfirm=A Confirmer tutti.table.benthos.batch.header.weight=Poids tutti.table.macrowaste.batch.header.comment=Commentaire -tutti.table.macrowaste.batch.header.file=Pièces jointes -tutti.table.macrowaste.batch.header.speciesByCode=Espèce -tutti.table.macrowaste.batch.header.speciesByGenusCode=Espèce +tutti.table.macrowaste.batch.header.file=Pi\u00c3\u00a8ces jointes +tutti.table.macrowaste.batch.header.speciesByCode=Esp\u00c3\u00a8ce +tutti.table.macrowaste.batch.header.speciesByGenusCode=Esp\u00c3\u00a8ce tutti.table.macrowaste.batch.header.weight=Poids tutti.table.plankton.batch.header.comment=Commentaire -tutti.table.plankton.batch.header.elevationRate=Fraction d'élévation -tutti.table.plankton.batch.header.file=Pièces jointes -tutti.table.plankton.batch.header.sampleWeight=Poids échantillonné -tutti.table.plankton.batch.header.speciesByCode=Espèce -tutti.table.plankton.batch.header.speciesByGenusCode=Espèce +tutti.table.plankton.batch.header.elevationRate=Fraction d'\u00c3\u00a9l\u00c3\u00a9vation +tutti.table.plankton.batch.header.file=Pi\u00c3\u00a8ces jointes +tutti.table.plankton.batch.header.sampleWeight=Poids \u00c3\u00a9chantillonn\u00c3\u00a9 +tutti.table.plankton.batch.header.speciesByCode=Esp\u00c3\u00a8ce +tutti.table.plankton.batch.header.speciesByGenusCode=Esp\u00c3\u00a8ce tutti.table.plankton.batch.header.toConfirm=A Confirmer tutti.table.plankton.batch.header.weight=Poids tutti.table.species.batch.header.age=Age tutti.table.species.batch.header.comment=Commentaire -tutti.table.species.batch.header.computedNumber=Nombre calculé -tutti.table.species.batch.header.computedWeight=Poids calculé -tutti.table.species.batch.header.elevationRate=Fraction d'élévation -tutti.table.species.batch.header.file=Pièces jointes -tutti.table.species.batch.header.maturity=Maturité -tutti.table.species.batch.header.sampleWeight=Poids échantillonné +tutti.table.species.batch.header.computedNumber=Nombre calcul\u00c3\u00a9 +tutti.table.species.batch.header.computedWeight=Poids calcul\u00c3\u00a9 +tutti.table.species.batch.header.elevationRate=Fraction d'\u00c3\u00a9l\u00c3\u00a9vation +tutti.table.species.batch.header.file=Pi\u00c3\u00a8ces jointes +tutti.table.species.batch.header.maturity=Maturit\u00c3\u00a9 +tutti.table.species.batch.header.sampleWeight=Poids \u00c3\u00a9chantillonn\u00c3\u00a9 tutti.table.species.batch.header.sex=Sexe -tutti.table.species.batch.header.speciesByCode=Espèce -tutti.table.species.batch.header.speciesByGenusCode=Espèce +tutti.table.species.batch.header.speciesByCode=Esp\u00c3\u00a8ce +tutti.table.species.batch.header.speciesByGenusCode=Esp\u00c3\u00a8ce tutti.table.species.batch.header.toConfirm=A Confirmer tutti.table.species.batch.header.vracHorsVrac=Vrac / Hors Vrac tutti.table.species.batch.header.weight=Poids -tutti.table.species.batch.header.weightCategory=Catégorie Poids -tutti.table.species.frequency.header.computedWeight=Poids calculé +tutti.table.species.batch.header.weightCategory=Cat\u00c3\u00a9gorie Poids +tutti.table.species.frequency.header.computedWeight=Poids calcul\u00c3\u00a9 tutti.table.species.frequency.header.lengthStep=Classe de taille tutti.table.species.frequency.header.number=Nombre -tutti.table.species.frequency.header.weight=Poids observé +tutti.table.species.frequency.header.weight=Poids observ\u00c3\u00a9 tutti.timeeditor.H=H -tutti.title.about=À propos de Tutti -tutti.title.create.cruise=Créer une nouvelle campagne -tutti.title.create.program=Créer une nouvelle série de campagne +tutti.title.about=\u00c3\u0080 propos de Tutti +tutti.title.create.cruise=Cr\u00c3\u00a9er une nouvelle campagne +tutti.title.create.program=Cr\u00c3\u00a9er une nouvelle s\u00c3\u00a9rie de campagne tutti.title.edit.cruise=Editer une campagne existante -tutti.title.edit.operations=Saisie des opérations de pêches (%s) -tutti.title.edit.program=Editer une série de campagne existante +tutti.title.edit.operations=Saisie des op\u00c3\u00a9rations de p\u00c3\u00aaches (%s) +tutti.title.edit.program=Editer une s\u00c3\u00a9rie de campagne existante tutti.title.frequency=Saisie des mensurations -tutti.title.home=Sélection de la campagne -tutti.title.noSelectedCruise=Pas de campagne sélectionné -tutti.title.noSelectedProgram=Pas de série de campagne sélectionné +tutti.title.home=S\u00c3\u00a9lection de la campagne +tutti.title.noSelectedCruise=Pas de campagne s\u00c3\u00a9lectionn\u00c3\u00a9 +tutti.title.noSelectedProgram=Pas de s\u00c3\u00a9rie de campagne s\u00c3\u00a9lectionn\u00c3\u00a9 tutti.title.selectedCruise=Campagne %s -tutti.title.selectedProgram=Série de campagne %s +tutti.title.selectedProgram=S\u00c3\u00a9rie de campagne %s tutti.to.be.done=< A FAIRE > -tutti.validator.error.cruise.beginDate.required=La date de début est obligatoire +tutti.tooltip.comment.none=Aucun +tutti.validator.error.cruise.beginDate.required=La date de d\u00c3\u00a9but est obligatoire tutti.validator.error.cruise.country.required=Le pays est obligatoire tutti.validator.error.cruise.endDate.required=La date de fin est obligatoire -tutti.validator.error.cruise.gear.required=Au moins un engin doit être sélectionné -tutti.validator.error.cruise.headOfMission.required=Au moins un chef de mission doit être sélectionné -tutti.validator.error.cruise.headOfSortRoom.required=Au moins un responsable de salle de tri doit être sélectionné +tutti.validator.error.cruise.gear.required=Au moins un engin doit \u00c3\u00aatre s\u00c3\u00a9lectionn\u00c3\u00a9 +tutti.validator.error.cruise.headOfMission.required=Au moins un chef de mission doit \u00c3\u00aatre s\u00c3\u00a9lectionn\u00c3\u00a9 +tutti.validator.error.cruise.headOfSortRoom.required=Au moins un responsable de salle de tri doit \u00c3\u00aatre s\u00c3\u00a9lectionn\u00c3\u00a9 tutti.validator.error.cruise.name.required=Le nom de la campagne est obligatoire tutti.validator.error.cruise.poche.required=Le nombre de poche est obligatoire -tutti.validator.error.cruise.program.required=La série est obligatoire -tutti.validator.error.cruise.vessel.required=Au moins un bateau doit être sélectionné -tutti.validator.error.cruise.year.required=L'année est obligatoire +tutti.validator.error.cruise.program.required=La s\u00c3\u00a9rie est obligatoire +tutti.validator.error.cruise.vessel.required=Au moins un bateau doit \u00c3\u00aatre s\u00c3\u00a9lectionn\u00c3\u00a9 +tutti.validator.error.cruise.year.required=L'ann\u00c3\u00a9e est obligatoire tutti.validator.error.fishingOperation.date.required=La date du fishingOperation est obligatoire -tutti.validator.error.fishingOperation.stationNumber.required=Le numéro de station est obligatoire -tutti.validator.error.program.name.required=Le nom de la série est obligatoire -tutti.validator.error.program.zone.required=La zone de la série est obligatoire +tutti.validator.error.fishingOperation.stationNumber.required=Le num\u00c3\u00a9ro de station est obligatoire +tutti.validator.error.program.name.required=Le nom de la s\u00c3\u00a9rie est obligatoire +tutti.validator.error.program.zone.required=La zone de la s\u00c3\u00a9rie est obligatoire
participants (1)
-
kmorin@users.forge.codelutin.com