Index: trunk/src/org/expeditee/auth/io/EncryptedExpReader.java
===================================================================
--- trunk/src/org/expeditee/auth/io/EncryptedExpReader.java	(revision 1200)
+++ trunk/src/org/expeditee/auth/io/EncryptedExpReader.java	(revision 1200)
@@ -0,0 +1,113 @@
+package org.expeditee.auth.io;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+import org.expeditee.auth.Authenticator;
+import org.expeditee.gui.Frame;
+import org.expeditee.io.ExpReader;
+import org.ngikm.Util.ThrowingFunction;
+import org.ngikm.cryptography.CryptographyConstants;
+
+public class EncryptedExpReader extends ExpReader implements CryptographyConstants {
+	static final String ENCRYPTED_EXP_FLAG = "EncryptedExp";
+	private List<SecretKey> keys;
+	
+	public EncryptedExpReader(final String frameName) throws UnsupportedEncodingException {
+		super(frameName);
+	}
+	
+	public static boolean isEncryptedExpediteeFile(final String path) throws IOException {
+		final BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
+		final String firstLine = in.readLine();
+		in.close();
+		return firstLine.startsWith(ENCRYPTED_EXP_FLAG);
+	}
+	
+	@Override
+	public Frame readFrame(final String fullPath) throws IOException {
+		final Reader in = new InputStreamReader(new FileInputStream(fullPath), "UTF-8");
+		return readFrame(new EncryptedLineReader(new BufferedReader(in)));
+	}
+	
+	public class EncryptedLineReader extends BufferedReader {
+
+		public EncryptedLineReader(final Reader in) {
+			super(in);
+		}
+		                                                  
+		@Override
+		public String readLine() throws IOException {
+			final String line = super.readLine();	
+			if (line.isEmpty()) { return ""; }
+			if (line.startsWith(ENCRYPTED_EXP_FLAG)) {
+				try {
+					// resolve labels to secret keys
+					final List<String> labels = Arrays.asList(line.split(" ")).stream().skip(1).collect(Collectors.toList());
+					final Authenticator auth = new Authenticator();
+					final ThrowingFunction<String, SecretKey, Exception> worker = new ThrowingFunction<String, SecretKey, Exception>() {
+						@Override
+						public SecretKey applyThrows(final String label) throws Exception {
+							return auth.getSecretKey(label, System.getProperty("password"));
+						}
+					};
+					keys = labels.stream().map(worker).collect(Collectors.toList());
+					return readLine();
+				} catch (final Exception e) {
+					return "";
+				}
+			}
+			
+			if (keys == null) {
+				// We do not have permission to view this frame.
+				return "";
+			}
+			
+			// use keys to decrypt
+			byte[] toDecrypt = Base64.getDecoder().decode(line);
+			for (final SecretKey key: keys) {
+				toDecrypt = DecryptSymmetric(toDecrypt, key);
+			}
+			final String result = new String(toDecrypt);
+			if (result.startsWith("Z")) { return result.trim(); }
+			else { return result; }
+		}		
+	}
+	
+	private static byte[] DecryptSymmetric(final byte[] toDecrypt, final SecretKey key) {
+		try {
+			final Cipher cipher = Cipher.getInstance(SymmetricAlgorithm + SymmetricAlgorithmParameters);
+			cipher.init(Cipher.DECRYPT_MODE, key);
+			final byte[] decryptedBytes = cipher.doFinal(toDecrypt);
+			int indexOfZero = decryptedBytes.length - 1;
+			for (int i = decryptedBytes.length - 1; i >= 0; i--) {
+				if (decryptedBytes[i] != (byte) 0) {
+					indexOfZero = i + 1;
+					break;
+				}
+			}
+			if (indexOfZero < 0) { return decryptedBytes; }
+			else { return Arrays.copyOf(decryptedBytes, indexOfZero); }
+		} catch (final NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+				| IllegalBlockSizeException | BadPaddingException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+}
Index: trunk/src/org/expeditee/auth/io/EncryptedExpWriter.java
===================================================================
--- trunk/src/org/expeditee/auth/io/EncryptedExpWriter.java	(revision 1200)
+++ trunk/src/org/expeditee/auth/io/EncryptedExpWriter.java	(revision 1200)
@@ -0,0 +1,94 @@
+package org.expeditee.auth.io;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.CertificateException;
+import java.util.Arrays;
+import java.util.Base64;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.expeditee.auth.Authenticator;
+import org.expeditee.io.ExpWriter;
+import org.expeditee.settings.UserSettings;
+import org.ngikm.cryptography.CryptographyConstants;
+
+public class EncryptedExpWriter extends ExpWriter implements CryptographyConstants {
+	private SecretKey key;
+	private static final String nl = "\n";
+	
+	public EncryptedExpWriter() throws IOException {
+		try {
+			final Authenticator auth = new Authenticator();
+			SecretKey key = auth.getSecretKey(UserSettings.UserName.get(), System.getProperty("password"));
+			if (key == null) {
+				final byte[] keyBytes = pad(UserSettings.UserName.get().getBytes("UTF-8"));
+				key = new SecretKeySpec(keyBytes, SymmetricAlgorithm);
+				auth.putKey(UserSettings.UserName.get(), System.getProperty("password"), key);
+			}
+			this.key = key;			
+		} catch (final KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | UnrecoverableEntryException e) {
+			e.printStackTrace();
+		}
+		
+	}
+
+	@Override
+	protected void preOutputFrame() {
+		try {
+			final String line = EncryptedExpReader.ENCRYPTED_EXP_FLAG + " " + UserSettings.UserName.get() + nl;
+			_writer.write(line);
+			_stringWriter.append(line);
+		} catch (final IOException e) { 
+			e.printStackTrace();
+		}
+	}
+	
+	@Override
+	protected void writeLine(String line) throws IOException {
+		// do not write empty lines
+		if (line == null) { return; }
+		
+		// prepare line to write out
+		final byte[] encrypted = EncryptSymmetric(line.getBytes(), key);
+		final String toWrite = Base64.getEncoder().encodeToString(encrypted) + nl;
+		
+		// output
+		_writer.write(toWrite);
+		_stringWriter.append(toWrite);
+	}
+	
+	private byte[] pad(final byte[] bytes) {
+		int c = 16;
+		while (c - bytes.length < 0) { c *= 2; }
+		return Arrays.copyOf(bytes, c);
+	}
+	
+	private static byte[] EncryptSymmetric(final byte[] toEncrypt, final SecretKey key) {
+		// toEncrypt = Base64.getDecoder().decode(toEncrypt);
+		try {
+			final Cipher cipher = Cipher.getInstance(SymmetricAlgorithm + SymmetricAlgorithmParameters);
+			cipher.init(Cipher.ENCRYPT_MODE, key);
+			//could use modulus
+			final int length = (int) ((Math.ceil(toEncrypt.length / 16f)) * 16);
+			final byte[] toEncryptSizeAdjusted = Arrays.copyOf(toEncrypt, length);
+			//System.err.println("(" + toEncryptSizeAdjusted.length + ")" + "Before Encryption Data: "
+			//		+ Arrays.toString(toEncryptSizeAdjusted));
+			final byte[] result = cipher.doFinal(toEncryptSizeAdjusted);
+			//System.err.println("(" + result.length + ")" + "Encrypted Data: " + Arrays.toString(result));
+			return result;
+		} catch (final NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+				| IllegalBlockSizeException | BadPaddingException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+}
Index: trunk/src/org/expeditee/gui/AttributeUtils.java
===================================================================
--- trunk/src/org/expeditee/gui/AttributeUtils.java	(revision 1199)
+++ trunk/src/org/expeditee/gui/AttributeUtils.java	(revision 1200)
@@ -283,4 +283,6 @@
 			_Attrib.put("Width",                Item.class.getMethod("getWidthToSave"),
 			                                    Item.class.getMethod("setWidth", pIntO));
+			_Attrib.put("MinWidth", 			Item.class.getMethod("getMinWidthToSave"), 
+												Item.class.getMethod("setMinWidth", pIntO));
 			_Attrib.put("X",                    null,
 			                                    Item.class.getMethod("setX", pFloat));
@@ -307,4 +309,6 @@
 			_Attrib.put("LetterSpacing",		Text.class.getMethod("getLetterSpacing"), 
 												Text.class.getMethod("setLetterSpacing", pFloat));
+			_Attrib.put("Mask", 				Text.class.getMethod("getMask"), 
+												Text.class.getMethod("setMask", pIntO));
 			
 			// Aliases for attribute setting
@@ -340,7 +344,6 @@
 			_Attrib.alias("j",                  "justification");
 			_Attrib.alias("w",                  "width");
+			_Attrib.alias("mw", 				"minwidth");	
 			_Attrib.alias("as",                 "autostamp");
-			
-
 		} catch (SecurityException e) {
 			// TODO Auto-generated catch block
Index: trunk/src/org/expeditee/gui/Frame.java
===================================================================
--- trunk/src/org/expeditee/gui/Frame.java	(revision 1199)
+++ trunk/src/org/expeditee/gui/Frame.java	(revision 1200)
@@ -126,8 +126,6 @@
 	private Stack<History> _redo = new Stack<History>();
 
-	// basically just a list of smaller objects?
-	// maybe a hashtable (id -> item?)
-	// Note: Needs to be able to be iterated through (for painting)
 	private List<Item> _body = new ArrayList<Item>();
+	private List<Item> _bodyHiddenDueToPermissions = new ArrayList<Item>();
 
 	// for drawing purposes
@@ -964,6 +962,5 @@
 	}
 
-	public void removeItem(Item item)
-	{
+	public void removeItem(Item item) {
 		removeItem(item, true);
 	}
@@ -2607,4 +2604,13 @@
 		return _observers != null && _observers.size() > 0;
 	}
+	
+	public List<Item> getBodyItemsWithInsufficientPermissions() {
+		return _bodyHiddenDueToPermissions;
+	}
+	
+	public void moveItemToBodyHiddenDueToPermission(final Item i) {
+		_body.remove(i);
+		_bodyHiddenDueToPermissions.add(i);
+	}
 
 	public Collection<? extends Item> getInteractableItems()
Index: trunk/src/org/expeditee/gui/FrameIO.java
===================================================================
--- trunk/src/org/expeditee/gui/FrameIO.java	(revision 1199)
+++ trunk/src/org/expeditee/gui/FrameIO.java	(revision 1200)
@@ -40,4 +40,6 @@
 import org.expeditee.actions.Actions;
 import org.expeditee.agents.ExistingFramesetException;
+import org.expeditee.auth.io.EncryptedExpReader;
+import org.expeditee.auth.io.EncryptedExpWriter;
 import org.expeditee.io.Conversion;
 import org.expeditee.io.ExpReader;
@@ -465,5 +467,9 @@
 
 			if (fullPath.endsWith(ExpReader.EXTENTION)) {
-				reader = new ExpReader(frameName);
+				if (EncryptedExpReader.isEncryptedExpediteeFile(fullPath)) {
+					reader = new EncryptedExpReader(frameName);
+				} else {
+					reader = new ExpReader(frameName);
+				}
 			} else {
 				reader = new KMSReader();
@@ -952,5 +958,5 @@
 		/* Dont save the frame if it has the noSave tag */
 		if (toSave.hasAnnotation("nosave")) {
-			Actions.PerformActionCatchErrors(toSave, null, "Restore");
+			Actions.LegacyPerformActionCatchErrors(toSave, null, "Restore");
 			return "";
 		}
@@ -964,5 +970,5 @@
 		/* Format the frame if it has the autoFormat tag */
 		if (toSave.hasAnnotation("autoformat")) {
-			Actions.PerformActionCatchErrors(toSave, null, "Format");
+			Actions.LegacyPerformActionCatchErrors(toSave, null, "Format");
 		}
 
@@ -988,5 +994,6 @@
 			// if its a new frame or an existing Exp frame...
 			if (fullPath == null || fullPath.endsWith(ExpReader.EXTENTION)) {
-				writer = new ExpWriter();
+				//writer = new ExpWriter();
+				writer = new ExpWriter();//new EncryptedExpWriter();
 				savedVersion = ExpReader.getVersion(fullPath);
 			} else {
@@ -1198,5 +1205,10 @@
 	public static Frame LoadProfile(String userName)
 	{
-		return LoadFrame(userName + "1");
+		final String profilesLoc = System.getProperty("profiles.loc");
+		if (profilesLoc != null) {
+			return LoadFrame(userName + "1", profilesLoc);
+		} else {
+			return LoadFrame(userName + "1");
+		}
 	}
 
Index: trunk/src/org/expeditee/gui/FrameUtils.java
===================================================================
--- trunk/src/org/expeditee/gui/FrameUtils.java	(revision 1199)
+++ trunk/src/org/expeditee/gui/FrameUtils.java	(revision 1200)
@@ -1288,4 +1288,8 @@
 						}
 					}
+					
+					if (i.getData() != null && !i.getData().isEmpty() && i.getText().isEmpty()) {
+						System.err.println();
+					}
 
 					if (i.contains(new Point(x, y))){
@@ -1867,5 +1871,5 @@
 				}
 			}
-			if (lastEdited != LastEdited && LastEdited.getText().length() == 0) {
+			if (lastEdited != LastEdited && LastEdited.getText().length() == 0 && LastEdited.getMinWidth() == null) {
 				parent.removeItem(LastEdited);
 			}
Index: trunk/src/org/expeditee/io/DefaultFrameReader.java
===================================================================
--- trunk/src/org/expeditee/io/DefaultFrameReader.java	(revision 1199)
+++ trunk/src/org/expeditee/io/DefaultFrameReader.java	(revision 1200)
@@ -96,4 +96,5 @@
 					pColor));
 
+			// Note: As of 26/11/18 there are no unused letter item tags.  Use other characters.
 			_ItemTags.put('S', Item.class.getMethod("setID", pInt));
 			_ItemTags.put('s', Item.class.getMethod("setDateCreated", pString));
@@ -150,4 +151,5 @@
 			_ItemTags.put('m', Text.class.getMethod("setInitialSpacing", pFloat));
 			_ItemTags.put('w', Text.class.getMethod("setWidth", pIntO));
+			_ItemTags.put('M', Text.class.getMethod("setMinWidth", pIntO));
 			_ItemTags.put('k', Text.class.getMethod("setJustification", pJustification));
 			_ItemTags.put('r', Text.class.getMethod("setAutoWrap", pBool));
@@ -161,4 +163,6 @@
 			
 			_ItemTags.put('p', Item.class.getMethod("setPermission", pPermission));
+			
+			_ItemTags.put('O', Text.class.getMethod("setMask", pIntO));
 			
 			// Lines and constraints are created differently
Index: trunk/src/org/expeditee/io/DefaultFrameWriter.java
===================================================================
--- trunk/src/org/expeditee/io/DefaultFrameWriter.java	(revision 1199)
+++ trunk/src/org/expeditee/io/DefaultFrameWriter.java	(revision 1200)
@@ -33,5 +33,4 @@
 import org.expeditee.agents.WriteTree;
 import org.expeditee.gio.EcosystemManager;
-import org.expeditee.gui.Browser;
 import org.expeditee.gui.Frame;
 import org.expeditee.gui.FrameIO;
@@ -80,4 +79,5 @@
 			_FrameTags.put('B', Frame.class.getMethod("getBackgroundColor"));
 			
+			// Note: As of 26/11/18 there are no unused letter item tags.  Use other characters.
 			_ItemTags.put('S', Item.class.getMethod("getTypeAndID"));
 			_ItemTags.put('s', Item.class.getMethod("getDateCreated"));
@@ -120,4 +120,6 @@
 			_ItemTags.put('f', Text.class.getMethod("getFont"));
 			_ItemTags.put('t', Text.class.getMethod("getSpacing"));
+			
+			_ItemTags.put('O', Text.class.getMethod("getMask"));
 
 			// TODO set a boolean flag to indicate that the text is a formula
@@ -129,4 +131,5 @@
 			_ItemTags.put('m', Text.class.getMethod("getInitialSpacing"));
 			_ItemTags.put('w', Text.class.getMethod("getWidthToSave"));
+			_ItemTags.put('M', Text.class.getMethod("getMinWidthToSave"));
 			_ItemTags.put('k', Text.class.getMethod("getJustification"));
 			_ItemTags.put('r', Text.class.getMethod("getAutoWrapToSave"));
Index: trunk/src/org/expeditee/io/ExpReader.java
===================================================================
--- trunk/src/org/expeditee/io/ExpReader.java	(revision 1199)
+++ trunk/src/org/expeditee/io/ExpReader.java	(revision 1200)
@@ -68,6 +68,10 @@
 	 */
 	protected static boolean isValidLine(String s) {
-		return s.length() >= 2 && s.charAt(1) == ' '
-				&& (Character.isLetter(s.charAt(0)) || s.charAt(0) == '[' || s.charAt(0) == ']' || s.charAt(0) == '^' || s.charAt(0) == '_');
+		// Previously lines in a .exp file had to start with a letter (A-Z, a-z).  This allowed for an efficient check for valid lines.
+		// As we started to run out of spare letters to use for properties, we wished to use the full range of characters.  But we did not
+		// wish to loose the efficiency of the Character.isLetter check.  In order to maintain as much of this efficiency as possible, but
+		// allow for all characters, we take advantage of how || is evaluated:
+		// 		if the check for Character.isLetter passes, then the more complex map lookup operation does not take place.
+		return s.length() >= 2 && (Character.isLetter(s.charAt(0)) || _ItemTags.keySet().contains(s.charAt(0)));
 	}
 
@@ -117,5 +121,5 @@
 							break;
 						}
