Add text-image control and separate Edit/View contact
authorNiki Roo <niki@nikiroo.be>
Mon, 29 Feb 2016 13:09:05 +0000 (14:09 +0100)
committerNiki Roo <niki@nikiroo.be>
Mon, 29 Feb 2016 13:09:05 +0000 (14:09 +0100)
src/be/nikiroo/jvcard/tui/ImageText.java [new file with mode: 0644]
src/be/nikiroo/jvcard/tui/KeyAction.java
src/be/nikiroo/jvcard/tui/MainWindow.java
src/be/nikiroo/jvcard/tui/panes/ContactDetails.java [new file with mode: 0644]
src/be/nikiroo/jvcard/tui/panes/ContactDetailsRaw.java
src/be/nikiroo/jvcard/tui/panes/ContactList.java
src/be/nikiroo/jvcard/tui/panes/FileList.java
src/be/nikiroo/jvcard/tui/panes/MainContent.java

diff --git a/src/be/nikiroo/jvcard/tui/ImageText.java b/src/be/nikiroo/jvcard/tui/ImageText.java
new file mode 100644 (file)
index 0000000..80b675b
--- /dev/null
@@ -0,0 +1,221 @@
+package be.nikiroo.jvcard.tui;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+
+import com.googlecode.lanterna.TerminalSize;
+
+public class ImageText {
+       private Image image;
+       private TerminalSize size;
+       private String text;
+       private boolean ready;
+
+       public ImageText(Image image, TerminalSize size) {
+               setImage(image, size);
+       }
+
+       public void setImage(Image image) {
+               setImage(image, size);
+       }
+
+       public void setImage(TerminalSize size) {
+               setImage(image, size);
+       }
+
+       public void setImage(Image image, TerminalSize size) {
+               this.text = null;
+               this.ready = false;
+               this.size = size;
+               if (image != null) {
+                       this.image = image;
+               }
+       }
+
+       public String getText() {
+               if (text == null) {
+                       if (image == null)
+                               return "";
+
+                       int w = size.getColumns() * 2;
+                       int h = size.getRows() * 2;
+                       BufferedImage buff = new BufferedImage(w, h,
+                                       BufferedImage.TYPE_INT_ARGB);
+                       Graphics gfx = buff.getGraphics();
+
+                       TerminalSize srcSize = getSize(image);
+                       int x = 0;
+                       int y = 0;
+                       if (srcSize.getColumns() > srcSize.getRows()) {
+                               double ratio = (double) srcSize.getRows()
+                                               / (double) srcSize.getColumns();
+                               h = (int) Math.round(ratio * h);
+                               y = (buff.getHeight() - h) / 2;
+                       } else {
+                               double ratio = (double) srcSize.getColumns()
+                                               / (double) srcSize.getRows();
+                               w = (int) Math.round(ratio * w);
+                               x = (buff.getWidth() - w) / 2;
+
+                       }
+
+                       if (gfx.drawImage(image, x, y, w, h, new ImageObserver() {
+                               @Override
+                               public boolean imageUpdate(Image img, int infoflags, int x,
+                                               int y, int width, int height) {
+                                       ImageText.this.ready = true;
+                                       return true;
+                               }
+                       })) {
+                               ready = true;
+                       }
+
+                       while (!ready) {
+                               try {
+                                       Thread.sleep(100);
+                               } catch (InterruptedException e) {
+                               }
+                       }
+
+                       gfx.dispose();
+
+                       int[][] square = new int[2][2];
+                       StringBuilder builder = new StringBuilder();
+                       for (int row = 0; row < buff.getHeight(); row += 2) {
+                               if (row > 0)
+                                       builder.append('\n');
+
+                               for (int col = 0; col < buff.getWidth(); col += 2) {
+                                       square[0][0] = buff.getRGB(col, row);
+                                       square[0][1] = buff.getRGB(col, row + 1);
+                                       square[1][0] = buff.getRGB(col + 1, row);
+                                       square[1][1] = buff.getRGB(col + 1, row + 1);
+                                       builder.append(getChar(square));
+                               }
+                       }
+
+                       text = builder.toString();
+               }
+
+               return text;
+       }
+
+       @Override
+       public String toString() {
+               return getText();
+       }
+
+       static private TerminalSize getSize(Image img) {
+               TerminalSize size = null;
+               while (size == null) {
+                       int w = img.getWidth(null);
+                       int h = img.getHeight(null);
+                       if (w > -1 && h > -1) {
+                               size = new TerminalSize(w, h);
+                       } else {
+                               try {
+                                       Thread.sleep(100);
+                               } catch (InterruptedException e) {
+                               }
+                       }
+               }
+
+               return size;
+       }
+
+       static private char getChar(int[][] square) {
+               int choice = 0;
+               if (rgb2hsl(square[0][0])[3] > 50)
+                       choice += 1;
+               if (rgb2hsl(square[0][1])[3] > 50)
+                       choice += 2;
+               if (rgb2hsl(square[1][0])[3] > 50)
+                       choice += 4;
+               if (rgb2hsl(square[1][1])[3] > 50)
+                       choice += 8;
+
+               switch (choice) {
+               case 0:
+                       return ' ';
+               case 1:
+                       return '▘';
+               case 2:
+                       return '▝';
+               case 3:
+                       return '▀';
+               case 4:
+                       return '▖';
+               case 5:
+                       return '▌';
+               case 6:
+                       return '▞';
+               case 7:
+                       return '▛';
+               case 8:
+                       return '▗';
+               case 9:
+                       return '▚';
+               case 10:
+                       return '▐';
+               case 11:
+                       return '▜';
+               case 12:
+                       return '▄';
+               case 13:
+                       return '▙';
+               case 14:
+                       return '▟';
+               case 15:
+                       return '█';
+               }
+
+               return ' ';
+       }
+
+       // return [a, h, s, l]; a/s/l: 0 to 100%, h = 0 to 359°
+       static int[] rgb2hsl(int argb) {
+               double a, r, g, b;
+               a = ((argb & 0xff000000) >> 24) / 255.0;
+               r = ((argb & 0x00ff0000) >> 16) / 255.0;
+               g = ((argb & 0x0000ff00) >> 8) / 255.0;
+               b = ((argb & 0x000000ff)) / 255.0;
+
+               double rgbMin, rgbMax;
+               rgbMin = Math.min(r, Math.min(g, b));
+               rgbMax = Math.max(r, Math.max(g, b));
+
+               double l;
+               l = (rgbMin + rgbMax) / 2;
+
+               double s;
+               if (rgbMin == rgbMax) {
+                       s = 0;
+               } else {
+                       if (l <= 0.5) {
+                               s = (rgbMax - rgbMin) / (rgbMax + rgbMin);
+                       } else {
+                               s = (rgbMax - rgbMin) / (2.0 - rgbMax - rgbMin);
+                       }
+               }
+
+               double h;
+               if (r > g && r > b) {
+                       h = (g - b) / (rgbMax - rgbMin);
+               } else if (g > b) {
+                       h = 2.0 + (b - r) / (rgbMax - rgbMin);
+               } else {
+                       h = 4.0 + (r - g) / (rgbMax - rgbMin);
+               }
+
+               int aa = (int) Math.round(100 * a);
+               int hh = (int) (60 * h);
+               if (hh < 0)
+                       hh += 360;
+               int ss = (int) Math.round(100 * s);
+               int ll = (int) Math.round(100 * l);
+
+               return new int[] { aa, hh, ss, ll };
+       }
+}
index 5686e2edaa7fea1d079c22776f37a1eab2b0deb9..a95835eb28b77468b758b6b53516bf0e481bda7f 100644 (file)
@@ -27,7 +27,7 @@ public class KeyAction {
         * 
         */
        public enum Mode {
-               NONE, MOVE, BACK, HELP, FILE_LIST, CONTACT_LIST, CONTACT_DETAILS, EDIT_DETAIL, DELETE_CONTACT, SAVE_CARD,
+               NONE, MOVE, BACK, HELP, FILE_LIST, CONTACT_LIST, CONTACT_DETAILS_RAW, CONTACT_DETAILS, EDIT_DETAIL, DELETE_CONTACT, SAVE_CARD,
        }
 
        public enum DataType {
index 13c6b3e238a4e021873d2a4694a667b023137eb8..cda484da6d30ecf2063101308c69e082d0adebc1 100644 (file)
@@ -13,6 +13,7 @@ import be.nikiroo.jvcard.i18n.Trans;
 import be.nikiroo.jvcard.i18n.Trans.StringId;
 import be.nikiroo.jvcard.tui.KeyAction.Mode;
 import be.nikiroo.jvcard.tui.UiColors.Element;
+import be.nikiroo.jvcard.tui.panes.ContactDetails;
 import be.nikiroo.jvcard.tui.panes.ContactDetailsRaw;
 import be.nikiroo.jvcard.tui.panes.ContactList;
 import be.nikiroo.jvcard.tui.panes.MainContent;
@@ -573,6 +574,11 @@ public class MainWindow extends BasicWindow {
                        }
                        break;
                case CONTACT_DETAILS:
+                       if (contact != null) {
+                               pushContent(new ContactDetails(contact));
+                       }
+                       break;
+               case CONTACT_DETAILS_RAW:
                        if (contact != null) {
                                pushContent(new ContactDetailsRaw(contact));
                        }
diff --git a/src/be/nikiroo/jvcard/tui/panes/ContactDetails.java b/src/be/nikiroo/jvcard/tui/panes/ContactDetails.java
new file mode 100644 (file)
index 0000000..825be1a
--- /dev/null
@@ -0,0 +1,73 @@
+package be.nikiroo.jvcard.tui.panes;
+
+import java.awt.Image;
+import java.util.Base64;
+import java.util.List;
+
+import javax.swing.ImageIcon;
+
+import be.nikiroo.jvcard.Contact;
+import be.nikiroo.jvcard.Data;
+import be.nikiroo.jvcard.TypeInfo;
+import be.nikiroo.jvcard.tui.ImageText;
+import be.nikiroo.jvcard.tui.KeyAction;
+import be.nikiroo.jvcard.tui.KeyAction.DataType;
+
+import com.googlecode.lanterna.TerminalSize;
+import com.googlecode.lanterna.gui2.BorderLayout;
+import com.googlecode.lanterna.gui2.Panel;
+import com.googlecode.lanterna.gui2.TextBox;
+
+public class ContactDetails extends MainContent {
+       private Contact contact;
+
+       @Override
+       public DataType getDataType() {
+               return DataType.DATA;
+       }
+
+       @Override
+       public List<KeyAction> getKeyBindings() {
+               // TODO Auto-generated method stub
+               return null;
+       }
+
+       public ContactDetails(Contact contact) {
+               this.contact = contact;
+
+               BorderLayout blayout = new BorderLayout();
+               setLayoutManager(blayout);
+
+               Panel top = new Panel();
+               if (contact != null) {
+                       Data photo = contact.getPreferredData("PHOTO");
+                       if (photo != null) {
+                               TypeInfo encoding = null;
+                               TypeInfo type = null;
+                               for (TypeInfo info : photo.getTypes()) {
+                                       if (info.getName() != null) {
+                                               if (info.getName().equalsIgnoreCase("ENCODING"))
+                                                       encoding = info;
+                                               if (info.getName().equalsIgnoreCase("TYPE"))
+                                                       type = info;
+                                       }
+                               }
+
+                               if (encoding != null && encoding.getValue() != null
+                                               && encoding.getValue().equalsIgnoreCase("b")) {
+
+                                       Image img = new ImageIcon(Base64.getDecoder().decode(
+                                                       photo.getValue())).getImage();
+
+                                       TerminalSize size = new TerminalSize(40, 20);
+                                       size = new TerminalSize(120, 50);
+
+                                       String str = new ImageText(img, size).getText();
+                                       top.addComponent(new TextBox(size, str));
+                               }
+                       }
+               }
+
+               addComponent(top, BorderLayout.Location.TOP);
+       }
+}
index 506eeff96f28481b2c5a9c0303ccde3e40e6ad83..271be442e82922e1ef30c0bd7dad61ee1ab3d5b1 100644 (file)
@@ -98,13 +98,7 @@ public class ContactDetailsRaw extends MainContentList {
        public DataType getDataType() {
                return DataType.DATA;
        }
-
-       @Override
-       public String getExitWarning() {
-               // TODO Auto-generated method stub
-               return null;
-       }
-
+       
        @Override
        public List<KeyAction> getKeyBindings() {
                // TODO Auto-generated method stub
@@ -132,11 +126,6 @@ public class ContactDetailsRaw extends MainContentList {
                return actions;
        }
 
-       @Override
-       public Mode getMode() {
-               return Mode.CONTACT_DETAILS;
-       }
-
        @Override
        public String getTitle() {
                String title = null;
index 7d1037006c13ea2b0998ca3c40976c8d8049df27..3bb6eac7095b093b006a3b4f865ce88905d749e3 100644 (file)
@@ -74,7 +74,7 @@ public class ContactList extends MainContentList {
                List<KeyAction> actions = new LinkedList<KeyAction>();
 
                // TODO add
-               actions.add(new KeyAction(Mode.CONTACT_DETAILS, 'e',
+               actions.add(new KeyAction(Mode.CONTACT_DETAILS_RAW, 'e',
                                Trans.StringId.KEY_ACTION_EDIT_CONTACT) {
                        @Override
                        public Object getObject() {
@@ -119,11 +119,6 @@ public class ContactList extends MainContentList {
                return DataType.CARD;
        }
 
-       @Override
-       public Mode getMode() {
-               return Mode.CONTACT_LIST;
-       }
-
        @Override
        public String getTitle() {
                if (card != null) {
index 66e35ce2e43d9985507a149f2f4b8f09498cd2ac..636ec431a8e0935c7ff94b859d9f953adb014e9e 100644 (file)
@@ -129,9 +129,4 @@ public class FileList extends MainContentList {
 
                return actions;
        }
-
-       @Override
-       public Mode getMode() {
-               return Mode.FILE_LIST;
-       }
 }
index c33bbd3fdb93a868b84177382add38f49d6712bb..68230d490eeb98e3bb612f323fd91533f73caeef 100644 (file)
@@ -29,13 +29,6 @@ abstract public class MainContent extends Panel {
                setLayoutManager(layout);
        }
 
-       /**
-        * The {@link KeyAction#Mode} that links to this {@link MainContent}.
-        * 
-        * @return the linked mode
-        */
-       abstract public KeyAction.Mode getMode();
-
        /**
         * The kind of data displayed by this {@link MainContent}.
         *