Ver código fonte

Merge branch 'master' into feat/tags

* master:
  Create jar-with-dependencies.jar
  Add a ScaleFactor object.
  Switch from the actual servings spinner to the recipe scale spinner when going read only.
  Wrap view menu in object. Add accelerator. Move menu objects into package.
  Add the ability to enable/disable editing for recipes.
  Don't duplicate ingredientpanel code for a read-only version.

# Conflicts:
#	src/main/lombok/org/leumasjaffe/recipe/view/summary/SummaryPanel.java
Sam Jaffe 4 anos atrás
pai
commit
b8061d1c48
33 arquivos alterados com 279 adições e 211 exclusões
  1. 14 2
      pom.xml
  2. 3 1
      src/main/lombok/org/leumasjaffe/recipe/controller/AddPhaseAction.java
  3. 4 0
      src/main/lombok/org/leumasjaffe/recipe/model/Amount.java
  4. 7 0
      src/main/lombok/org/leumasjaffe/recipe/view/AutoGrowPanel.java
  5. 26 8
      src/main/lombok/org/leumasjaffe/recipe/view/ElementPanel.java
  6. 23 7
      src/main/lombok/org/leumasjaffe/recipe/view/IngredientPanel.java
  7. 0 98
      src/main/lombok/org/leumasjaffe/recipe/view/IngredientPreparationPanel.java
  8. 17 4
      src/main/lombok/org/leumasjaffe/recipe/view/PhasePanel.java
  9. 14 5
      src/main/lombok/org/leumasjaffe/recipe/view/PreparationPanel.java
  10. 12 2
      src/main/lombok/org/leumasjaffe/recipe/view/RecipeCardPanel.java
  11. 5 0
      src/main/lombok/org/leumasjaffe/recipe/view/RecipeManagerFrame.java
  12. 19 3
      src/main/lombok/org/leumasjaffe/recipe/view/RestPanel.java
  13. 15 5
      src/main/lombok/org/leumasjaffe/recipe/view/StepPanel.java
  14. 1 1
      src/main/lombok/org/leumasjaffe/recipe/view/FileMenu.java
  15. 28 0
      src/main/lombok/org/leumasjaffe/recipe/view/menu/ViewMenu.java
  16. 3 2
      src/main/lombok/org/leumasjaffe/recipe/view/summary/ElementPanel.java
  17. 13 4
      src/main/lombok/org/leumasjaffe/recipe/view/summary/IngredientPanel.java
  18. 28 5
      src/main/lombok/org/leumasjaffe/recipe/view/summary/SummaryPanel.java
  19. 20 0
      src/main/lombok/org/leumasjaffe/recipe/viewmodel/ScaleFactor.java
  20. 2 0
      src/test/java/org/leumasjaffe/recipe/controller/AddPhaseActionTest.java
  21. 2 1
      src/test/java/org/leumasjaffe/recipe/view/ElementPanelTest.java
  22. 2 1
      src/test/java/org/leumasjaffe/recipe/view/IngredientPanelTest.java
  23. 0 51
      src/test/java/org/leumasjaffe/recipe/view/IngredientPreparationPanelTest.java
  24. 2 1
      src/test/java/org/leumasjaffe/recipe/view/PhasePanelIT.java
  25. 2 1
      src/test/java/org/leumasjaffe/recipe/view/PhasePanelTest.java
  26. 2 1
      src/test/java/org/leumasjaffe/recipe/view/PreparationPanelTest.java
  27. 2 1
      src/test/java/org/leumasjaffe/recipe/view/RecipeCardPanelTest.java
  28. 2 1
      src/test/java/org/leumasjaffe/recipe/view/StepPanelIT.java
  29. 3 2
      src/test/java/org/leumasjaffe/recipe/view/StepPanelTest.java
  30. 2 1
      src/test/java/org/leumasjaffe/recipe/view/FileMenuTest.java
  31. 2 1
      src/test/java/org/leumasjaffe/recipe/view/summary/ElementPanelTest.java
  32. 2 1
      src/test/java/org/leumasjaffe/recipe/view/summary/IngredientPanelTest.java
  33. 2 1
      src/test/java/org/leumasjaffe/recipe/view/summary/SummaryPanelTest.java

+ 14 - 2
pom.xml

@@ -57,8 +57,8 @@
       </plugin>
       </plugin>
       <plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <version>3.1.0</version>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>2.4</version>
         <configuration>
         <configuration>
           <archive>
           <archive>
             <manifest>
             <manifest>
@@ -67,7 +67,19 @@
               <mainClass>org.leumasjaffe.recipe.RecipeManager</mainClass>
               <mainClass>org.leumasjaffe.recipe.RecipeManager</mainClass>
             </manifest>
             </manifest>
           </archive>
           </archive>
+          <descriptorRefs>
+            <descriptorRef>jar-with-dependencies</descriptorRef>
+          </descriptorRefs>
         </configuration>
         </configuration>
+        <executions>
+          <execution>
+            <id>make-assembly</id> <!-- this is used for inheritance merges -->
+            <phase>package</phase> <!-- bind to the packaging phase -->
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
       </plugin>
       </plugin>
     </plugins>
     </plugins>
   </build>
   </build>

+ 3 - 1
src/main/lombok/org/leumasjaffe/recipe/controller/AddPhaseAction.java

@@ -9,6 +9,7 @@ import javax.swing.JSeparator;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.view.PhasePanel;
 import org.leumasjaffe.recipe.view.PhasePanel;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -20,13 +21,14 @@ import lombok.experimental.NonFinal;
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class AddPhaseAction implements ActionListener {
 public class AddPhaseAction implements ActionListener {
 	JPanel view;
 	JPanel view;
+	ScaleFactor scale;
 	@NonFinal @Setter Element model = null;
 	@NonFinal @Setter Element model = null;
 
 
 	@Override
 	@Override
 	public void actionPerformed(ActionEvent e) {
 	public void actionPerformed(ActionEvent e) {
 		Phase newPhase = new Phase();
 		Phase newPhase = new Phase();
 		this.model.getPhases().add(newPhase);
 		this.model.getPhases().add(newPhase);
-		view.add(new PhasePanel(newPhase));
+		view.add(new PhasePanel(newPhase, scale));
 		view.add(new JSeparator());
 		view.add(new JSeparator());
 		view.revalidate();
 		view.revalidate();
 	}
 	}

+ 4 - 0
src/main/lombok/org/leumasjaffe/recipe/model/Amount.java

@@ -96,6 +96,10 @@ public class Amount {
 		}
 		}
 		return new Amount(unit, value + amount.value * scale(amount), vol, wgt);
 		return new Amount(unit, value + amount.value * scale(amount), vol, wgt);
 	}
 	}