-						_linePoints.put(currentItem.getID(), currentItem);
+						_linePoints.put(currentItem.getID(), currentItem);						
 						newFrame.addItem(currentItem);
 					} else if (currentItem != null && actionShouldBeDelayed(getTag(next))) { 
@@ -123,4 +127,11 @@
 					} else if (currentItem != null) {
 						processBodyLine(currentItem, next);
+//						final boolean hasSpecifiedPermission = currentItem.getPermission() != null;
+//						final boolean hasSpecifiedOwner = currentItem.getOwner() != null;
+//						if (hasSpecifiedPermission && hasSpecifiedOwner && currentItem.getPermission().getPermission(currentItem.getOwner()) == UserAppliedPermission.denied) {
+//							newFrame.removeItem(currentItem);
+//							newFrame.addItemHidden(currentItem);
+//							continue;
+//						}
 					} else {
 						System.err.println("Error while reading in frame (ExpReader): Found body line but no current item to apply it to.");
@@ -236,11 +247,12 @@
 		Character tag = getTag(line);
 		String value = getValue(line);
-
+		
 		Method toRun = _ItemTags.get(tag);
+				
 		if (toRun == null) {
 			System.out.println("Error accessing tag method: " + tag);
 		}
+				
 		Object[] vals = Conversion.Convert(toRun, value);
-
 		try {
 			if (vals != null) {
@@ -322,8 +334,10 @@
 					String value = getValue(next);
 					if (tag.equals('V')) {
+						reader.close();
 						return Integer.parseInt(value);
 					}
 				}
 			}
+			reader.close();
 		} catch (Exception e) {
 		}
