From f04d8b1c4c3ed29d4d23cc076f307ef455b2dcb6 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Mon, 29 Feb 2016 14:09:05 +0100 Subject: [PATCH] Add text-image control and separate Edit/View contact --- src/be/nikiroo/jvcard/tui/ImageText.java | 221 ++++++++++++++++++ src/be/nikiroo/jvcard/tui/KeyAction.java | 2 +- src/be/nikiroo/jvcard/tui/MainWindow.java | 6 + .../jvcard/tui/panes/ContactDetails.java | 73 ++++++ .../jvcard/tui/panes/ContactDetailsRaw.java | 13 +- .../nikiroo/jvcard/tui/panes/ContactList.java | 7 +- src/be/nikiroo/jvcard/tui/panes/FileList.java | 5 - .../nikiroo/jvcard/tui/panes/MainContent.java | 7 - 8 files changed, 303 insertions(+), 31 deletions(-) create mode 100644 src/be/nikiroo/jvcard/tui/ImageText.java create mode 100644 src/be/nikiroo/jvcard/tui/panes/ContactDetails.java diff --git a/src/be/nikiroo/jvcard/tui/ImageText.java b/src/be/nikiroo/jvcard/tui/ImageText.java new file mode 100644 index 0000000..80b675b --- /dev/null +++ b/src/be/nikiroo/jvcard/tui/ImageText.java @@ -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 }; + } +} diff --git a/src/be/nikiroo/jvcard/tui/KeyAction.java b/src/be/nikiroo/jvcard/tui/KeyAction.java index 5686e2e..a95835e 100644 --- a/src/be/nikiroo/jvcard/tui/KeyAction.java +++ b/src/be/nikiroo/jvcard/tui/KeyAction.java @@ -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 { diff --git a/src/be/nikiroo/jvcard/tui/MainWindow.java b/src/be/nikiroo/jvcard/tui/MainWindow.java index 13c6b3e..cda484d 100644 --- a/src/be/nikiroo/jvcard/tui/MainWindow.java +++ b/src/be/nikiroo/jvcard/tui/MainWindow.java @@ -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 index 0000000..825be1a --- /dev/null +++ b/src/be/nikiroo/jvcard/tui/panes/ContactDetails.java @@ -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 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); + } +} diff --git a/src/be/nikiroo/jvcard/tui/panes/ContactDetailsRaw.java b/src/be/nikiroo/jvcard/tui/panes/ContactDetailsRaw.java index 506eeff..271be44 100644 --- a/src/be/nikiroo/jvcard/tui/panes/ContactDetailsRaw.java +++ b/src/be/nikiroo/jvcard/tui/panes/ContactDetailsRaw.java @@ -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 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; diff --git a/src/be/nikiroo/jvcard/tui/panes/ContactList.java b/src/be/nikiroo/jvcard/tui/panes/ContactList.java index 7d10370..3bb6eac 100644 --- a/src/be/nikiroo/jvcard/tui/panes/ContactList.java +++ b/src/be/nikiroo/jvcard/tui/panes/ContactList.java @@ -74,7 +74,7 @@ public class ContactList extends MainContentList { List actions = new LinkedList(); // 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) { diff --git a/src/be/nikiroo/jvcard/tui/panes/FileList.java b/src/be/nikiroo/jvcard/tui/panes/FileList.java index 66e35ce..636ec43 100644 --- a/src/be/nikiroo/jvcard/tui/panes/FileList.java +++ b/src/be/nikiroo/jvcard/tui/panes/FileList.java @@ -129,9 +129,4 @@ public class FileList extends MainContentList { return actions; } - - @Override - public Mode getMode() { - return Mode.FILE_LIST; - } } diff --git a/src/be/nikiroo/jvcard/tui/panes/MainContent.java b/src/be/nikiroo/jvcard/tui/panes/MainContent.java index c33bbd3..68230d4 100644 --- a/src/be/nikiroo/jvcard/tui/panes/MainContent.java +++ b/src/be/nikiroo/jvcard/tui/panes/MainContent.java @@ -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}. * -- 2.27.0