+	
+	public Amount scale(final double factor) {
+		return new Amount(unit, value * factor, vol, wgt);
+	}
 
 
 	private double scale(final Amount amount) {
 	private double scale(final Amount amount) {
 		switch (unit) {
 		switch (unit) {

+ 7 - 0
src/main/lombok/org/leumasjaffe/recipe/view/AutoGrowPanel.java

@@ -158,6 +158,13 @@ public class AutoGrowPanel<C extends Component & AutoGrowPanel.ChildComponent, T
 		models.remove(index);
 		models.remove(index);
 	}
 	}
 	
 	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		members.forEach(c -> c.setEnabled(enabled));
+		last().setVisible(enabled);
+	}
+	
 	private int lastIndex() { return members.size() - 1; }
 	private int lastIndex() { return members.size() - 1; }
 	
 	
 	private C last() { return members.get(lastIndex()); }
 	private C last() { return members.get(lastIndex()); }

+ 26 - 8
src/main/lombok/org/leumasjaffe/recipe/view/ElementPanel.java

@@ -8,6 +8,7 @@ import org.leumasjaffe.observer.ObservableController;
 import org.leumasjaffe.observer.ObservableListener;
 import org.leumasjaffe.observer.ObservableListener;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Phase;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.leumasjaffe.recipe.controller.AddPhaseAction;
 import org.leumasjaffe.recipe.controller.AddPhaseAction;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 
 
@@ -26,6 +27,7 @@ import java.awt.GridBagLayout;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagConstraints;
 import java.awt.Insets;
 import java.awt.Insets;
 import java.awt.event.FocusListener;
 import java.awt.event.FocusListener;
+import java.util.stream.Stream;
 import java.awt.Component;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Dimension;
 
 
@@ -40,12 +42,13 @@ public class ElementPanel extends JScrollPane implements AutoGrowPanel.ChildComp
 	ObservableListener<CollatedDurationPanel, Element> durationListener;
 	ObservableListener<CollatedDurationPanel, Element> durationListener;
 
 
 	@Getter(AccessLevel.PACKAGE) JTextField txtName;
 	@Getter(AccessLevel.PACKAGE) JTextField txtName;
+	JButton btnAdd;
 	AddPhaseAction addPhase;
 	AddPhaseAction addPhase;
 	JPanel panelViewPort;
 	JPanel panelViewPort;
+	ScaleFactor scale;
 	
 	
-	public ElementPanel() {
-		setPreferredSize(new Dimension(600, 450));
-
+	public ElementPanel(final ScaleFactor scale) {
+		this.scale = scale;
 		setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 		setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
 		setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
 		setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
 
 
@@ -66,7 +69,7 @@ public class ElementPanel extends JScrollPane implements AutoGrowPanel.ChildComp
 		gbc_txtName.gridy = 0;
 		gbc_txtName.gridy = 0;
 		panelColumnHeader.add(txtName, gbc_txtName);
 		panelColumnHeader.add(txtName, gbc_txtName);
 		
 		
-		JButton btnAdd = new JButton("+");
+		btnAdd = new JButton("+");
 		btnAdd.setToolTipText("Add new phase");
 		btnAdd.setToolTipText("Add new phase");
 		btnAdd.setPreferredSize(new Dimension(17, 17));
 		btnAdd.setPreferredSize(new Dimension(17, 17));
 		btnAdd.setMargin(new Insets(0, 0, 0, 0));
 		btnAdd.setMargin(new Insets(0, 0, 0, 0));
@@ -93,17 +96,19 @@ public class ElementPanel extends JScrollPane implements AutoGrowPanel.ChildComp
 		setViewportView(panelViewPort);
 		setViewportView(panelViewPort);
 		panelViewPort.setLayout(new VerticalLayout(5));
 		panelViewPort.setLayout(new VerticalLayout(5));
 		
 		
-		addPhase = new AddPhaseAction(panelViewPort);
+		addPhase = new AddPhaseAction(panelViewPort, scale);
 		btnAdd.addActionListener(addPhase);
 		btnAdd.addActionListener(addPhase);
 
 
 		nameController = ObservableController.from(txtName,
 		nameController = ObservableController.from(txtName,
 				Element::getName, Element::setName);
 				Element::getName, Element::setName);
 		durationListener = new ObservableListener<>(panelDuration,
 		durationListener = new ObservableListener<>(panelDuration,
 				(c, v) -> c.setModel(v.getCollatedDuration()));
 				(c, v) -> c.setModel(v.getCollatedDuration()));
+		
+		setEnabled(true);
 	}
 	}
 	
 	
-	public ElementPanel(final Element element) {
-		this();
+	public ElementPanel(final Element element, final ScaleFactor scale) {
+		this(scale);
 		setModel(element);
 		setModel(element);
 	}
 	}
 	
 	
@@ -112,7 +117,7 @@ public class ElementPanel extends JScrollPane implements AutoGrowPanel.ChildComp
 		
 		
 		panelViewPort.removeAll();
 		panelViewPort.removeAll();
 		for (final Phase phase : element.getPhases()) {
 		for (final Phase phase : element.getPhases()) {
-			panelViewPort.add(new PhasePanel(phase));
+			panelViewPort.add(new PhasePanel(phase, scale));
 			panelViewPort.add(new JSeparator());
 			panelViewPort.add(new JSeparator());
 		}
 		}
 		
 		
@@ -121,6 +126,19 @@ public class ElementPanel extends JScrollPane implements AutoGrowPanel.ChildComp
 		durationListener.setObserved(element);
 		durationListener.setObserved(element);
 	}
 	}
 	
 	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		if (enabled) {
+			setPreferredSize(new Dimension(600, 450));
+		} else {
+			setPreferredSize(null);
+		}
+		txtName.setEditable(enabled);
+		btnAdd.setEnabled(enabled);
+		Stream.of(panelViewPort.getComponents()).forEach(c -> c.setEnabled(enabled));
+	}
+	
 	@Override
 	@Override
 	public void removeNotify() {
 	public void removeNotify() {
 		super.removeNotify();
 		super.removeNotify();

+ 23 - 7
src/main/lombok/org/leumasjaffe/recipe/view/IngredientPanel.java

@@ -9,11 +9,13 @@ import java.awt.event.FocusListener;
 
 
 import javax.swing.event.DocumentListener;
 import javax.swing.event.DocumentListener;
 
 
+import org.leumasjaffe.observer.ForwardingObservableListener;
 import org.leumasjaffe.observer.ObservableController;
 import org.leumasjaffe.observer.ObservableController;
 import org.leumasjaffe.observer.ObservableListener;
 import org.leumasjaffe.observer.ObservableListener;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.view.formatter.AmountFormatter;
 import org.leumasjaffe.recipe.view.formatter.AmountFormatter;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.Getter;
@@ -28,11 +30,15 @@ public class IngredientPanel extends JPanel implements AutoGrowPanel.ChildCompon
 	ObservableListener<JTextField, Ingredient> nameController;
 	ObservableListener<JTextField, Ingredient> nameController;
 	ObservableListener<JFormattedTextField, Ingredient> amountController;
 	ObservableListener<JFormattedTextField, Ingredient> amountController;
 	ObservableListener<JTextField, Ingredient> preparationController;
 	ObservableListener<JTextField, Ingredient> preparationController;
+	ForwardingObservableListener<Ingredient> forward = new ForwardingObservableListener<>();
+	
 	@Getter(AccessLevel.PACKAGE) JTextField txtName;
 	@Getter(AccessLevel.PACKAGE) JTextField txtName;
 	@Getter(AccessLevel.PACKAGE) JFormattedTextField txtAmount;
 	@Getter(AccessLevel.PACKAGE) JFormattedTextField txtAmount;
 	@Getter(AccessLevel.PACKAGE) JTextField txtPreparation;
 	@Getter(AccessLevel.PACKAGE) JTextField txtPreparation;
+	ScaleFactor scale;
 		
 		
-	public IngredientPanel() {
+	public IngredientPanel(final ScaleFactor scale, boolean editable) {
+		this.scale = scale;
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		gridBagLayout.columnWidths = new int[]{0, 0, 0, 0, 0};
 		gridBagLayout.columnWidths = new int[]{0, 0, 0, 0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0};
@@ -49,6 +55,7 @@ public class IngredientPanel extends JPanel implements AutoGrowPanel.ChildCompon
 		add(label, gbc_label);
 		add(label, gbc_label);
 		
 		
 		txtName = new JTextField();
 		txtName = new JTextField();
+		txtName.setEditable(editable);
 		GridBagConstraints gbc_txtName = new GridBagConstraints();
 		GridBagConstraints gbc_txtName = new GridBagConstraints();
 		gbc_txtName.fill = GridBagConstraints.HORIZONTAL;
 		gbc_txtName.fill = GridBagConstraints.HORIZONTAL;
 		gbc_txtName.insets = new Insets(0, 0, 0, 5);
 		gbc_txtName.insets = new Insets(0, 0, 0, 5);
@@ -58,6 +65,7 @@ public class IngredientPanel extends JPanel implements AutoGrowPanel.ChildCompon
 		txtName.setColumns(15);
 		txtName.setColumns(15);
 		
 		
 		txtAmount = new JFormattedTextField(new AmountFormatter());
 		txtAmount = new JFormattedTextField(new AmountFormatter());
+		txtAmount.setEditable(editable);
 		GridBagConstraints gbc_txtAmount = new GridBagConstraints();
 		GridBagConstraints gbc_txtAmount = new GridBagConstraints();
 		gbc_txtAmount.fill = GridBagConstraints.HORIZONTAL;
 		gbc_txtAmount.fill = GridBagConstraints.HORIZONTAL;
 		gbc_txtAmount.insets = new Insets(0, 0, 0, 5);
 		gbc_txtAmount.insets = new Insets(0, 0, 0, 5);
@@ -67,6 +75,7 @@ public class IngredientPanel extends JPanel implements AutoGrowPanel.ChildCompon
 		txtAmount.setColumns(6);
 		txtAmount.setColumns(6);
 		
 		
 		txtPreparation = new JTextField();
 		txtPreparation = new JTextField();
+		txtPreparation.setEditable(editable);
 		GridBagConstraints gbc_txtPreparation = new GridBagConstraints();
 		GridBagConstraints gbc_txtPreparation = new GridBagConstraints();
 		gbc_txtPreparation.anchor = GridBagConstraints.ABOVE_BASELINE;
 		gbc_txtPreparation.anchor = GridBagConstraints.ABOVE_BASELINE;
 		gbc_txtPreparation.fill = GridBagConstraints.HORIZONTAL;
 		gbc_txtPreparation.fill = GridBagConstraints.HORIZONTAL;
@@ -75,22 +84,20 @@ public class IngredientPanel extends JPanel implements AutoGrowPanel.ChildCompon
 		add(txtPreparation, gbc_txtPreparation);
 		add(txtPreparation, gbc_txtPreparation);
 		txtPreparation.setColumns(10);
 		txtPreparation.setColumns(10);
 		
 		
-		// I technically don't need to listen here as of this change,
-		// but if I ever restore support for it, it will be convenient.
 		nameController = ObservableController.from(txtName,
 		nameController = ObservableController.from(txtName,
 				Ingredient::getName, Ingredient::setName);
 				Ingredient::getName, Ingredient::setName);
 		amountController = ObservableController.from(txtAmount,
 		amountController = ObservableController.from(txtAmount,
-				Ingredient::getAmount, Ingredient::setAmount);
+				i -> i.getAmount().scale(scale.getScale()), Ingredient::setAmount);
 		preparationController = ObservableController.from(txtPreparation,
 		preparationController = ObservableController.from(txtPreparation,
 				Ingredient::getPreparation, Ingredient::setPreparation,
 				Ingredient::getPreparation, Ingredient::setPreparation,
 				ing -> {
 				ing -> {
 					ing.setPreparation("");
 					ing.setPreparation("");
 					ObserverDispatch.notifySubscribers(ing);
 					ObserverDispatch.notifySubscribers(ing);
-				});
+				});		
 	}
 	}
 
 
-	public IngredientPanel(final Ingredient ingredient) {
-		this();
+	public IngredientPanel(final Ingredient ingredient, final ScaleFactor scale, boolean editable) {
+		this(scale, editable);
 		setModel(ingredient);
 		setModel(ingredient);
 	}
 	}
 	
 	
@@ -98,6 +105,15 @@ public class IngredientPanel extends JPanel implements AutoGrowPanel.ChildCompon
 		nameController.setObserved(ingredient);
 		nameController.setObserved(ingredient);
 		amountController.setObserved(ingredient);
 		amountController.setObserved(ingredient);
 		preparationController.setObserved(ingredient);
 		preparationController.setObserved(ingredient);
+		forward.setObserved(ingredient, scale);
+	}
+	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		txtName.setEditable(enabled);
+		txtAmount.setEditable(enabled);
+		txtPreparation.setEditable(enabled);
 	}
 	}
 	
 	
 	@Override
 	@Override