Index: trunk/src/org/expeditee/io/ExpWriter.java
===================================================================
--- trunk/src/org/expeditee/io/ExpWriter.java	(revision 1199)
+++ trunk/src/org/expeditee/io/ExpWriter.java	(revision 1200)
@@ -47,5 +47,5 @@
 public class ExpWriter extends DefaultFrameWriter {
 
-	private ProxyWriter _writer = null;
+	protected ProxyWriter _writer = null;
 
 	protected StringBuilder _stringWriter = null;
@@ -104,4 +104,6 @@
 		if (_writer == null)
 			return;
+		
+		preOutputFrame();
 
 		writeHeader(frame);
@@ -109,4 +111,9 @@
 		// write each item in the frame
 		for (Item i : frame.getItemsToSave()) {
+			assert (!(i instanceof Line));
+			writeItem(i);
+		}
+		
+		for (final Item i: frame.getBodyItemsWithInsufficientPermissions()) {
 			assert (!(i instanceof Line));
 			writeItem(i);
@@ -122,4 +129,7 @@
 
 		return;
+	}
+
+	protected void preOutputFrame() {
 	}
 
Index: trunk/src/org/expeditee/items/Item.java
===================================================================
--- trunk/src/org/expeditee/items/Item.java	(revision 1199)
+++ trunk/src/org/expeditee/items/Item.java	(revision 1200)
@@ -50,5 +50,4 @@
 import org.expeditee.gui.DisplayController;
 import org.expeditee.gui.Frame;
-import org.expeditee.gui.FrameGraphics;
 import org.expeditee.gui.FrameIO;
 import org.expeditee.gui.FrameUtils;
@@ -59,4 +58,5 @@
 import org.expeditee.io.Conversion;
 import org.expeditee.settings.UserSettings;
+import org.expeditee.settings.legacy.LegacyFeatures;
 import org.expeditee.simple.Context;
 import org.expeditee.stats.AgentStats;
@@ -275,5 +275,5 @@
 
 	private List<String> _data = null;
-
+	 
 	private String _formula = null;
 
@@ -309,4 +309,8 @@
 
 	protected boolean _filled = true;
+	
+	// NGIKM Attributes
+	private List<String> _ngikmLabels = new ArrayList<String>();
+	private List<String> _ngikmKeys = new ArrayList<String>();
 
 	/** Just calls source.duplicateOnto(dest). */
@@ -380,4 +384,7 @@
 
 		dest._visible = this._visible;
+		
+		dest._ngikmKeys = new ArrayList<String>(this._ngikmKeys);
+		dest._ngikmLabels = new ArrayList<String>(this._ngikmLabels);
 
 		Frame parent = DisplayController.getCurrentFrame();
@@ -494,4 +501,7 @@
 	{
 		_permissionPair = permissionPair;
+		if (_permissionPair.getPermission(_owner) == UserAppliedPermission.denied) {
+			this.getParent().moveItemToBodyHiddenDueToPermission(this);
+		}
 	}
 	
@@ -1155,7 +1165,15 @@
 		return getWidth();
 	}
