JEditorPane mit Zeilenhervorhebung

7. Februar 2009 von Jan Lolling in Java, Java Code Beispiele
Schlagwörter: , ,

Ich habe für meinen Editor eine Möglichkeit gesucht die aktuelle Zeile unter dem Cursor vollständig mit einer anderen Hintergrundfarbe hervorzuheben. Diese Möglichkeit habe ich folgendermassen realisiert:

Zusammenfassung:

Der JEditorPane füge ich einen speziellen CaretListener hinzu der bei Veränderungen des Cursors einen wiederum für die aktuelle Zeile einen Highlighter einrichtet. Das besondere ist der als innere Klasse realisierte HightlighterPainter. Dieser ist so entwickelt, dass er die ganze Zeile unabhängig von der Zeichenanzahl also auf die sichtbare Breite des Editors hervorhebt.

Hier nun der Quelltext des CaretListeners:

package sqlrunner.text;
 
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
 
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.LayeredHighlighter;
import javax.swing.text.Position;
import javax.swing.text.Utilities;
import javax.swing.text.View;
 
public class HighlightCurrentLineCaretListener implements CaretListener {
 
    static final Color DEFAULT_COLOR = new Color(230, 230, 250);
 
    private Highlighter.HighlightPainter painter;
 
    private Object highlight;
 
    public HighlightCurrentLineCaretListener() {
 
        this(null);
    }
 
    public HighlightCurrentLineCaretListener(Color highlightColor) {
 
        Color c = highlightColor != null ? highlightColor : DEFAULT_COLOR;
        this.painter = new LineHighlightPainter(c);
    }
 
    public void caretUpdate(CaretEvent evt) {
 
        final JTextComponent comp = (JTextComponent) evt.getSource();
        if (comp != null && this.highlight != null) {
            comp.getHighlighter().removeHighlight(this.highlight);
            this.highlight = null;
        }
        int pos = comp.getCaretPosition();
        final Element elem = Utilities.getParagraphElement(comp, pos);
        int start = elem.getStartOffset();
        int end = elem.getEndOffset();
        try {
            this.highlight = comp.getHighlighter().addHighlight(start,
                                                                end,
                                                                this.painter);
            comp.repaint();
        } catch (BadLocationException ex) {
            ex.printStackTrace();
        }
    }
 
    /**
     * Simple highlight painter that fills a highlighted area with
     * a solid color.
     */
    public static class LineHighlightPainter extends
        LayeredHighlighter.LayerPainter {
 
        private Color color;
 
        /**
         * Constructs a new highlight painter. If <code>c</code> is null,
         * the JTextComponent will be queried for its selection color.
         *
         * @param c
         *            the color for the highlight
         */
        public LineHighlightPainter(Color c) {
 
            this.color = c;
        }
 
        /**
         * Returns the color of the highlight.
         *
         * @return the color
         */
        public Color getColor() {
 
            return this.color;
        }
 
        // --- HighlightPainter methods ---------------------------------------
        /**
         * Paints a highlight.
         *
         * @param g
         *            the graphics context
         * @param offs0
         *            the starting model offset &gt;= 0
         * @param offs1
         *            the ending model offset &gt;= offs1
         * @param bounds
         *            the bounding box for the highlight
         * @param c
         *            the editor
         */
        public void paint(Graphics g, int offs0, int offs1, Shape bounds,
                          JTextComponent c) {
 
            final Rectangle alloc = bounds.getBounds();
            try {
                // --- determine locations ---
                TextUI mapper = c.getUI();
                final Rectangle p0 = mapper.modelToView(c, offs0);
                final Rectangle p1 = mapper.modelToView(c, offs1);
 
                // --- render ---
                if (getColor() == null) {
                    g.setColor(c.getSelectionColor());
                } else {
                    g.setColor(getColor());
                }
                final Rectangle r = p0.union(p1);
                g.fillRect(r.x, r.y, r.width, r.height);
            } catch (BadLocationException e) {
                // can't render
            }
        }
 
        // --- LayerPainter methods ----------------------------
        /**
         * Paints a portion of a highlight.
         *
         * @param g
         *            the graphics context
         * @param offs0
         *            the starting model offset &gt;= 0
         * @param offs1
         *            the ending model offset &gt;= offs1
         * @param bounds
         *            the bounding box of the view, which is not
         *            necessarily the region to paint.
         * @param c
         *            the editor
         * @param view
         *            View painting for
         * @return region drawing occured in
         */
        @Override
        public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds,
                                JTextComponent c, View view) {
 
            if (getColor() == null) {
                g.setColor(c.getSelectionColor());
            } else {
                g.setColor(getColor());
            }
            // Should only render part of View.
            try {
                // --- determine locations ---
                Shape shape = view.modelToView(
                                               offs0,
                                               Position.Bias.Forward,
                                               offs1,
                                               Position.Bias.Backward,
                                               bounds);
                Rectangle r = shape instanceof Rectangle ? (Rectangle) shape
                        : shape.getBounds();
                g.fillRect(r.x, r.y, c.getWidth(), r.height);
                return r;
            } catch (BadLocationException e) {
                // can't render
            }
            return null;
        }
    }
}

