The Mudcat Café TM
Thread #135614   Message #3093282
Posted By: GUEST,Grishka
11-Feb-11 - 12:21 PM
Thread Name: Tech: CopyUnicode: Create any char
Subject: Tech: CopyUnicode: Create any char

CopyUnicode is a utility which helps you enter individual characters in a portable way. To use it, you start the program, scroll to the character you want, click it, then paste to the Mudcat message entry area. Full directions for downloading and running the utility are given here in the second message.

I will attempt to keep the script and information up-to-date. I may also remove test posts and eliminate or correct faulty advice in situ. To test your messages, please use the Preview option; submit test messages only if you need someone tech-savvy to check your results (since what you see may not show the same way to other users.)

-Artful Codger-
[Source script updated 12 Feb 2011.]


On the Permathread about HTML character codes, I boastfully offered the following tool, assuming it would be rejected. However, Artful Codger asked me to post it in a separate thread, so here it is, FWIW. I extracted the following Java source code from a larger utility, which has been developped by a group of friends including myself. We donate this to the public domain, anyone is free to improve it.

Those of us who can profit most of this tool are likely not to have a Java compiler at hand. Therefore I suggest that Mudcat offer a compiled version (CopyUnicode.jar) for download (the same applies to Artful's htmlesc.java tool), which then can be used as an executable file on all up-to-date computers (Windows, Mac, Linux, ...).

When you invoke the program, usually by double-clicking on an icon etc., you will see a scrollable table displaying all the two-byte Unicode characters. If you click on one of these representations, the character resp. its HTML representation (looking like &...;) will be copied to the clipboard. If you then click into the Mudcat entry box and press Ctrl-V, the result will be inserted. You can deselect the HTML conversion and thus obtain the actual character, to inserted in other entry boxes or word processor systems etc.

I tried to keep the code short, so that experts can easily read it. Some bells and whistles may be found lacking. If mnemonic entities are desired, you can easily add this feature, as seen in htmlesc.

Actually I cannot believe that we are the first to invent this tool. If you know another implementation, feel free to post a link to it here; may the best win. Tell us about your experiences.

import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;

/**
 * @author  Grishka and friends
 * Donated to the public domain, feel free to improve
 */
public class ClipUnicode extends JFrame
{
  JCheckBox mHTMLCheckBox = new JCheckBox ();
  JComboBox mFontComboBox = new JComboBox ();
  JScrollPane mScrollPane = new JScrollPane ();
  JTable mCharsTable;

  boolean mIsFilling = false;

  String [] mFontNames;
  int [] mFontQualities;
  Integer [] mFontSortedIndices;
 
  private ClipUnicode ()
  {
    setTitle ("Copy a Unicode character to the clipboard");
    setDefaultCloseOperation (javax.swing.WindowConstants.EXIT_ON_CLOSE);
    getContentPane ().setLayout (new GridBagLayout ());

    JLabel lLabel = new JLabel ();
    lLabel.setText ("Instruction: Click on a character, click to the " +
            "destination, press Ctrl-V");
    lLabel.setToolTipText ("Donated to the public domain by Grishka and " +
        "Friends. Source code available at www.mudcat.org");
    GridBagConstraints lConstraints = new GridBagConstraints (0, 0,
        GridBagConstraints.REMAINDER, 1,
        1., 1., GridBagConstraints.CENTER, GridBagConstraints.NONE,
        new Insets (0, 0, 3, 0), 0, 0);
    getContentPane ().add (lLabel, lConstraints);

    // checkbox ======================
    mHTMLCheckBox.setText ("Copy the HTML code (otherwise the pure character)");
    mHTMLCheckBox.setToolTipText ("If selected, \"&#x...;\" will be copied, " +
        "otherwise a single character, no font information.");
    lConstraints = new GridBagConstraints (1, 1, 1, 1,
        0., 0., GridBagConstraints.NORTH, GridBagConstraints.NONE,
        new Insets (0, 0, 0, 0), 0, 0);
    getContentPane ().add (mHTMLCheckBox, lConstraints);
    mHTMLCheckBox.setSelected (true);

    // combobox to list the available fonts ======================
    mFontComboBox.addActionListener (new ActionListener ()
    {
      public void actionPerformed (ActionEvent evt)
      {
        if (!mIsFilling)
          SetView (mFontComboBox.getSelectedIndex ());
      }
    });

    lConstraints = new GridBagConstraints (2, 1, 1, 1,
        1., 1., GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
        new Insets (3, 5, 3, 5), 0, 0);
    getContentPane ().add (mFontComboBox, lConstraints);

    // fill it
    SortTheFonts ();
    mIsFilling = true;
    for (Integer lFont : mFontSortedIndices)
    {
      if (mFontQualities [lFont] < 10 && mFontComboBox.getItemCount () != 0)
        break;
      mFontComboBox.addItem (mFontNames [lFont]
          + " (" + mFontQualities [lFont] + ")");
    }
    mFontComboBox.setSelectedIndex (0);
    mFontComboBox.setToolTipText ("Font to use in this display, " +
        "good ones first in the list");
    mIsFilling = false;

    // scroll pane for the character table ======================
    mScrollPane.setPreferredSize (new Dimension (650, 600));
    lConstraints = new GridBagConstraints (0, 2, GridBagConstraints.REMAINDER, 1,
        1., 1., GridBagConstraints.CENTER, GridBagConstraints.NONE,
        new Insets (0, 0, 0, 0), 0, 0);
    getContentPane ().add (mScrollPane, lConstraints);

    // table of characters ======================
    CharTableModel lModel = new CharTableModel ();
    mCharsTable = new javax.swing.JTable (lModel)
    {
      @Override
      public String getToolTipText (MouseEvent aEvt)
      {
        Point lPt = aEvt.getPoint();
        int lRow = rowAtPoint (lPt);
        int lCol = convertColumnIndexToModel (columnAtPoint(lPt));
        if (lCol == 0)
          return "";
        int aCode = lRow * 16 + lCol - 1;
        String lRc = "x" + Integer.toHexString (aCode).toUpperCase () + " ("
                 + Integer.toString (aCode) + ")";
        Character.UnicodeBlock lUB = Character.UnicodeBlock.of (aCode);
        if (lUB != null)
          lRc += ", " + lUB.toString ();
        int lVal = Character.getNumericValue (aCode);
        if (lVal >= 0)
          lRc += ", numeric value " + Integer.toString (lVal);
        return lRc;
      }
    };
    mCharsTable.setRowHeight (25);
    mCharsTable.setCellSelectionEnabled (true);
    SetView (0);

    // action! ======================
    mCharsTable.addMouseListener (new MouseListener () {
      public void mousePressed (MouseEvent aEvt)
      {
        String lToClip = "";
        int lLow = mCharsTable.getSelectedColumn () - 1;
        if (lLow >= 0)
        {
          int lCodepoint = 16 * mCharsTable.getSelectedRow () + lLow;
          lToClip = (mHTMLCheckBox.isSelected ())?
              "&#x" + Integer.toHexString (lCodepoint).toUpperCase () + ";"
            : String.valueOf ((char) lCodepoint);
        }
        else
          mCharsTable.clearSelection ();
        StringSelection lSel = new StringSelection (lToClip);
        Toolkit.getDefaultToolkit ().getSystemClipboard ()
            .setContents (lSel, lSel);  
      }

      public void mouseClicked (MouseEvent aEvt) {}
      public void mouseReleased (MouseEvent aEvt) {}
      public void mouseEntered (MouseEvent aEvt) {}
      public void mouseExited (MouseEvent aEvt) {}
    });

    pack ();
  }

  private void SortTheFonts () // good ones first
  {
    GraphicsEnvironment lEnv = GraphicsEnvironment.getLocalGraphicsEnvironment ();
    mFontNames = lEnv.getAvailableFontFamilyNames ();
    mFontQualities = new int [mFontNames.length];
    mFontSortedIndices = new Integer [mFontNames.length];
    String lQualityTest = "\u00e1\u00e4\u00e7\u00df\u010d\u0142\u0119\u00a9" +
          "\u044f\u03c9\u00e5\u00bf\u00a3\u01dd\u20ac\u05d0\u0621\u266d" +
          "\u0e01\u1100\u2708\u3041\u4e00\uac00\u27f6";
    for (int lIdx = 0; lIdx < mFontNames.length; lIdx++)
    {
      mFontSortedIndices [lIdx] = lIdx;
      Font lFontSample = new Font (mFontNames [lIdx], 0, 10);
      mFontQualities [lIdx] = 2 * lFontSample.canDisplayUpTo (lQualityTest);
      if (mFontQualities [lIdx] < 0) // can display all of lQualityTest
        mFontQualities [lIdx] = 2 * lQualityTest.length ();
      if (mFontNames [lIdx].toLowerCase ().contains ("unicode"))
        mFontQualities [lIdx]++;
    }
    Arrays.sort (mFontSortedIndices, new Comparator ()
    {
      public int compare (Object aObj1, Object aObj2)
      {
        int lDiff = mFontQualities [(Integer) aObj2] - mFontQualities [(Integer) aObj1];
        if (lDiff != 0)
          return lDiff;
        return mFontNames [(Integer) aObj1].compareTo (mFontNames [(Integer) aObj2]);
      }
    });
  }

  private void SetView (int aFontIdx)
  {
    mCharsTable.setFont (new Font (mFontNames [mFontSortedIndices [aFontIdx]], 0, 20));
    mScrollPane.setViewportView (mCharsTable);
  }

  public static void main (final String args [])
  {
    EventQueue.invokeLater (new Runnable () {
      public void run ()
      {
        new ClipUnicode ().setVisible (true);
      }
    });
  }

  class CharTableModel extends AbstractTableModel
  {
    Component mComponent = null;
    Color mBgRowHeaders = new Color (240, 240, 240);
    Color mBgUppercase = new Color (230, 230, 255);
    Color mBgLowercase = new Color (255, 230, 230);
    Color mBgOtherLetter = new Color (255, 240, 215);
    Color mBgDigit = new Color (230, 255, 230);
    Color mBgElse = Color.white;
    Color mCurrBg = null;

    public int getRowCount() { return 256 * 16; }
    public int getColumnCount () { return 17; }
    @Override
    public String getColumnName (int aCol)
    {
      return (aCol == 0)? "" : Integer.toHexString (aCol - 1);
    }
    public Object getValueAt (int aRow, int aCol)
    {
      char lChar = (char) (aRow * 16 + aCol - 1);

      if (mComponent == null)
        mComponent = mCharsTable.getCellRenderer (0, 0)
            .getTableCellRendererComponent (mCharsTable, 0,
             rootPaneCheckingEnabled, rootPaneCheckingEnabled, 0, 0);
      if (aCol == 0)
      {
        mComponent.setBackground (mCurrBg = mBgRowHeaders);
        return Integer.toHexString (aRow);
      }

      // set the background color according to the type of character
      Color lColor;
      if (Character.isUpperCase (lChar))
        lColor = mBgUppercase;
      else if (Character.isLowerCase (lChar))
        lColor = mBgLowercase;
      else if (Character.isLetter (lChar))
        lColor = mBgOtherLetter;
      else if (Character.isDigit (lChar))
        lColor = mBgDigit;
      else
        lColor = mBgElse;
      if (lColor != mCurrBg)
        mComponent.setBackground (mCurrBg = lColor);

      return lChar;
    }
  }
}