+	
+	public Integer getMinWidthToSave() {
+		return getMinWidth();
+	}
 
 	public Integer getWidth()
 	{
+		return null;
+	}
+	
+	public Integer getMinWidth() {
 		return null;
 	}
@@ -1458,6 +1476,11 @@
 
 		for (String s : getAction()) {
-			Object returnValue = Actions.PerformActionCatchErrors(sourceFrame,
-					sourceItem, s);
+			Object returnValue;
+			if (LegacyFeatures.UseLegacyActionArgumentMapping.get()) { 
+				returnValue = Actions.LegacyPerformActionCatchErrors(sourceFrame, sourceItem, s);
+			} else {
+				returnValue = Actions.PerformActionCatchErrors(sourceFrame, sourceItem, s, this);
+			}
+			
 			if (returnValue != null) {
 				FreeItems.getInstance().clear();
@@ -2254,4 +2277,8 @@
 		throw new UnsupportedOperationException("Item type does not support width attribute!");
 	}
+	
+	public void setMinWidth(final Integer width) throws UnsupportedOperationException {
+		throw new UnsupportedOperationException("Item type does not support minwidth attribute.");
+	}
 
 	public void setRightMargin(int i, boolean fixWidth)
@@ -2283,4 +2310,7 @@
 	 */
 	public void setY(float newY) {
+		if (newY == 265) {
+			System.err.println("Item::setY::setting y to 265");
+		}
 		setPosition(getX(), newY);
 	}
Index: trunk/src/org/expeditee/items/PermissionPair.java
===================================================================
--- trunk/src/org/expeditee/items/PermissionPair.java	(revision 1199)
+++ trunk/src/org/expeditee/items/PermissionPair.java	(revision 1200)
@@ -35,5 +35,5 @@
 			if (permissionCode.length() != 0) {
 
-				if (permissionCode.length() == 1) {
+				if (permissionCode.length() == 1 || (permissionCode.length() == 2 && permissionCode.startsWith("-"))) {
 					// replicate it to cover ifOwner/ifNotOwner
 					permissionCode += permissionCode;
@@ -50,8 +50,4 @@
 				ownerPermission = UserAppliedPermission.getPermission(ownerPermissionCode, defaultPermission);
 				notOwnerPermission = UserAppliedPermission.getPermission(notOwnerPermissionCode, defaultPermission);
-				//ownerPermission = UserAppliedPermission.getPermission(permissionCode.substring(0, 1),
-				//		defaultPermission);
-				//notOwnerPermission = UserAppliedPermission.getPermission(permissionCode.substring(1, 2),
-				//		defaultPermission);
 			}
 		}
Index: trunk/src/org/expeditee/items/Text.java
===================================================================
--- trunk/src/org/expeditee/items/Text.java	(revision 1199)
+++ trunk/src/org/expeditee/items/Text.java	(revision 1200)
@@ -136,4 +136,6 @@
 	 */
 	private Integer _maxWidth = -Integer.MAX_VALUE;
+	
+	private Integer _minWidth = -Integer.MAX_VALUE;
 
 	private Justification _justification = Justification.left;
@@ -172,10 +174,13 @@
 
 	// text is broken up into lines
-	private StringBuffer _text = new StringBuffer();
-
-	private List<TextLayout> _textLayouts = new LinkedList<TextLayout>();
+	protected StringBuffer _text = new StringBuffer();
+
+	protected List<TextLayout> _textLayouts = new LinkedList<TextLayout>();
 
 	// The font to display this text in
 	private Font _font;
+	
+	// The optional mask character to us in place of the text's content.
+	private Integer _mask = null;
 
 	protected static void InitFontFamily(File fontFamilyDir)
@@ -373,15 +378,4 @@
 
 	/**
-<<<<<<< .mine
-	 * Sets the maximum width of this Text item when justification is used.
-	 * passing in 0 or -1 means there is no maximum width
-||||||| .r1094
-	 * Sets the maximum width of this Text item when justifcation is used.
-	 * passing in 0 or -1 means there is no maximum width
-=======
-	 * Sets the maximum width of this Text item when justifcation is used. passing
-	 * in 0 or -1 means there is no maximum width
->>>>>>> .r1100
-	 * 
 	 * @param width
 	 *            The maximum width of this item when justification is applied to
@@ -399,4 +393,19 @@
 
 		_maxWidth = width;
+		rebuild(true);
+		invalidateAll();
+	}
+	
+	@Override
+	public void setMinWidth(final Integer width) {
+		invalidateAll();
+		
+		if (width == null) {
+			setJustification(Justification.left);
+			setRightMargin(DisplayController.getFramePaintArea().getWidth(), false);
+			return;
+		}
+		
+		_minWidth = width;
 		rebuild(true);
 		invalidateAll();
@@ -433,4 +442,18 @@
 
 		return Math.abs(_maxWidth);
+	}
+	
+	public Integer getMinWidth() {
+		if (_minWidth == null || _minWidth <= 0) {
+			return null;
+		}
+		return _minWidth;
+	}
+	
+	public Integer getAbsoluteMinWidth() {
+		if (_minWidth == null) {
+			return Integer.MAX_VALUE;
+		}
+		return Math.abs(_minWidth);
 	}
 
@@ -1035,23 +1058,36 @@
 			lineIndex = newLine;
 		}
-
-		// move the cursor to the new location
-		float[] caret = current.getCaretInfo(hit);
-		float y = getLineDrop(current) * lineIndex;
-		y = getY() + y + caret[1];
-
-		float x = getX() + caret[0] + getJustOffset(current);
-		x = Math.min(x, (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth()));
-
-		invalidateAll();
 		
-//		System.err.println("Mouse location: " + mouseX + "," + mouseY);
-//		System.err.println("Caret location: " + caret[0] + "," + caret[1]);
-
-//		System.err.println(text);
-//		System.err.println("X delta: " + (x - getX()));
-		final Point newCursor = new Point(Math.round(x), Math.round(y));
-//		System.err.println("Moving cursor to: " + newCursor);
-		return newCursor;
+		if (this.getMask() != null) {
+			// If we are working with a mask than the positioning of the mouse is simplified. (as character length is constant)
+			String content = "";
+			for (int i = 0; i < this._text.length(); i++) {
+				content += (char) this.getMask().intValue();
+			}
+			final TextLayout fakeLayout = TextLayout.getManager().layoutStringSimple(content, this.getFont());
+			//final TextHitInfo fakeHitInfo = fakeLayout.getNextRightHit(hit.getCharIndex());
+			final float[] fakeCaretInfo = fakeLayout.getCaretInfo(hit);
+			
+			float y = getLineDrop(current) * lineIndex;
+			y = getY() + y + fakeCaretInfo[1];
+			float x = getX() + fakeCaretInfo[0] + getJustOffset(current);
+			x = Math.min(x, (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth()));
+			
+			return new Point(Math.round(x), Math.round(y));
+		} else {
+			// If we have no mask then....
+			// move the cursor to the new location
+			float[] caret = current.getCaretInfo(hit);
+			float y = getLineDrop(current) * lineIndex;
+			y = getY() + y + caret[1];
+
+			float x = getX() + caret[0] + getJustOffset(current);
+			x = Math.min(x, (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth()));
+
+			invalidateAll();
+			
+			final Point newCursor = new Point(Math.round(x), Math.round(y));
+			return newCursor;
+		}
 	}
 
@@ -1503,5 +1539,5 @@
 	 * 		The distance to advance in the y-direction before the next line.
 	 */
-	private float getLineDrop(TextLayout layout)
+	protected float getLineDrop(TextLayout layout)
 	{
 		if (getSpacing() < 0) {
@@ -1620,4 +1656,8 @@
 			return false;
 		}
+		
+		if (this.getMinWidth() != null && outline.contains(mouseX,  mouseY)) {
+			return true;
+		}
 
 		for (TextLayout text : _textLayouts) {
@@ -1628,5 +1668,5 @@
 			// gravity of right
 			int justOffset = getJustOffset(text);
-
+			
 			if (mouseY - textY > textOutline.getMinY() &&
 					mouseY - textY < textOutline.getMinY() + textOutline.getHeight() &&
@@ -1653,7 +1693,8 @@
 		}
 
-		if (_textLayouts == null || _textLayouts.size() < 1) {
+		// if there is no text layouts and the text has no min width, do nothing
+		if (_textLayouts == null || (_textLayouts.size() < 1 && this.getMinWidth() == null)) {
 			return null;
-		}
+		} 
 
 		int preChangeWidth = 0;
@@ -1669,4 +1710,14 @@
 
 		float y = -1;
+		
+		if (this.getMinWidth() != null && _textLayouts.size() == 0 && this.getFont() != null) {
+			final TextLayout fakeLayout = TextLayout.getManager().layoutStringSimple("p", this.getFont());
+			final int xPos = getX() - getGravity();
+			final int yPos = getY() - getGravity() - (int) fakeLayout.getAscent();
+			final int width = 2 * getGravity() + this.getMinWidth();
+			final int height = 2 * getGravity() + EcosystemManager.getGraphicsManager().getFontHeight(getFont());
+			TextLayout.getManager().releaseLayout(fakeLayout);
+			return new AxisAlignedBoxBounds(xPos, yPos, width, height);
+		}
 
 		// Fix concurrency error in ScaleFrameset
@@ -1676,17 +1727,22 @@
 		}
 
-		for (TextLayout layout : tmpTextLayouts) {
+		for (int index = 0; index < tmpTextLayouts.size(); index++) {
+			final TextLayout layout = tmpTextLayouts.get(index);
 			AxisAlignedBoxBounds bounds = layout.getLogicalHighlightShape(0, layout.getCharacterCount());
 
 			if (y < 0) {
 				y = 0;
-			} else {
+			} else { 
 				y += getLineDrop(layout);
 			}
-
-			maxX = Math.max(maxX, bounds.getMaxX());
+			
+			int minWidth = getAbsoluteMinWidth();
+
 			minX = Math.min(minX, bounds.getMinX());
+			maxX = minWidth < Integer.MAX_VALUE 
+					? Math.max(minX + minWidth, bounds.getMaxX()) 
+					: Math.max(maxX, bounds.getMaxX());
+			minY = Math.min(minY, (int) (bounds.getMinY() + y));
 			maxY = Math.max(maxY, (int) (bounds.getMaxY() + y));
-			minY = Math.min(minY, (int) (bounds.getMinY() + y));
 		}
 
@@ -1805,6 +1861,6 @@
 			//System.err.println(Arrays.toString(_text.toString().toCharArray()));
 		}
+		
 		invalidateBounds();
-
 	}
 
@@ -1933,5 +1989,10 @@
 
 		// if there is no text to paint, do nothing.
-		if (_text == null || _text.length() == 0) {
+		if (_text == null) {
+			return;
+		}
+		
+		// if the text to paint is empty string and there is no min width, do nothing.
+		if ((_text.length() == 0 && getMinWidth() == null)) {
 			return;
 		}
@@ -2001,5 +2062,4 @@
 				fill = null;
 			}
-//			System.err.println("Top left: " + getBoundingBox().getTopLeft() + ", " + "Bottom right: " + getBoundingBox().getBottomRight());
 			g.drawRectangle(bounds, 0.0, fill, getPaintHighlightColor(), highlightStroke, null);
 		}
@@ -2032,12 +2092,18 @@
 				}
 