+ 0 - 98
src/main/lombok/org/leumasjaffe/recipe/view/IngredientPreparationPanel.java

@@ -1,98 +0,0 @@
-package org.leumasjaffe.recipe.view;
-
-import javax.swing.JPanel;
-import java.awt.GridBagLayout;
-import javax.swing.JTextField;
-import java.awt.GridBagConstraints;
-import java.awt.Insets;
-
-import org.leumasjaffe.observer.ObservableListener;
-import org.leumasjaffe.observer.ObserverDispatch;
-import org.leumasjaffe.recipe.model.Ingredient;
-import org.leumasjaffe.recipe.view.formatter.AmountFormatter;
-
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.experimental.FieldDefaults;
-
-import javax.swing.JFormattedTextField;
-import javax.swing.JLabel;
-
-@SuppressWarnings("serial")
-@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
-public class IngredientPreparationPanel extends JPanel {
-	
-	ObservableListener<IngredientPreparationPanel, Ingredient> listener;
-	@Getter(AccessLevel.PACKAGE) JTextField txtName;
-	@Getter(AccessLevel.PACKAGE) JFormattedTextField txtAmount;
-	@Getter(AccessLevel.PACKAGE) JTextField txtPreparation;
-	
-	public IngredientPreparationPanel() {
-		GridBagLayout gridBagLayout = new GridBagLayout();
-		gridBagLayout.columnWidths = new int[]{0, 0, 0, 0, 0};
-		gridBagLayout.rowHeights = new int[]{0, 0};
-		gridBagLayout.columnWeights = new double[]{0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE};
-		gridBagLayout.rowWeights = new double[]{0.0, Double.MIN_VALUE};
-		setLayout(gridBagLayout);
-		
-		JLabel label = new JLabel("\u2022");
-		GridBagConstraints gbc_label = new GridBagConstraints();
-		gbc_label.insets = new Insets(0, 0, 0, 5);
-		gbc_label.anchor = GridBagConstraints.EAST;
-		gbc_label.gridx = 0;
-		gbc_label.gridy = 0;
-		add(label, gbc_label);
-		
-		txtName = new JTextField();
-		txtName.setEditable(false);
-		GridBagConstraints gbc_txtName = new GridBagConstraints();
-		gbc_txtName.fill = GridBagConstraints.HORIZONTAL;
-		gbc_txtName.insets = new Insets(0, 0, 0, 5);
-		gbc_txtName.gridx = 1;
-		gbc_txtName.gridy = 0;
-		add(txtName, gbc_txtName);
-		txtName.setColumns(15);
-		
-		txtAmount = new JFormattedTextField(new AmountFormatter());
-		txtAmount.setEditable(false);
-		GridBagConstraints gbc_txtAmount = new GridBagConstraints();
-		gbc_txtAmount.fill = GridBagConstraints.HORIZONTAL;
-		gbc_txtAmount.insets = new Insets(0, 0, 0, 5);
-		gbc_txtAmount.gridx = 2;
-		gbc_txtAmount.gridy = 0;
-		add(txtAmount, gbc_txtAmount);
-		txtAmount.setColumns(6);
-		
-		txtPreparation = new JTextField();
-		txtPreparation.setEditable(false);
-		GridBagConstraints gbc_txtPreparation = new GridBagConstraints();
-		gbc_txtPreparation.anchor = GridBagConstraints.ABOVE_BASELINE;
-		gbc_txtPreparation.fill = GridBagConstraints.HORIZONTAL;
-		gbc_txtPreparation.gridx = 3;
-		gbc_txtPreparation.gridy = 0;
-		add(txtPreparation, gbc_txtPreparation);
-		txtPreparation.setColumns(10);
-		
-		listener = new ObservableListener<>(this, (c, v) -> {
-			c.txtName.setText(v.getName());
-			c.txtAmount.setValue(v.getAmount());
-			c.txtPreparation.setText(v.getPreparation());
-		});
-	}
-	
-	public IngredientPreparationPanel(final Ingredient ingredient) {
-		this();
-		setModel(ingredient);
-	}
-	
-	public void setModel(final Ingredient ingredient) {
-		listener.setObserved(ingredient);
-	}
-	
-	@Override
-	public void removeNotify() {
-		super.removeNotify();
-		ObserverDispatch.unsubscribeAll(listener);
-	}
-
-}

+ 17 - 4
src/main/lombok/org/leumasjaffe/recipe/view/PhasePanel.java

@@ -10,6 +10,7 @@ import org.leumasjaffe.observer.Observable;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Step;
 import org.leumasjaffe.recipe.model.Step;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.Getter;