Diesen CaretListener muss man nun mit:

textComp.addCaretListener(new HighlightCurrentLineCaretListener());

der Textkomponente hinzufügen. Hat bei mir ausgezeichnet funktioniert. Ich habe diesen Code unter Mac OS X und Windows getestet und er läuft abwärts bis Java 5.

  • Kommentare deaktiviert

Installation von PostGreSQL auf dem Mac

8. Oktober 2007 von Jan Lolling in Mac Fan Club
Schlagwörter:

1. holen der Sourcen von PostGreSQL

Hier bitte das tar-Archiv holen welches mit gz verpackt ist. Mit dem Stand heute ist die Version 8.2.5 aktuell.

2. auspacken

Ich installiere Tools und Datenbanken für Entwicklungszwecke immer unter /Developer. Also das empfangene Archiv nach Developer verschieben. Das Auspacken erfolgt dann mit (dazu muss man noch kein root sein):

tar xzvf postgresql-8.2.5.tar.gz

3.Kompilieren und bauen

cd postgresql-8.2.5
./configure
make

nun eine bash als root starten

sudo bash
make install

Wo findet man dann PostGreSQL ?

unter /usr/local/pgsql ist die Datenbank installiert

  • Kommentare deaktiviert

DMG erstellen mit ant

19. September 2007 von Jan Lolling in Java, Mac Fan Club
Schlagwörter: , ,

In einem der vorhergehenden Beiträge habe ich gezeigt wie eine Java Mac app erstellt wird. Typischerweise werden Mac-Anwendungen in Form eines Diskimages (DMG) verteilt. DMGs sind virtuelle Laufwerke mit Kompression, die aus einer Datei bestehen und vom Finder automatisch gemountet werden können.

Folgender ant-Task löst die Aufgabe unter Zuhilfenahme des command-line utils von Mac OS X:

	
		
			
				
			
		
		
			
		
	

In macappdir liegt das zuvor (mit dem ant task macapp) erstellte Verzeichnis SQLRunner.app und distrib verweist auf eine Verzeichnis in dem ich alle Endprodukte ablege. Wichtig ist das distrib nicht innerhalb von macappdir liegt.
Man sollte noch beachten, dass das Diskimage als Laufwerksname den Namen des Verzeichnisses von macappdir bekommt.
Klappt alles vorzüglich und schnell.

Java-Anwendung erstellen für Mac OS X

22. August 2007 von Jan Lolling in Java, Java Code Beispiele
Schlagwörter: , , ,

Wer eine Mac hat will natürlich seine Java-Anwendung auch mal echt im Dock sehen oder eine sinnvolle Dateiverknüpfung nutzen usw.
Das alles ist sehr einfach machbar.
Also was habe ich hier nun vor:
Ich werde euch an hand einer realen Anwendung zeigen wie man die Apple-Application-Events auswertet und wie man eine echte Mac-Anwendung zusammenbaut mit ant.
Meine Anwendung ist ein SQL-Client der speziell für die Entwicklung von Java-Anwendungen in Verbindung mit SQL entwicklelt wurde und darüberhinaus sehr viel Komfort bei der Handhabung der Datenbank und deren Daten mit sich bringt. Ich habe diese SQLRunner genannt und sie ist in meiner Firma mittlerweile sehr verbreitet via Java web Start.