+				if (layout.getCharacterCount() == 0) { continue; }
 				int ldx = 1 + getX() + getJustOffset(layout); // Layout draw x
-
-				g.drawTextLayout(layout, new Point(ldx, (int) y), paintColour);
+				if (_mask == null) {
+					g.drawTextLayout(layout, new Point(ldx, (int) y), paintColour);
+				} else {
+					String mask = "";
+					for(int o = 0; o < layout.getCharacterCount(); o++) { mask += (char) getMask().intValue(); }
+					g.drawString(mask, new Point(ldx, (int) y), layout.getFont(), paintColour);
+				}
 
 				y += getLineDrop(layout);
 			}
 		}
-
+		
 		paintLink();
 	}
@@ -2076,4 +2142,6 @@
 		copy.setWidth(getWidthToSave());
 		copy.setFont(getFont().clone());
+		copy.setMinWidth(getMinWidthToSave());
+		copy.setMask(_mask);
 		if (hasFormula()) {
 			copy.calculate(getFormula());
@@ -2168,4 +2236,9 @@
 
 		if (_text.length() == 0) {
+			if (this.getMinWidth() != null) {
+//				final TextLayout base = _textLayouts.get(0);
+//				_textLayouts.set(0, TextLayout.get(" ", base.getFont(), 0, 1));
+				_textLayouts.clear();
+			}
 			if (this.isLineEnd()) {
 				// Remove and replace with a dot
@@ -2496,5 +2569,5 @@
 		String text = getText();
 		assert (text != null);
-		return text.trim().length() == 0 || super.dontSave();
+		return super.dontSave() || (text.trim().length() == 0 && this.getMinWidth() == null);
 	}
 
@@ -2713,8 +2786,17 @@
 	@Override
 	public void setAnchorTop(Integer anchor) {
-		if (!isLineEnd()) {
+		if (!isLineEnd()) {			
 			super.setAnchorTop(anchor);
 			if (anchor != null) {
-				setY(anchor + _textLayouts.get(0).getAscent());
+				if (!_textLayouts.isEmpty()) {
+					final float ascent = _textLayouts.get(0).getAscent();
+					setY(anchor + ascent);
+				} else if (this.getFont() != null) {		
+					// p could be any character
+					final TextLayout fakeLayout = TextLayout.getManager().layoutStringSimple("p", this.getFont());
+					final float ascent = fakeLayout.getAscent();
+					EcosystemManager.getTextLayoutManager().releaseLayout(fakeLayout);
+					setY(anchor + ascent);
+				} 
 			}
 			return;
@@ -2740,5 +2822,16 @@
 			super.setAnchorBottom(anchor);
 			if (anchor != null) {
-				setY(DisplayController.getFramePaintArea().getHeight() - (anchor + this.getBoundsHeight() - _textLayouts.get(0).getAscent() - _textLayouts.get(0).getDescent()));
+				if (!_textLayouts.isEmpty()) {
+					final float ascent = _textLayouts.get(0).getAscent();
+					final float descent = _textLayouts.get(0).getDescent();
+					setY(DisplayController.getFramePaintArea().getHeight() - (anchor + this.getBoundsHeight() - ascent - descent));
+				} else if (this.getFont() != null) {
+					// p could be any character
+					final TextLayout fakeLayout = TextLayout.getManager().layoutStringSimple("p", this.getFont());
+					final float ascent = fakeLayout.getAscent();
+					final float descent = fakeLayout.getDescent();
+					setY(DisplayController.getFramePaintArea().getHeight() - (anchor + this.getBoundsHeight() - ascent - descent));
+				}
+				
 			}
 			return;
@@ -3055,5 +3148,11 @@
 			DisplayController.setCursorPosition(newText.getParagraphEndPosition());
 		}
-
-	}
+	}
+	
+	public Integer getMask() { 
+		if (_mask == null) { return null; }
+		else { return _mask; }
+	}
+	
+	public void setMask(final Integer c) { _mask = c; }
 }