@@ -22,19 +23,23 @@ import org.jdesktop.swingx.VerticalLayout;
 public class PhasePanel extends JPanel {
 public class PhasePanel extends JPanel {
 	ForwardingObservableListener<Phase> listener = new ForwardingObservableListener<>();
 	ForwardingObservableListener<Phase> listener = new ForwardingObservableListener<>();
 
 
+	PreparationPanel panelPrep;
+	RestPanel panelRest;
 	@Getter(AccessLevel.PACKAGE) AutoGrowPanel<StepPanel, Step> panelSteps;
 	@Getter(AccessLevel.PACKAGE) AutoGrowPanel<StepPanel, Step> panelSteps;
 	
 	
 	// TODO Re-configure to support this(); setModel(phase); pattern.
 	// TODO Re-configure to support this(); setModel(phase); pattern.
-	public PhasePanel(final Phase phase) {		
+	public PhasePanel(final Phase phase, final ScaleFactor scale) {		
 		setLayout(new VerticalLayout(5));
 		setLayout(new VerticalLayout(5));
 		
 		
-		add(new PreparationPanel(phase));
+		panelPrep = new PreparationPanel(phase, scale);
+		add(panelPrep);
 		
 		
-		panelSteps = new AutoGrowPanel<>(Step::new, StepPanel::new);
+		panelSteps = new AutoGrowPanel<>(Step::new, s -> new StepPanel(s, scale));
 		panelSteps.setGap(5);
 		panelSteps.setGap(5);
 		add(panelSteps);
 		add(panelSteps);
 		
 		
-		add(new RestPanel(phase.getRest()));
+		panelRest = new RestPanel(phase.getRest());
+		add(panelRest);
 
 
 		panelSteps.setModel(phase.getCooking(), added -> {
 		panelSteps.setModel(phase.getCooking(), added -> {
 			listener.setObserved(phase, allChildren(phase));
 			listener.setObserved(phase, allChildren(phase));
@@ -52,6 +57,14 @@ public class PhasePanel extends JPanel {
 		return children;
 		return children;
 	}
 	}
 	
 	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		panelPrep.setEnabled(enabled);
+		panelSteps.setEnabled(enabled);
+		panelRest.setEnabled(enabled);
+	}
+	
 	@Override
 	@Override
 	public void removeNotify() {
 	public void removeNotify() {
 		super.removeNotify();
 		super.removeNotify();

+ 14 - 5
src/main/lombok/org/leumasjaffe/recipe/view/PreparationPanel.java

@@ -12,6 +12,7 @@ import org.leumasjaffe.recipe.model.Duration;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Preparation;
 import org.leumasjaffe.recipe.model.Preparation;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.experimental.FieldDefaults;
 import lombok.experimental.FieldDefaults;
@@ -33,9 +34,11 @@ public class PreparationPanel extends JPanel {
 	IndirectObservableListener<JPanel, Preparation> childListener;
 	IndirectObservableListener<JPanel, Preparation> childListener;
 	ObservableListener<JFormattedTextField, Preparation> durationController;
 	ObservableListener<JFormattedTextField, Preparation> durationController;
 	
 	
-	public PreparationPanel() {
+	DurationPanel panelDuration;
+	
+	public PreparationPanel(final ScaleFactor scale) {
 		controller = new ReplaceChildrenAction<>(Preparation::getIngredients,
 		controller = new ReplaceChildrenAction<>(Preparation::getIngredients,
-				IngredientPreparationPanel::new);
+				ing -> new IngredientPanel(ing, scale, false));
 		
 		
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		gridBagLayout.columnWidths = new int[]{0, 0, 0};
 		gridBagLayout.columnWidths = new int[]{0, 0, 0};
@@ -73,7 +76,7 @@ public class PreparationPanel extends JPanel {
 		gbc_horizontalGlue.gridy = 0;
 		gbc_horizontalGlue.gridy = 0;
 		panelLeft.add(horizontalGlue, gbc_horizontalGlue);
 		panelLeft.add(horizontalGlue, gbc_horizontalGlue);
 		
 		
-		DurationPanel panelDuration = new DurationPanel("Requires", Duration.ZERO);
+		panelDuration = new DurationPanel("Requires", Duration.ZERO);
 		GridBagConstraints gbc_panelDuration = new GridBagConstraints();
 		GridBagConstraints gbc_panelDuration = new GridBagConstraints();
 		gbc_panelDuration.gridx = 2;
 		gbc_panelDuration.gridx = 2;
 		gbc_panelDuration.gridy = 0;
 		gbc_panelDuration.gridy = 0;
@@ -96,8 +99,8 @@ public class PreparationPanel extends JPanel {
 				Preparation::getDuration, Preparation::setDuration);
 				Preparation::getDuration, Preparation::setDuration);
 	}
 	}
 	
 	
-	public PreparationPanel(final Phase phase) {
-		this();
+	public PreparationPanel(final Phase phase, final ScaleFactor scale) {
+		this(scale);
 		setModel(phase);
 		setModel(phase);
 	}
 	}
 	
 	
@@ -107,6 +110,12 @@ public class PreparationPanel extends JPanel {
 		durationController.setObserved(prep);
 		durationController.setObserved(prep);
 	}
 	}
 	
 	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		panelDuration.txtTime.setEditable(enabled);
+	}
+	
 	@Override
 	@Override
 	public void removeNotify() {
 	public void removeNotify() {
 		super.removeNotify();
 		super.removeNotify();

+ 12 - 2
src/main/lombok/org/leumasjaffe/recipe/view/RecipeCardPanel.java

@@ -7,6 +7,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.RecipeCard;
 import org.leumasjaffe.recipe.model.RecipeCard;
 import org.leumasjaffe.recipe.view.summary.SummaryPanel;
 import org.leumasjaffe.recipe.view.summary.SummaryPanel;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.experimental.FieldDefaults;
 import lombok.experimental.FieldDefaults;
@@ -21,14 +22,15 @@ import javax.swing.ScrollPaneConstants;
 public class RecipeCardPanel extends JSplitPane {
 public class RecipeCardPanel extends JSplitPane {
 	ForwardingObservableListener<RecipeCard> listener;
 	ForwardingObservableListener<RecipeCard> listener;
 	
 	
+	ScaleFactor scale = new ScaleFactor();
 	SummaryPanel summaryPanel;
 	SummaryPanel summaryPanel;
 	AutoGrowPanel<ElementPanel, Element> panelElements;
 	AutoGrowPanel<ElementPanel, Element> panelElements;
 	
 	
 	public RecipeCardPanel() {
 	public RecipeCardPanel() {
 		setPreferredSize(new Dimension(1100, 600));
 		setPreferredSize(new Dimension(1100, 600));
 
 
-		summaryPanel = new SummaryPanel();
-		panelElements = new AutoGrowPanel<>(Element::new, ElementPanel::new);
+		summaryPanel = new SummaryPanel(scale);
+		panelElements = new AutoGrowPanel<>(Element::new, e -> new ElementPanel(e, scale));
 		panelElements.setGap(5);
 		panelElements.setGap(5);
 
 
 		final JScrollPane scrollPane = new JScrollPane(panelElements);
 		final JScrollPane scrollPane = new JScrollPane(panelElements);
@@ -46,6 +48,7 @@ public class RecipeCardPanel extends JSplitPane {
 	}
 	}
 	
 	
 	public void setModel(final RecipeCard card) {
 	public void setModel(final RecipeCard card) {
+		scale.setModel(card);
 		summaryPanel.setModel(card);
 		summaryPanel.setModel(card);
 		panelElements.setModel(card.getElements(), added -> {
 		panelElements.setModel(card.getElements(), added -> {
 			listener.setObserved(card, card.getElements());
 			listener.setObserved(card, card.getElements());
@@ -62,5 +65,12 @@ public class RecipeCardPanel extends JSplitPane {
 		super.removeNotify();
 		super.removeNotify();
 		ObserverDispatch.unsubscribeAll(listener);
 		ObserverDispatch.unsubscribeAll(listener);
 	}
 	}
+	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		summaryPanel.setEnabled(enabled);
+		panelElements.setEnabled(enabled);
+	}
 
 
 }
 }

+ 5 - 0
src/main/lombok/org/leumasjaffe/recipe/view/RecipeManagerFrame.java

@@ -4,6 +4,8 @@ import javax.swing.JFrame;
 
 
 import org.leumasjaffe.recipe.controller.FileController;
 import org.leumasjaffe.recipe.controller.FileController;
 import org.leumasjaffe.recipe.model.RecipeCard;
 import org.leumasjaffe.recipe.model.RecipeCard;
+import org.leumasjaffe.recipe.view.menu.FileMenu;
+import org.leumasjaffe.recipe.view.menu.ViewMenu;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.experimental.FieldDefaults;
 import lombok.experimental.FieldDefaults;
@@ -26,6 +28,9 @@ public class RecipeManagerFrame extends JFrame implements FileController.ViewMod
 		JMenu mnFile = new FileMenu(this, fileController);
 		JMenu mnFile = new FileMenu(this, fileController);
 		menuBar.add(mnFile);
 		menuBar.add(mnFile);
 		
 		
+		JMenu mnView = new ViewMenu(this);
+		menuBar.add(mnView);
+				
 		fileController.create();
 		fileController.create();
 
 
 		pack();
 		pack();

+ 19 - 3
src/main/lombok/org/leumasjaffe/recipe/view/RestPanel.java

@@ -25,6 +25,8 @@ public class RestPanel extends JPanel {
 	ObservableListener<JFormattedTextField, Rest> upToController;
 	ObservableListener<JFormattedTextField, Rest> upToController;
 
 
 	JComboBox<Rest.Where> jcbLocation;
 	JComboBox<Rest.Where> jcbLocation;
+	DurationPanel panelDuration;
+	DurationPanel panelUpTo;
 	
 	
 	// TODO
 	// TODO
 	Rest model;
 	Rest model;
@@ -51,20 +53,21 @@ public class RestPanel extends JPanel {
 		gbc_lblLocation.gridy = 0;
 		gbc_lblLocation.gridy = 0;
 		add(jcbLocation, gbc_lblLocation);
 		add(jcbLocation, gbc_lblLocation);
 		
 		
-		DurationPanel panelDuration = new DurationPanel("");
+		panelDuration = new DurationPanel("");
 		GridBagConstraints gbc_panelDuration = new GridBagConstraints();
 		GridBagConstraints gbc_panelDuration = new GridBagConstraints();
 		gbc_panelDuration.gridx = 2;
 		gbc_panelDuration.gridx = 2;
 		gbc_panelDuration.gridy = 0;
 		gbc_panelDuration.gridy = 0;
 		add(panelDuration, gbc_panelDuration);
 		add(panelDuration, gbc_panelDuration);
 
 
-		DurationPanel panelUpTo = new DurationPanel("and up to");
+		panelUpTo = new DurationPanel("and up to");
 		GridBagConstraints gbc_panelUpTo = new GridBagConstraints();
 		GridBagConstraints gbc_panelUpTo = new GridBagConstraints();
 		gbc_panelUpTo.gridx = 3;
 		gbc_panelUpTo.gridx = 3;
 		gbc_panelUpTo.gridy = 0;
 		gbc_panelUpTo.gridy = 0;
 		add(panelUpTo, gbc_panelUpTo);
 		add(panelUpTo, gbc_panelUpTo);
 
 
 		jcbLocation.addItemListener(e -> {
 		jcbLocation.addItemListener(e -> {
-			panelDuration.txtTime.setEditable(!e.getItem().equals(Rest.Where.NONE));
+			panelDuration.txtTime.setEditable(!isResting());
+			panelUpTo.txtTime.setEditable(!isResting());
 			this.model.setWhere(Rest.Where.class.cast(e.getItem()));
 			this.model.setWhere(Rest.Where.class.cast(e.getItem()));
 		});
 		});
 		durationController = ObservableController.from(panelDuration.txtTime,
 		durationController = ObservableController.from(panelDuration.txtTime,
@@ -73,6 +76,7 @@ public class RestPanel extends JPanel {
 				Rest::getUpTo, Rest::setUpTo);
 				Rest::getUpTo, Rest::setUpTo);
 		
 		
 		panelDuration.txtTime.setEditable(false);
 		panelDuration.txtTime.setEditable(false);
+		panelUpTo.txtTime.setEditable(false);
 	}
 	}
 
 
 	public RestPanel(final Rest rest) {
 	public RestPanel(final Rest rest) {
@@ -87,10 +91,22 @@ public class RestPanel extends JPanel {
 		upToController.setObserved(rest);
 		upToController.setObserved(rest);
 	}
 	}
 	
 	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		jcbLocation.setEnabled(enabled);
+		panelDuration.txtTime.setEditable(enabled && !isResting());
+		panelUpTo.txtTime.setEditable(enabled && !isResting());
+	}
+	
 	@Override
 	@Override
 	public void removeNotify() {
 	public void removeNotify() {
 		super.removeNotify();
 		super.removeNotify();
 		ObserverDispatch.unsubscribeAll(durationController);
 		ObserverDispatch.unsubscribeAll(durationController);
 	}
 	}
 
 
+	private boolean isResting() {
+		return jcbLocation.getSelectedItem().equals(Rest.Where.NONE);
+	}
+
 }
 }

+ 15 - 5
src/main/lombok/org/leumasjaffe/recipe/view/StepPanel.java

@@ -10,6 +10,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.controller.TextBinding;
 import org.leumasjaffe.recipe.controller.TextBinding;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Step;
 import org.leumasjaffe.recipe.model.Step;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.Getter;
@@ -39,8 +40,9 @@ public class StepPanel extends JPanel implements AutoGrowPanel.ChildComponent {
 	JLabel lblIndex;
 	JLabel lblIndex;
 	@Getter(AccessLevel.PACKAGE) JTextPane txtpnInstructions;
 	@Getter(AccessLevel.PACKAGE) JTextPane txtpnInstructions;
 	AutoGrowPanel<IngredientPanel, Ingredient> panelIngredients;
 	AutoGrowPanel<IngredientPanel, Ingredient> panelIngredients;
+	DurationPanel panelDuration;
 		
 		
-	public StepPanel() {
+	public StepPanel(final ScaleFactor scale) {
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		gridBagLayout.columnWidths = new int[]{0, 0, 0};
 		gridBagLayout.columnWidths = new int[]{0, 0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0};
@@ -77,13 +79,13 @@ public class StepPanel extends JPanel implements AutoGrowPanel.ChildComponent {
 		gbc_horizontalGlue.gridy = 0;
 		gbc_horizontalGlue.gridy = 0;
 		panelLeft.add(horizontalGlue, gbc_horizontalGlue);
 		panelLeft.add(horizontalGlue, gbc_horizontalGlue);
 		
 		
-		DurationPanel panelDuration = new DurationPanel("Requires");
+		panelDuration = new DurationPanel("Requires");
 		GridBagConstraints gbc_panelDuration = new GridBagConstraints();
 		GridBagConstraints gbc_panelDuration = new GridBagConstraints();
 		gbc_panelDuration.gridx = 2;
 		gbc_panelDuration.gridx = 2;
 		gbc_panelDuration.gridy = 0;
 		gbc_panelDuration.gridy = 0;
 		panelLeft.add(panelDuration, gbc_panelDuration);
 		panelLeft.add(panelDuration, gbc_panelDuration);
 		
 		
-		panelIngredients = new AutoGrowPanel<>(Ingredient::new, IngredientPanel::new);
+		panelIngredients = new AutoGrowPanel<>(Ingredient::new, ing -> new IngredientPanel(ing, scale, true));
 		GridBagConstraints gbc_panelIngredients = new GridBagConstraints();
 		GridBagConstraints gbc_panelIngredients = new GridBagConstraints();
 		gbc_panelIngredients.gridwidth = 3;
 		gbc_panelIngredients.gridwidth = 3;
 		gbc_panelIngredients.insets = new Insets(0, 0, 0, 5);
 		gbc_panelIngredients.insets = new Insets(0, 0, 0, 5);
@@ -108,8 +110,8 @@ public class StepPanel extends JPanel implements AutoGrowPanel.ChildComponent {
 		setListPosition(0);
 		setListPosition(0);
 	}
 	}
 	
 	
-	public StepPanel(final Step step) {
-		this();
+	public StepPanel(final Step step, final ScaleFactor scale) {
+		this(scale);
 		setModel(step);
 		setModel(step);
 	}
 	}
 	
 	
@@ -125,6 +127,14 @@ public class StepPanel extends JPanel implements AutoGrowPanel.ChildComponent {
 		durationController.setObserved(step);
 		durationController.setObserved(step);
 	}
 	}
 	
 	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		panelDuration.txtTime.setEditable(enabled);
+		txtpnInstructions.setEditable(enabled);
+		panelIngredients.setEnabled(enabled);
+	}
+	
 	@Override
 	@Override
 	public void removeNotify() {
 	public void removeNotify() {
 		super.removeNotify();
 		super.removeNotify();

+ 1 - 1
src/main/lombok/org/leumasjaffe/recipe/view/FileMenu.java

@@ -1,4 +1,4 @@
-package org.leumasjaffe.recipe.view;
+package org.leumasjaffe.recipe.view.menu;
 
 
 import java.awt.Toolkit;
 import java.awt.Toolkit;
 import java.awt.event.InputEvent;
 import java.awt.event.InputEvent;

+ 28 - 0
src/main/lombok/org/leumasjaffe/recipe/view/menu/ViewMenu.java

@@ -0,0 +1,28 @@
+package org.leumasjaffe.recipe.view.menu;
+
+import java.awt.Toolkit;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.KeyStroke;
+
+import org.leumasjaffe.recipe.view.RecipeCardPanel;
+
+@SuppressWarnings("serial")
+public class ViewMenu extends JMenu {
+	static final int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
+
+	public ViewMenu(JFrame parent) {
+		super("View");
+		
+		JCheckBoxMenuItem chckbxmntmReadOnly = new JCheckBoxMenuItem("Read Only");
+		add(chckbxmntmReadOnly);
+		chckbxmntmReadOnly.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, MASK));
+		chckbxmntmReadOnly.addActionListener(e -> {
+			((RecipeCardPanel) parent.getContentPane()).setEnabled(!chckbxmntmReadOnly.getState());
+		});
+	}
+
+}

+ 3 - 2
src/main/lombok/org/leumasjaffe/recipe/view/summary/ElementPanel.java

@@ -8,6 +8,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.controller.ReplaceChildrenAction;
 import org.leumasjaffe.recipe.controller.ReplaceChildrenAction;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.Getter;
@@ -29,9 +30,9 @@ public class ElementPanel extends JPanel {
 	@Getter(AccessLevel.PACKAGE) JLabel lblProductName;
 	@Getter(AccessLevel.PACKAGE) JLabel lblProductName;
 	@Getter(AccessLevel.PACKAGE) JPanel panelIngredients;
 	@Getter(AccessLevel.PACKAGE) JPanel panelIngredients;
 	
 	
-	public ElementPanel(final Element element) {
+	public ElementPanel(final Element element, final ScaleFactor scale) {
 		controller = new ReplaceChildrenAction<>(
 		controller = new ReplaceChildrenAction<>(
-				Element::getIngredients, IngredientPanel::new);
+				Element::getIngredients, i -> new IngredientPanel(i, scale));
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		gridBagLayout.columnWidths = new int[]{0, 0, 0};
 		gridBagLayout.columnWidths = new int[]{0, 0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0, 0};

+ 13 - 4
src/main/lombok/org/leumasjaffe/recipe/view/summary/IngredientPanel.java

@@ -6,8 +6,10 @@ import javax.swing.JTextField;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagConstraints;
 import java.awt.Insets;
 import java.awt.Insets;
 
 
+import org.leumasjaffe.observer.IndirectObservableListener;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.view.formatter.AmountFormatter;
 import org.leumasjaffe.recipe.view.formatter.AmountFormatter;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.Getter;
@@ -19,10 +21,13 @@ import javax.swing.JLabel;
 @SuppressWarnings("serial")
 @SuppressWarnings("serial")
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 public class IngredientPanel extends JPanel {
 public class IngredientPanel extends JPanel {
+	ScaleFactor scale;
 	@Getter(AccessLevel.PACKAGE) JTextField txtName;
 	@Getter(AccessLevel.PACKAGE) JTextField txtName;
 	@Getter(AccessLevel.PACKAGE) JFormattedTextField txtAmount;
 	@Getter(AccessLevel.PACKAGE) JFormattedTextField txtAmount;
+	IndirectObservableListener<JFormattedTextField, Ingredient> listener;
 	
 	
-	public IngredientPanel() {
+	public IngredientPanel(final ScaleFactor scale) {
+		this.scale = scale;
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		GridBagLayout gridBagLayout = new GridBagLayout();
 		gridBagLayout.columnWidths = new int[]{0, 0, 0, 0};
 		gridBagLayout.columnWidths = new int[]{0, 0, 0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0};
 		gridBagLayout.rowHeights = new int[]{0, 0};
@@ -56,15 +61,19 @@ public class IngredientPanel extends JPanel {
 		gbc_txtAmount.gridy = 0;
 		gbc_txtAmount.gridy = 0;
 		add(txtAmount, gbc_txtAmount);
 		add(txtAmount, gbc_txtAmount);
 		txtAmount.setColumns(6);
 		txtAmount.setColumns(6);
+		
+		listener = new IndirectObservableListener<>(txtAmount,
+				(c, i) -> c.setValue(i.getAmount().scale(scale.getScale())));
 	}
 	}
 
 
-	public IngredientPanel(final Ingredient ingredient) {
-		this();
+	public IngredientPanel(final Ingredient ingredient, final ScaleFactor scale) {
+		this(scale);
 		setModel(ingredient);
 		setModel(ingredient);
 	}
 	}
 	
 	
 	public void setModel(final Ingredient ingredient) {
 	public void setModel(final Ingredient ingredient) {
 		txtName.setText(ingredient.getName());
 		txtName.setText(ingredient.getName());
-		txtAmount.setValue(ingredient.getAmount());
+		listener.setObserved(ingredient, ingredient, scale);
+		txtAmount.setValue(ingredient.getAmount().scale(scale.getScale()));
 	}
 	}
 }
 }

+ 28 - 5
src/main/lombok/org/leumasjaffe/recipe/view/summary/SummaryPanel.java

@@ -22,6 +22,7 @@ import org.leumasjaffe.recipe.model.RecipeCard;
 import org.leumasjaffe.recipe.view.CollatedDurationPanel;
 import org.leumasjaffe.recipe.view.CollatedDurationPanel;
 import org.leumasjaffe.recipe.view.ImagePanel;
 import org.leumasjaffe.recipe.view.ImagePanel;
 import org.leumasjaffe.recipe.view.TagInputPanel;
 import org.leumasjaffe.recipe.view.TagInputPanel;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 import lombok.AccessLevel;
 import lombok.AccessLevel;
 import lombok.experimental.FieldDefaults;
 import lombok.experimental.FieldDefaults;
@@ -34,6 +35,7 @@ public class SummaryPanel extends JPanel {
 	ReplaceChildrenAction<RecipeCard, Element> controller;
 	ReplaceChildrenAction<RecipeCard, Element> controller;
 	TextBinding<RecipeCard> titleBinding;
 	TextBinding<RecipeCard> titleBinding;
 	SpinnerBinding<RecipeCard, Integer> servingsBinding;
 	SpinnerBinding<RecipeCard, Integer> servingsBinding;
+	SpinnerBinding<ScaleFactor, Integer> servingsToMakeBinding;
 	TextBinding<RecipeCard> descriptionBinding;
 	TextBinding<RecipeCard> descriptionBinding;
 	ObservableListener<CollatedDurationPanel, RecipeCard> durationListener;
 	ObservableListener<CollatedDurationPanel, RecipeCard> durationListener;
 	ObservableListener<JPanel, RecipeCard> childListener;
 	ObservableListener<JPanel, RecipeCard> childListener;
@@ -41,11 +43,13 @@ public class SummaryPanel extends JPanel {
 	JTextField txtTitle;
 	JTextField txtTitle;
 	JTextArea txaDescription;
 	JTextArea txaDescription;
 	TagInputPanel panelTags;
 	TagInputPanel panelTags;
+	JSpinner spnServings;
+	JSpinner spnServingsToMake;
 	
 	
-	public SummaryPanel() {
+	public SummaryPanel(final ScaleFactor scale) {
 		controller = new ReplaceChildrenAction<>(RecipeCard::getElements, element -> {
 		controller = new ReplaceChildrenAction<>(RecipeCard::getElements, element -> {
 			JPanel wrapper = new JPanel(new VerticalLayout());
 			JPanel wrapper = new JPanel(new VerticalLayout());
-			wrapper.add(new ElementPanel(element));
+			wrapper.add(new ElementPanel(element, scale));
 			wrapper.add(new JSeparator());
 			wrapper.add(new JSeparator());
 			return wrapper;
 			return wrapper;
 		});
 		});
@@ -109,12 +113,14 @@ public class SummaryPanel extends JPanel {
 		gbc_lblServes.gridy = 0;
 		gbc_lblServes.gridy = 0;
 		panelServings.add(lblServes, gbc_lblServes);
 		panelServings.add(lblServes, gbc_lblServes);
 		
 		
-		JSpinner spnServings = new JSpinner(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1));
+		spnServings = new JSpinner(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1));
+		spnServingsToMake = new JSpinner(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1));
 		GridBagConstraints gbc_spnServings = new GridBagConstraints();
 		GridBagConstraints gbc_spnServings = new GridBagConstraints();
 		gbc_spnServings.fill = GridBagConstraints.HORIZONTAL;
 		gbc_spnServings.fill = GridBagConstraints.HORIZONTAL;
 		gbc_spnServings.gridx = 1;
 		gbc_spnServings.gridx = 1;
 		gbc_spnServings.gridy = 0;
 		gbc_spnServings.gridy = 0;
 		panelServings.add(spnServings, gbc_spnServings);
 		panelServings.add(spnServings, gbc_spnServings);
+		panelServings.add(spnServingsToMake, gbc_spnServings);
 		
 		
 		JPanel panelIngredients = new JPanel();
 		JPanel panelIngredients = new JPanel();
 		GridBagConstraints gbc_panelIngredients = new GridBagConstraints();
 		GridBagConstraints gbc_panelIngredients = new GridBagConstraints();
@@ -173,6 +179,13 @@ public class SummaryPanel extends JPanel {
 		servingsBinding = new SpinnerBinding<>(spnServings, 
 		servingsBinding = new SpinnerBinding<>(spnServings, 
 				RecipeCard::getServings, RecipeCard::setServings);
 				RecipeCard::getServings, RecipeCard::setServings);
 
 
+		servingsToMakeBinding = new SpinnerBinding<>(spnServingsToMake, 
+				ScaleFactor::getOutputServings, (obj, s) -> {
+					obj.setOutputServings(s);
+					ObserverDispatch.notifySubscribers(obj);
+				});
+		servingsToMakeBinding.setModel(scale);
+
 		descriptionBinding = new TextBinding<>(txaDescription,
 		descriptionBinding = new TextBinding<>(txaDescription,
 				RecipeCard::getDescription, RecipeCard::setDescription);
 				RecipeCard::getDescription, RecipeCard::setDescription);
 
 
@@ -184,8 +197,8 @@ public class SummaryPanel extends JPanel {
 				(c, v) -> controller.accept(c, v));
 				(c, v) -> controller.accept(c, v));
 	}
 	}
 	
 	
-	public SummaryPanel(final RecipeCard card) {
-		this();
+	public SummaryPanel(final RecipeCard card, final ScaleFactor scale) {
+		this(scale);
 		setModel(card);
 		setModel(card);
 	}
 	}
 	
 	
@@ -198,6 +211,16 @@ public class SummaryPanel extends JPanel {
 		childListener.setObserved(card);
 		childListener.setObserved(card);
 	}
 	}
 	
 	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		txtTitle.setEditable(enabled);
+		txaDescription.setEditable(enabled);
+		spnServings.setVisible(enabled);
+		spnServingsToMake.setValue(spnServings.getValue());
+		spnServingsToMake.setVisible(!enabled);
+	}
+	
 	@Override
 	@Override
 	public void removeNotify() {
 	public void removeNotify() {
 		super.removeNotify();
 		super.removeNotify();

+ 20 - 0
src/main/lombok/org/leumasjaffe/recipe/viewmodel/ScaleFactor.java

@@ -0,0 +1,20 @@
+package org.leumasjaffe.recipe.viewmodel;
+
+import org.leumasjaffe.observer.Observable;
+import org.leumasjaffe.recipe.model.RecipeCard;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.FieldDefaults;
+
+@FieldDefaults(level=AccessLevel.PRIVATE)
+public class ScaleFactor extends Observable.Instance {
+	@Setter RecipeCard model = null;
+	@Getter @Setter int outputServings;
+	
+	public double getScale() {
+		return (model == null || outputServings == 0) ? 1.0 :
+			outputServings / (double) model.getServings();
+	}
+}

+ 2 - 0
src/test/java/org/leumasjaffe/recipe/controller/AddPhaseActionTest.java

@@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.view.PhasePanel;
 import org.leumasjaffe.recipe.view.PhasePanel;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.InjectMocks;
 import org.mockito.InjectMocks;
 import org.mockito.Spy;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -19,6 +20,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 class AddPhaseActionTest {
 class AddPhaseActionTest {
 	
 	
 	@Spy Element model;
 	@Spy Element model;
+	@Spy ScaleFactor scale;
 	@Spy JPanel view;
 	@Spy JPanel view;
 	@InjectMocks AddPhaseAction action;
 	@InjectMocks AddPhaseAction action;
 	
 	

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/ElementPanelTest.java

@@ -16,6 +16,7 @@ import org.leumasjaffe.mock.MockObserverListener;
 import org.leumasjaffe.observer.ObservableListener;
 import org.leumasjaffe.observer.ObservableListener;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Phase;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.mockito.InjectMocks;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mock;
@@ -30,7 +31,7 @@ class ElementPanelTest extends SwingTestCase {
 	
 	
 	@Mock ObservableListener<CollatedDurationPanel, Element> durationListener;
 	@Mock ObservableListener<CollatedDurationPanel, Element> durationListener;
 	@Spy JPanel panelViewPort;
 	@Spy JPanel panelViewPort;
-	@InjectMocks ElementPanel panel = new ElementPanel();
+	@InjectMocks ElementPanel panel = new ElementPanel(new ScaleFactor());
 
 
 	@BeforeEach
 	@BeforeEach
 	void setUp() {
 	void setUp() {

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/IngredientPanelTest.java

@@ -10,6 +10,7 @@ import org.leumasjaffe.mock.MockObserverListener;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.Spy;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 
@@ -22,7 +23,7 @@ class IngredientPanelTest extends SwingTestCase {
 	@BeforeEach
 	@BeforeEach
 	void setUp() {
 	void setUp() {
 		stuff = new Ingredient("Onions", "Sliced", new Amount("100 g"));
 		stuff = new Ingredient("Onions", "Sliced", new Amount("100 g"));
-		panel = new IngredientPanel(stuff);
+		panel = new IngredientPanel(stuff, new ScaleFactor(), true);
 		listener.setObserved(stuff);
 		listener.setObserved(stuff);
 		// setObserved invokes our callback.
 		// setObserved invokes our callback.
 		clearInvocations(listener);
 		clearInvocations(listener);

+ 0 - 51
src/test/java/org/leumasjaffe/recipe/view/IngredientPreparationPanelTest.java

@@ -1,51 +0,0 @@
-package org.leumasjaffe.recipe.view;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.leumasjaffe.observer.ObserverDispatch;
-import org.leumasjaffe.recipe.model.Amount;
-import org.leumasjaffe.recipe.model.Ingredient;
-
-class IngredientPreparationPanelTest extends SwingTestCase {
-
-	Ingredient stuff;
-	IngredientPreparationPanel panel;
-	
-	@BeforeEach
-	void setUp() {
-		stuff = new Ingredient("Onions", "Sliced", new Amount("100 g"));
-		panel = new IngredientPreparationPanel(stuff);
-	}
-
-	@Test
-	void testFilledOutWithContent() {
-		assertEquals("Onions", panel.getTxtName().getText());
-		assertEquals("Sliced", panel.getTxtPreparation().getText());
-		assertEquals(new Amount("100 g"), panel.getTxtAmount().getValue());
-	}
-	
-	@Test
-	void testCannotEditContent() {
-		assertFalse(panel.getTxtName().isEditable());
-		assertFalse(panel.getTxtPreparation().isEditable());
-		assertFalse(panel.getTxtAmount().isEditable());
-	}
-
-	@Test
-	void testIsSubscribedToUpdates() {
-		stuff.setName("Bacon");
-		stuff.setPreparation("Cut into Lardons");
-		stuff.setAmount(new Amount("50 g"));
-		
-		assertEquals("Onions", panel.getTxtName().getText());
-		
-		ObserverDispatch.notifySubscribers(stuff);
-		
-		assertEquals("Bacon", panel.getTxtName().getText());
-		assertEquals("Cut into Lardons", panel.getTxtPreparation().getText());
-		assertEquals(new Amount("50 g"), panel.getTxtAmount().getValue());
-	}
-
-}

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/PhasePanelIT.java

@@ -14,6 +14,7 @@ import org.leumasjaffe.mock.MockObserverListener;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Step;
 import org.leumasjaffe.recipe.model.Step;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.Spy;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 
@@ -31,7 +32,7 @@ class PhasePanelIT extends SwingTestCase {
 		steps = new ArrayList<>(Arrays.asList(step));
 		steps = new ArrayList<>(Arrays.asList(step));
 		doReturn(steps).when(stuff).getCooking();
 		doReturn(steps).when(stuff).getCooking();
 				
 				
-		panel = new PhasePanel(stuff);
+		panel = new PhasePanel(stuff, new ScaleFactor());
 		listener.setObserved(stuff);
 		listener.setObserved(stuff);
 		// setObserved invokes our callback.
 		// setObserved invokes our callback.
 		clearInvocations(listener);
 		clearInvocations(listener);

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/PhasePanelTest.java

@@ -14,6 +14,7 @@ import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Preparation;
 import org.leumasjaffe.recipe.model.Preparation;
 import org.leumasjaffe.recipe.model.Rest;
 import org.leumasjaffe.recipe.model.Rest;
 import org.leumasjaffe.recipe.model.Step;
 import org.leumasjaffe.recipe.model.Step;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.Mock;
 import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -35,7 +36,7 @@ class PhasePanelTest extends SwingTestCase {
 		doReturn(Arrays.asList(stub)).when(stuff).getCooking();
 		doReturn(Arrays.asList(stub)).when(stuff).getCooking();
 		doReturn(prep).when(stuff).getPreparation();
 		doReturn(prep).when(stuff).getPreparation();
 		doReturn(rest).when(stuff).getRest();
 		doReturn(rest).when(stuff).getRest();
-		panel = new PhasePanel(stuff);
+		panel = new PhasePanel(stuff, new ScaleFactor());
 		
 		
 		listener.setObserved(stuff);
 		listener.setObserved(stuff);
 		// setObserved() calls update
 		// setObserved() calls update

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/PreparationPanelTest.java

@@ -13,6 +13,7 @@ import org.leumasjaffe.recipe.controller.ReplaceChildrenAction;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Phase;
 import org.leumasjaffe.recipe.model.Preparation;
 import org.leumasjaffe.recipe.model.Preparation;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.InjectMocks;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -24,7 +25,7 @@ class PreparationPanelTest extends SwingTestCase {
 	@Mock ReplaceChildrenAction<Preparation, Ingredient> controller;
 	@Mock ReplaceChildrenAction<Preparation, Ingredient> controller;
 	Preparation stuff = new Preparation();
 	Preparation stuff = new Preparation();
 	@Mock Phase parent;
 	@Mock Phase parent;
-	@InjectMocks PreparationPanel panel = new PreparationPanel();
+	@InjectMocks PreparationPanel panel = new PreparationPanel(new ScaleFactor());
 	
 	
 	@BeforeEach
 	@BeforeEach
 	void setUp() {
 	void setUp() {

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/RecipeCardPanelTest.java

@@ -14,6 +14,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.RecipeCard;
 import org.leumasjaffe.recipe.model.RecipeCard;
 import org.leumasjaffe.recipe.view.summary.SummaryPanel;
 import org.leumasjaffe.recipe.view.summary.SummaryPanel;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.InjectMocks;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.Spy;
@@ -27,7 +28,7 @@ class RecipeCardPanelTest {
 
 
 	@Mock SummaryPanel summaryPanel;
 	@Mock SummaryPanel summaryPanel;
 	@Spy AutoGrowPanel<ElementPanel, Element> panelElements =
 	@Spy AutoGrowPanel<ElementPanel, Element> panelElements =
-			new AutoGrowPanel<>(Element::new, ElementPanel::new);
+			new AutoGrowPanel<>(Element::new, e -> new ElementPanel(e, new ScaleFactor()));
 	
 	
 	@InjectMocks RecipeCardPanel panel = new RecipeCardPanel();
 	@InjectMocks RecipeCardPanel panel = new RecipeCardPanel();
 	
 	

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/StepPanelIT.java

@@ -15,6 +15,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Step;
 import org.leumasjaffe.recipe.model.Step;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.Spy;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 
@@ -25,7 +26,7 @@ class StepPanelIT extends SwingTestCase {
 	List<Ingredient> ingredients;
 	List<Ingredient> ingredients;
 	Step stuff;
 	Step stuff;
 	
 	
-	StepPanel panel = new StepPanel();
+	StepPanel panel = new StepPanel(new ScaleFactor());
 
 
 	@BeforeEach
 	@BeforeEach
 	void setUp() {
 	void setUp() {

+ 3 - 2
src/test/java/org/leumasjaffe/recipe/view/StepPanelTest.java

@@ -18,6 +18,7 @@ import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Duration;
 import org.leumasjaffe.recipe.model.Duration;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Step;
 import org.leumasjaffe.recipe.model.Step;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.InjectMocks;
 import org.mockito.InjectMocks;
 import org.mockito.Spy;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -29,8 +30,8 @@ class StepPanelTest extends SwingTestCase {
 	
 	
 	@Spy JLabel lblIndex;
 	@Spy JLabel lblIndex;
 	@Spy AutoGrowPanel<IngredientPanel, Ingredient> panelIngredients =
 	@Spy AutoGrowPanel<IngredientPanel, Ingredient> panelIngredients =
-			new AutoGrowPanel<>(Ingredient::new, IngredientPanel::new);
-	@InjectMocks StepPanel panel = new StepPanel();
+			new AutoGrowPanel<>(Ingredient::new, ing -> new IngredientPanel(ing, new ScaleFactor(), true));
+	@InjectMocks StepPanel panel = new StepPanel(new ScaleFactor());
 
 
 	@BeforeEach
 	@BeforeEach
 	void setUp() {
 	void setUp() {

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/FileMenuTest.java

@@ -1,4 +1,4 @@
-package org.leumasjaffe.recipe.view;
+package org.leumasjaffe.recipe.view.menu;
 
 
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -13,6 +13,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.leumasjaffe.recipe.controller.FileController;
 import org.leumasjaffe.recipe.controller.FileController;
+import org.leumasjaffe.recipe.view.SwingTestCase;
 import org.mockito.Mock;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/summary/ElementPanelTest.java

@@ -14,6 +14,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.view.SwingTestCase;
 import org.leumasjaffe.recipe.view.SwingTestCase;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.mockito.Spy;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -32,7 +33,7 @@ class ElementPanelTest extends SwingTestCase {
 				new Ingredient("Garlic", "", new Amount("2 tsp"))))
 				new Ingredient("Garlic", "", new Amount("2 tsp"))))
 		.when(stuff).getIngredients();
 		.when(stuff).getIngredients();
 		
 		
-		panel = new ElementPanel(stuff);
+		panel = new ElementPanel(stuff, new ScaleFactor());
 	}
 	}
 
 
 	@Test
 	@Test

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/summary/IngredientPanelTest.java

@@ -8,6 +8,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Amount;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.model.Ingredient;
 import org.leumasjaffe.recipe.view.SwingTestCase;
 import org.leumasjaffe.recipe.view.SwingTestCase;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 
 
 class IngredientPanelTest extends SwingTestCase {
 class IngredientPanelTest extends SwingTestCase {
 
 
@@ -17,7 +18,7 @@ class IngredientPanelTest extends SwingTestCase {
 	@BeforeEach
 	@BeforeEach
 	void setUp() {
 	void setUp() {
 		stuff = new Ingredient("Onions", "Sliced", new Amount("100 g"));
 		stuff = new Ingredient("Onions", "Sliced", new Amount("100 g"));
-		panel = new IngredientPanel(stuff);
+		panel = new IngredientPanel(stuff, new ScaleFactor());
 	}
 	}
 
 
 	@Test
 	@Test

+ 2 - 1
src/test/java/org/leumasjaffe/recipe/view/summary/SummaryPanelTest.java

@@ -8,6 +8,7 @@ import org.leumasjaffe.observer.ObserverDispatch;
 import org.leumasjaffe.recipe.controller.ReplaceChildrenAction;
 import org.leumasjaffe.recipe.controller.ReplaceChildrenAction;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.Element;
 import org.leumasjaffe.recipe.model.RecipeCard;
 import org.leumasjaffe.recipe.model.RecipeCard;
+import org.leumasjaffe.recipe.viewmodel.ScaleFactor;
 import org.mockito.InjectMocks;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -17,7 +18,7 @@ class SummaryPanelTest {
 	
 	
 	RecipeCard card = new RecipeCard();
 	RecipeCard card = new RecipeCard();
 	@Mock ReplaceChildrenAction<RecipeCard, Element> controller;
 	@Mock ReplaceChildrenAction<RecipeCard, Element> controller;
-	@InjectMocks SummaryPanel panel = new SummaryPanel(card);
+	@InjectMocks SummaryPanel panel = new SummaryPanel(card, new ScaleFactor());
 
 
 	@Test
 	@Test
 	void testUpdateToCardInvokesController() {
 	void testUpdateToCardInvokesController() {