Hier soll es aber nicht um Java Web Start gehen. JWS ist wirklich gut dokumentiert, dass muss ich hier nicht nachholen.

1. Wie kommuniziert Mac OS X mit einer Java Anwendung:
Mac OS sendet an die Anwendung Events:
a) zum Öffnen einer Datei
b) zum Öffnen des “Einstellungen…” Dialoges
c) zum Drucken einer Datei
d) zum Öffnen des About-Dialoges
e) zum Schliessen der Anwendung
Klingt erstmal etwas merkwürdig – jedenfalls für mich.
Wenn man sich Mac OS X Anwendungen ansieht so fällt auf, dass das Menü, egal was man selbst programmiert hat um einen ganz links stehenden Menüpunkt erweitert wurde der nach der Anwendung benannt ist und typischerweise aus immer den gleichen Einträgen besteht: “About”, “Preferences…”, Services (hat keinen Bezug zur Anwendung), “Exit ”

Apple stellt in seiner Java-Implementierung nun eine API zur Verfügung die einem diese Kommunikation bereitstellt. Die dafür notwendigen Klassen sind in:

/System/Library/Frameworks/JavaVM.framework/Classes/ui.jar

Man sollte aber darauf achten welche JRE-Version in der Anwendung unterstützt werden soll und sich besser die Datei aus der passenden version holen.
Das folgende Codesnippet zeigt die Registrierung des notwendigen EventListeners:
Ein paar Vorbemerkungen:
Bei mir hat der Code zum Öffnen einer Datei anfangs nicht funktioniert. Meine Anwendung wurde zwar gestartet aber es kam kein Event zum Öffnen der Datei. Das Problem lag schlicht daran, dass die nachfolgend beschriebene Registrierung zu einem Zeitpunkt erfolgte an dem Mac OS den Event bereits vorher gesendet hat. Meine Applikation hatte schlicht zu spät zugehört. Deshalb unbedingt die Registrierung soweit wie möglich nach vorn verlegen.
Dann hatte ich aber das Problem, dass es zu diesem Zeitpunkt noch kein Fenster oder Editor gab der sich für die übergebenen Inhalte interessiert hätte. Das muss man so lösen, dass man z.B. den Dateinamen erstmal merkt und beim Initialisierungsprozess des Fensters nachfragt ob solche Informationen vorliegen und die dann auch gleich berücksichtigt. Der SQLRunner z.B. initialisiert sich recht langwierig, da diverse Konfigurationen eingelesen werden müssen und dieser Vorgang wird in der boolean initalizationFinished mit true dokumentiert.

