Das Tool
Die Klasse Tool ist zentrale Basis für Werkzeuge und hat vier
wesentliche Aufgabenbereiche:
die Verarbeitung von Eingabeereignissen
das Anreichern der Darstellung des Views mit eigenen Elementen, das heißt die Teilname am Rendering des Views
das Beitragen von Kontextmenüelementen
die Teilnahme an der Zustandsverwaltung im Zusammenspiel mit dem
ToolManager
Bis auf den letzten Punkt sind alle Aufgaben optional. Tools müssen nicht auf Eingaben reagieren oder visuelle
Beiträge zur Oberfläche liefern.
Auch die Erstellung eigener Tools ist recht einfach durch Erweiterung der abstrakten
Klasse Tool möglich.
Tools werden von den zugehörigen Views mit
Eingabeereignissen versorgt, wodurch sie die Möglichkeit erhalten, auf Eingaben zu
reagieren. Die von AWT/Swing bekannte Klassenhierarchie für Ereignisse
(InputEvent und die davon abgeleiteten Klassen) bildet die Basis
für die Eingabeereignisse. Allerdings werden diese nicht direkt verwendet, da Tools für ihre Arbeit in der Regel weitere
Kontextinformationen, wie die betroffene Seite (Page), die aktuellen RenderControls und ähnliches benötigen. Aus diesem
Grund stellt jadice den InputEvents eine analoge Klassenhierarchie
unterhalb der abstrakten Klasse EditEvent zur Seite:
KeyEditEvent, MouseEditEvent und
MouseWheelEditEvent. Jedes EditEvent
bietet immer auch den Zugriff auf den usprünglich auslösenden
InputEvent. EditEvents beziehen sich oft
auf eine Page, zum Beispiel dann, wenn sich der Mauszeiger in
der Nähe oder über der Darstellung einer Seite befindet. Allerdings können die Ereignisse
auch ohne Seitenkontext auftreten, weshalb Tools mit diesem Umstand umgehen können müssen.
In der Praxis treten bei der Ereignisverarbeitung im Zusammenhang mit in Views
dargestellten Seiten immer wieder ähnliche Anforderungen zutage. Die EditEvents versuchen
hierbei die häufigsten Anforderungen auf komfortable Weise abzudecken. Beispielsweise
bietet das MouseEditEvent fünf verschiedene Methoden zum Zugriff
auf die Koordinaten der Maus:
getInputEvent().getPoint()entspricht den von AWT/Swing gewohnten Koordinaten in Pixeln, bezogen auf die obere linke Ecke des Views.getPoint()liefert die Bildschirmkoordinaten (Pixel) bezogen auf die obere linke Ecke der Seite, auf die sich das Event bezieht.getConstrainedPoint()liefert die gleichen Werte wiegetPoint(), allerdings werden diese, wenn sich die Maus außerhalb einer Seite befindet, so begrenzt, dass der nächstgelegene Punkt innerhalb der nächstgelegenen Seite geliefert wird.getDocumentPoint()liefert die Dokumentkoordinaten, das heißt Koordinaten in Einheiten vonDocument.BASE_RESOLUTION, bezogen auf die obere linke Ecke der nächstgelegenen Seite.getConstrainedDocumentPoint()entspricht wiedergetConstrainedPoint(), allerdings findet hier erneut das »Einfangen« der Koordinaten wie beigetConstrainedPoint()statt.
Die Klasse Tool nutzt für die Übergabe von
EditEvents zunächst die Methode
handleEditEvent(...). Die Standardimplementierung dieser Methode
nimmt eine Verteilung an eventspezifische Template-Methoden wie keyPressed, mouseClicked
und so weiter, vor. Nachfahren der Klasse Tool können so sehr komfortabel selektiv nur diejenigen
Ereignismethoden überschreiben, die das Tool tatsächlich benötigt.
Da zu einer View häufig mehr als ein Tool registriert ist, ist es wichtig, die
Reihenfolge, in der die verschiedenen Tools mit Ereignissen versorgt werden, beeinflussen
zu können. Tools können weitgehend selbst steuern, wie früh oder
spät in der Reihenfolge sie mit Ereignissen versorgt werden. Die Methode
getDispatchPriority() des Tools muss hierzu einen Wert zwischen
Tool.MIN_PRIORITY und Tool.MAX_PRIORITY
zurückgeben. Je näher an MAX_PRIORITY der Wert ist, desto früher
wird das Tool mit Ereignissen versorgt.
Hat ein Tool ein Ereignis erhalten und hat es sich dafür
entschieden, darauf zu reagieren, ist es oft sinnvoll dafür zu sorgen, dass keine
anderen Tools mehr auf das Ereignis reagieren. Hierzu genügt
es, beim Verarbeiten des Ereignisses dessen consume()-Methode
aufzurufen. Sobald ein Ereignis konsumiert wurde, wird es nicht mehr an weitere Tools zur Verarbeitung übergeben. Im Umkehrschluss
bedeutet dies auch, dass Tools nicht explizit prüfen müssen, ob ein Ereignis
bereits konsumiert wurde, da sie dieses sonst überhaupt nicht erhalten hätten.
Nachdem Views die Seitendarstellungen gerendert haben, erhalten Tools die Gelegenheit, eigene visuelle Elemente
beizutragen. Für jede sichtbare Seite wird zu diesem Zweck die Tool-Methode render(...)
aufgerufen, die ein Parameterobjekt (RenderParameters) sowie die
Information, ob das Tool gerade aktiv ist, übergeben bekommt. Die
RenderParameters enthalten alle Kontextinformationen, die zum
Rendering benötigt werden, zum Beispiel den Zielgrafikkontext
(Graphics2D), die Seite, den Bereich, in den die Seite gerendert
wurde und so weiter.
Wie es eine Dispatch-Reihenfolge für die Verteilung von Ereignissen gibt, existiert
eine separate Reihenfolge für das Zeichnen der Tools. Über getRenderPriority()
kann ein Tool festlegen, wie früh oder spät es beim Rendering
zum Zug kommen will. In der Regel sollten die Dispatch- und Renderreihenfolge in einer
inversen Beziehung zueinander stehen. Tools können konzeptionell wie Ebenen betrachtet
werden können: je weiter ›oben‹, desto früher werden Ereignisse verteilt und desto
später wird gerendert. Die Methode getRenderPriority() kann Werte
zwischen Tool.MIN_PRIORITY und
Tool.MAX_PRIORITY zurückgeben. Die Standardimplementierung von
getRenderPriority() gibt den Wert
DEFAULT_PRIORITY zurück, der ein Mittelwert zwischen
Tool.MIN_PRIORITY und Tool.MAX_PRIORITY
ist.
Die Verwaltung des Zustandes von Tools obliegt dem ToolManager. Der Zustandsraum von Tools umfasst dabei folgende Freiheitsgrade:
- registriert/nicht registriert
Tools, die (noch) nicht bei einem bestimmten
ToolManagerregistriert wurden, können in keiner Weise mit ihrem View interagieren. Erst durch die Registrierung werden sie dazu in die Lage versetzt. Bei der Registrierung wird die MethodesetManager(ToolManager)desTools aufgerufen.- eingeschaltet/ausgeschaltet (enabled/disabled)
Bereits bei der Registrierung eines
Tools kann angegeben werden, ob dasTooldanach ein- oder ausgeschaltet (enabled/disabled) sein soll. EnabledTools sind demToolManagerzwar bekannt, sie erhalten aber weder Ereignisse, noch die Gelegenheit zum Rendering. Ob einToolenabled ist, kann durch die MethodesetEnabled(Class<? extends Tool>, boolean)gesteuert werden.- aktiv/inaktiv
In manchen Situationen kann es notwendig sein, dass ein
Tool, obwohl es in der Dispatch-Reihenfolge eigentlich nicht an erster Stelle steht, temporär vorrangig vor allen anderenTools die Gelegenheit zur Verabeitung von Ereignissen erhält. Dies kann beispielsweise dann nützlich sein, wenn eine Programmoberfläche implementiert werden soll, die dem Nutzer verschiedene Bearbeitungsmodi, die von Tools realisiert werden, anbietet. Die Umschaltung zwischen diesen Modi würde dann durch das Aktivieren des entsprechenden (Modus-)Tools verwirklicht werden.Im
ToolManagerwird diese Auszeichnung als aktivesToolverwaltet. EinToolkann aktiv gesetzt werden mithilfe der Methodeactivate(...). Es kann immer nur einToolgleichzeitig aktiv sein, daher führt ein Aufruf der Methodeactivate(...)immer auch zum Inaktivieren des letzten aktivenTools, sofern es ein solches gab.Neben dieser ›manuellen‹ Art
Tools zu aktivieren, kann eineToolActivationPolicydie Auswahl des jeweils aktivenTools automatisieren. Dazu kann imToolManagerdie gewünschteToolActivationPolicymit der MethodesetActivationPolicy(ToolActivationPolicy)gesetzt werden. Vor der Ereignisverarbeitung erhält die Policy eine Liste aller Tools, die sich um Aktivierung ›bewerben‹. Sie wählt daraus, welches Tool den Zuschlag bekommt und das Ereignis vorrangig verarbeiten darf.- exklusiv
Es kann sinnvoll sein, bestimmten
Tools für einen begrenzten Zeitraum die alleinige Kontrolle zu überlassen, um Konflikte mit anderenTools während komplexer Operationen zu vermeiden. Diese Exklusivität greift zum Beispiel bei der Annotationsbearbeitung, während Annotationen in der Größe verändert oder verschoben werden, sowie während der Texteditor aktiv ist. Der Exklusivmodus sorgt dafür, dass lediglich das exklusiveToolmit Ereignissen versorgt wird und Gelegenheit zum Rendering erhält. In der Regel ist es dasToolselbst, das den Exklusivmodus mittelsToolManager.setExclusive(Class<? extends Tool>)aktiviert. Bei der Verwendung des Exklusivmodus ist es entscheidend, dafür zu sorgen, dass dieser auch zuverlässig wieder zu verlassen wird, da sonst gegebenenfalls die Funktion aller anderenTools dauerhaft blockiert wird.


