Thursday, November 21, 2013

Swing Components in HTML GUI in Java

I would like to use Swing components in a HTML based layout. Initially I tried to have empty elements with id attributes in the loaded text, but they were pruned from the internal representation, so I put text inside of them and replaced the whole element with the Swing component.

HtmlGui.java:
package ca.sarah_happy.sandbox;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

public class HtmlGui implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new HtmlGui());
    }

    private JTextPane viewer;
    private HTMLDocument document;

    @Override
    public void run() {
        try {
            viewer = new JTextPane();
            viewer.setEditable(false);
            viewer.setPreferredSize(new Dimension(400, 300));

            JFrame frame = new JFrame("screen");
            frame.setContentPane(new JScrollPane(viewer));
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

            URL screen = HtmlGui.class.getResource("screen.html");
            viewer.setPage(screen);
            document = (HTMLDocument) viewer.getDocument();
            viewer.addPropertyChangeListener("page", onLoad);

            frame.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private JSlider slider;

    private PropertyChangeListener onLoad = new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent e) {
            JButton button = new JButton("Button");
            button.addActionListener(onButton);
            insertComponent("button", button);

            slider = new JSlider();
            slider.addChangeListener(onSlider);
            insertComponent("slider", slider);
        }
    };

    private ChangeListener onSlider = new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            addOutput("changed slider: " + slider.getValue());
        }
    };

    private void insertComponent(String id, Component component) {
        Element e = document.getElement(id);
        viewer.setCaretPosition(e.getStartOffset());
        viewer.moveCaretPosition(e.getEndOffset());
        viewer.insertComponent(component);
    }

    private ActionListener onButton = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            addOutput("pressed button");
        }
    };

    private void addOutput(String text) {
        try {
            Element out = document.getElement("output");
            document.insertString(out.getEndOffset() - 1, text + "\n", null);
        } catch (BadLocationException ex) {
            ex.printStackTrace();
        }
    }
}

screen.html:
<html><body>
Some stuff...

<p><span id="button">button</span> <span id="slider">slider</span></p>

<p><i>Output:</i>
<div id="output"></div></p>
</body></html>

first HTML GUI in Java

Java GUI layout can be cumbersome, and HTML layout is less cumbersome. I wanted to have a screen in a Java application based on a HTML document.

For an initial attempt I made a screen with two buttons and an output area using a JEditorPane, the buttons are made from links and the output area is made from a document element.

HtmlText.java:
package ca.sarah_happy.sandbox;

import java.awt.Dimension;
import java.net.URL;

import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

public class HtmlText implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new HtmlText());
    }

    private JEditorPane viewer;
    private HTMLDocument document;

    @Override
    public void run() {
        try {
            URL text = HtmlText.class.getResource("screen.html");
            viewer = new JEditorPane(text);
            viewer.setEditable(false);
            viewer.setPreferredSize(new Dimension(400, 300));

            JFrame frame = new JFrame("screen");
            frame.setContentPane(new JScrollPane(viewer));
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

            document = (HTMLDocument) viewer.getDocument();
            viewer.addHyperlinkListener(onLink);

            frame.setVisible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private HyperlinkListener onLink = new HyperlinkListener() {
        @Override
        public void hyperlinkUpdate(HyperlinkEvent e) {
            if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
                return;
            }

            String message = "pressed <" + e.getDescription() + ">";

            try {
                Element out = document.getElement("output");
                document.insertString(out.getEndOffset() - 1,
                        message + "\n", null);
            } catch (BadLocationException ex) {
                ex.printStackTrace();
            }
        }
    };
}

screen.html:
<html><body>
This is a test.
<p><a href="#link1">link 1</a>
<a href="#link2">link 2</a></p>

<p>output:
<div id="output"></div>
</p>
</body></html>

Friday, November 8, 2013

YouTube to iTunes

Sometimes I come across a video on YouTube that I would like to turn into a song in iTunes.

When thinking about puzzles, especially programming puzzles, often my thoughts are around what I have, what I want, and how I can bring the haves and wants closer to each other. As I put effort into the ways to bring the haves and wants together I end up with more haves and more options for wants. I am done when I find that I already have what I want.

I have a video on YouTube. I want a song in iTunes. iTunes can import mp3 files as songs, so I can also want a mp3 file.  I also have Chrome, and a freshly installed copy of Mac OSX Snow Leopard.

One option is YouTube to MP3 Converter, but it can only handle videos up to 20 minutes, and I want to convert a longer one.

I found a Chrome plugin that allows downloads of the YouTube media file that gets streamed to the browser, YouTube Options.  After installing that I can also have a .mp4 or .flv video file.

I have a script that I wrote a while ago that turns video files into mp3 files, but to run the script wants ffmpeg installed.  MacPorts has package for ffmpeg, but to install MacPorts it wants Xcode installed. Xcode is on the Snow Leopard Installation DVD, so I install it. Now I can install MacPorts, so I do. Now I can install ffmpeg, so I do (sudo port install ffmpeg) .

Now the script works, so I have what I wanted when I started, almost like magic.

A video on YouTube --> download the video from YouTube  --> convert the downloaded video to mp3 --> import the generated mp3 into iTunes --> a song in iTunes.

Thursday, November 7, 2013

fleece mittens

The mittens I used last winter shrank in the wash, so they did not fit well anymore, so I pondered how to make some out of fleece for a while, made a pattern by tracing another pair of mittens, and made myself some mittens.


They are symmetric so I don't need to figure out which one goes on which hand.

Monday, November 4, 2013

rainbow shoulder bag

One day a month or so ago I decided to make a new rainbow shoulder bag, one with a wider strap that would be more comfortable when I am wearing it for long periods of time. Today I got most of the work done.



The strap is filled with crocheted plastic bags giving it a sturdy padded structure. There are some loops of webbing to hang carabiners for holding things like my water bottle or hanging the bag off my backpack.  This bag is a bit larger than my previous one, I sized it using a mesh bag from the Dollarama.

In the future I will add a waterproof liner to keep the contents dry in the rain, and a zipper for easy closing.

Update: I didn't know at the time, but this bag also works as a backpack, with plenty of space.  I just clip a string across the bag and around the strap to make two spaces to put my arms, and because the strap is so wide it is very comfortable when it is weighted down.