public static void main(String[] args) {
    commandLineArguments = args;
    System.getProperties().put("apple.laf.useScreenMenuBar", "true"); // damit Menüs oben in der Leiste landen !
    BasicConfigurator.configure(); // log4j minimal konfigurieren, wird später verbessert
    logger.info("SQLRunner Application Version " + VERSION);
     // unbedingt soweit zum Beginn als möglich den Listener registerieren !!
    String osname = System.getProperty("os.name");
    // laufen wir gerade auf Mac OS X ?
    if (osname != null &amp;&amp; osname.toLowerCase().indexOf("mac") != -1) {
        // diese Methode zeitlich so nah wie möglich zum Start der Anwendung ausführen !!
        setupMacOSXApplicationListener(); // registrieren des Listeners
    }
... die Anwendung weiter initialisieren, z.B. Konfigdateien lesen und GUI erstellen usw.
}
    private static void setupMacOSXApplicationListener() {
        if (logger.isDebugEnabled()) {
            logger.debug("setupMacOSXApplicationListener()");
        }
        try {
            final com.apple.eawt.Application application = com.apple.eawt.Application.getApplication();
            application.setEnabledAboutMenu(true); // damit ein "Über " Menü erscheint
            application.addPreferencesMenuItem(); // "Einstellen..." Dialog
            application.setEnabledPreferencesMenu(true); // diesen Dialog auch freischalten
            application.addApplicationListener(new com.apple.eawt.ApplicationListener() {
                // jemand hat auf Über... geklickt
 
                public void handleAbout(com.apple.eawt.ApplicationEvent ae) {
                    if (initializationFinished) { // nur was tun wenn Anwendung bereit ist
// .... hier den eigenen Info-Dialog zeigen oder was auch immer !
                        ae.setHandled(true); // habe fertig...
                    }
                }
 
                // kommt z.B. wenn eine Datei aus dem Kontextmenü heraus geöffnet wird
                // Mac OS startet ganz normal die Anwendung und versendet diesen Event
                public void handleOpenApplication(com.apple.eawt.ApplicationEvent ae) {
                    if (ae.getFilename() != null) {
                        if (initializationFinished) {
                            // die Anwendung ist bereit und alles initialisiert
                            // ... alles notwendige tun um eine Datei zu laden
                        } else {
                            // die Anwendung fährt gerade hoch
                            // noch kein Fenster da.
                            // ich merke mir die zu ladene Datei und muss später das laden nachholen
                            fileToLoad = ae.getFilename(); // fileToLoad ist eine static String variable
                        }
                    }
                }
 
                public void handlePreferences(com.apple.eawt.ApplicationEvent ae) {
                    if (initalizationFinished) {
                        // ... zeige meinen eigenen Einstellen-Dialog
                        ae.setHandled(true); // habe fertig
                    }
                }
 
                public void handlePrintFile(com.apple.eawt.ApplicationEvent ae) {
                    // meine Anwendung kann nicht drucken und will es auch nicht
                }
 
                public void handleQuit(com.apple.eawt.ApplicationEvent ae) {
                    // ... Die Anwendung geordnet beenden
                    // Fragen nach ggf notwendigen Speichern, Streams schliessen usw.
                    ae.setHandled(true); // bin mir nicht ganz sicher ob ich hier noch hinkomme ;-)
                }
 
                // meine Anwendung läuft bereits und jemand klickt unten auf mein Dock-Icon
                public void handleReOpenApplication(com.apple.eawt.ApplicationEvent ae) {
                    // ich würde hier ein weiteres Fenster aufmachen ...
                }
 
                // kommt z.B. wenn eine Datei aus dem Kontextmenü heraus geöffnet wird
                // Die Anwwendung ist aber schon vorher gestartet worden
                public void handleOpenFile(com.apple.eawt.ApplicationEvent ae) {
                    if (initializationFinished) { // sicher ist sicher
                        // Datei öffnen
                    } else {
                        // Datei merken und ggf später öffnen
                        fileToLoad = ae.getFilename();
                    }
                }
            });
        } catch (Throwable e) {
            logger.warn("setupMacOSXApplicationListener failed: " + e.getMessage(), e);
        }
    }

Soweit nun fast alles was man braucht um den eigenen Code zu erweitern

Wir brauchen nun ein schickes Icon für unsere Anwendung.
Dazu malt man sich in der Grösse 128×128 Bit mit 256 Bit Farbtiefe ein hübsche Bildchen im PNG oder JPG-Format.
Dieses Bild muss nun ind das sogenannte Icon-Format überführt werden. bei Mac OS X heissen diese Dateien .icns.
Der angedeutete Plural ist nicht ohne Grund. In der Datei stecken mehrere Bilder mit gleichem Inhalt aber unterschiedlicher Auflösung und Bildgrösse um die Skalierung zu ermöglichen wenn im Doc das Bild grösser wird oder wenn im Finder grössere Icons eingestellt werden. Damit Mac OS X da was zum interpolieren hat sind mehrere unterschiedliche Grössen in einer Datei enthalten.
Ein nettes (billiges aber kostenpflichtiges) Photoshop-Plugin Icon Export hilft hier weiter. Mit Photoshop kann man simpel ein nettes Bild bastel und es dann sogleich mit diesem Plugin in das icns-Format exportieren. Das Plugin funktioniert auch bei dem sehr preiswerten Photoshop Elements (momentan version 4.0) was jeder zweiten Digitalkamera kostenlos beiliegt oder im Laden 90 Euro kostet. Ich hatte es mir gekauft und es nie bereut !

Nun sind alle Zutaten zusammen: Wir haben eine nette Anwendung und auch ein hübsches Icon.

Nun müssen wir noch eine echte Mac-App bauen.
Ich bevorzuge dazu ant. Aktuell ist gerade die Version 1.7.0 – kann ich nur dringend empfehlen !

Hier ein Teil meines ant-scripts.
Ich kenne nur einen wirklich sinnvollen Weg: der jarbundler. Das Teil ist sehr gut dokumentiert !

Hier ein Beispiel für ein ant-task:

<target name="macapp"
        depends="jar"
        description="build Mac OS X application bundle">
    <delete failonerror="false"
            includeemptydirs="true"
            dir="${macappdir}" />
    <mkdir dir="${macappdir}" />
    <taskdef name="jarbundler"
             classname="net.sourceforge.jarbundler.JarBundler" />
    <jarbundler dir="${macappdir}"
                name="SQLRunner"
                shortname="SQLRunner"
                infostring="Version ${version} Copyright Jan Lolling"
                icon="${src}/sqlrunner.icns"
                version="${version}"
                mainclass="sqlrunner.Main"
                jvmversion="1.5+"
                vmoptions="-Xmx512m -Dcom.sun.management.jmxremote=true -Dapple.awt.graphics.UseQuartz=true">
        <jarfileset dir="${lib}">
            <include name="*.jar" />
        </jarfileset>
        <jarfileset dir="${distrib}">
            <include name="sqlrunner2.jar" />
        </jarfileset>
        <documenttype name="SQL script" extensions="sql" role="Editor" />
        <documenttype name="SQLRunner CSV Import Configuration" extensions="importconfig" role="Editor" />
    </jarbundler>
    <mkdir dir="${macappdir}/SQLRunner.app/Contents/Resources/German.lproj"/>
    <mkdir dir="${macappdir}/SQLRunner.app/Contents/Resources/English.lproj"/>
    <delete file="${macappdir}/SQLRunner.app/Contents/MacOS/JavaApplicationStub" />
    <echo>copy JavaApplicationStub</echo>
    <exec executable="cp">
        <arg line="/System/Library/Frameworks/JavaVM.framework/Resources/MacOS/JavaApplicationStub" />
        <arg value="${macappdir}/SQLRunner.app/Contents/MacOS/JavaApplicationStub" />
    </exec>
</target>

Java-Anwendungen werden wenn sie wie normale Mac Anwendungen gestartet werden sollen mit einem JavaStub gestartet. Dieser kann direkt in das eigene Projekt kopiert werden oder er wird verlinkt (wie im Beispiel). das direkte Kopieren hat den Nachteil das man damit die JRE-version festlegt ist aber krisenfest wenn Apple mal die Verzeichnis-Struktur von Java ändern sollte. Der symbolische Link hat den Vorteil, dass man immer mit der aktuellsten Version arbeitet und den Nachteil dass Verzeichnisänderungen der JRE ein echtes Problem darstellen würden. Seite der ersten Version von Mac OS X ist dieser Pfad aber stabil geblieben, weshalb ich das skizzierte Risiko als gering einschätze.

Viel Freude mit Java auf dem Mac wünscht Jan

Oracle SQL Developer

10. April 2006 von Ralph Bergmann in Allgemein
Schlagwörter: ,

raptor_image.jpgOracle bietet für die Betriebsystems MacOS X, Windows XP und Linux das freie Entwicklungswerkzeug SQL Developer an. Mit dem Java-basierten Werkzeug kann die komplette Struktur einer Oracle Datenbank ab Version 9.2 analysiert werden. Weiterhin kann man PL/SQL Skripte direkt entwickeln und ausführen lassen. Mehr: http://www.oracle.com/technology/products/database/sql_developer/index.html

  • Kommentare deaktiviert