Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/kmcaster.git

Normalize mouse icons, show 'button' panel

Author DaveJarvis <email>
Date 2020-07-16 19:54:26 GMT-0700
Commit 13d830302cb15bb23066307e47e41de43cee7ee5
Parent 16a6742
.idea/workspace.xml
<component name="ChangeListManager">
<list default="true" id="be1819c5-28d3-4984-9634-0b266734e13c" name="Default Changelist" comment="">
- <change afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/AppImage.java" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/.idea/gradle.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/gradle.xml" afterDir="false" />
+ <change afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/Modifier.java" afterDir="false" />
+ <change afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/1-3.svg" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/key/dn/alphanum.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/key/dn/short.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/key/dn/modifier.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/key/dn/medium.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/key/dn/shift.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/key/dn/long.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/key/up/alphanum.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/key/up/short.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/key/up/modifier.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/key/up/medium.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/key/up/shift.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/key/up/long.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/mouse/all.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/all.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/mouse/chord.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/2.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/mouse/left.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/1.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/mouse/rest.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/0.svg" afterDir="false" />
- <change beforePath="$PROJECT_DIR$/images/mouse/right.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/3.svg" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/AppImage.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/AppImage.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/EventFrame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/EventFrame.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/KmCaster.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/KmCaster.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardListener.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/MouseListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/MouseListener.java" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/resources/images/mouse/0.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/0.svg" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/resources/images/mouse/1.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/1.svg" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/resources/images/mouse/2.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/2.svg" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/resources/images/mouse/3.svg" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/images/mouse/3.svg" afterDir="false" />
+ <change beforePath="$PROJECT_DIR$/src/main/resources/images/mouse/all.svg" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<system id="GRADLE">
<state>
+ <task path="$PROJECT_DIR$">
+ <activation />
+ </task>
<projects_view>
<tree_state>
<expand>
+ <path>
+ <item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
+ <item name="kmcaster" type="f1a62948:ProjectNode" />
+ </path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="kmcaster" type="f1a62948:ProjectNode" />
+ <item name="Tasks" type="e4a08cd1:TasksNode" />
</path>
</expand>
<option name="RECENT_TEMPLATES">
<list>
- <option value="Class" />
<option value="Enum" />
+ <option value="Class" />
</list>
</option>
<component name="RecentsManager">
<key name="MoveInnerDialog.RECENTS_KEY">
- <recent name="com.whitemagicsoftware.kmcaster" />
<recent name="com.whitemagicsoftware.kmcaster.listeners" />
+ <recent name="com.whitemagicsoftware.kmcaster" />
</key>
<key name="CreateClassDialog.RecentsKey">
</state>
<state x="813" y="683" width="218" height="250" key="#Notifications/0.28.2560.1529@0.28.2560.1529" timestamp="1594856235998" />
- <state x="734" y="519" width="376" height="578" key="#com.intellij.ide.util.MemberChooser" timestamp="1594881096559">
+ <state x="314" y="436" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1594912481218">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state x="734" y="519" width="376" height="578" key="#com.intellij.ide.util.MemberChooser/0.28.2560.1529@0.28.2560.1529" timestamp="1594881096559" />
- <state x="745" y="737" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog" timestamp="1594860488340">
+ <state x="314" y="436" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594912481218" />
+ <state x="734" y="519" width="376" height="578" key="#com.intellij.ide.util.MemberChooser" timestamp="1594883540197">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state x="745" y="737" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594860488340" />
- <state x="707" y="657" width="431" height="302" key="#com.intellij.refactoring.move.moveInner.MoveInnerDialog" timestamp="1594860490819">
+ <state x="734" y="519" width="376" height="578" key="#com.intellij.ide.util.MemberChooser/0.28.2560.1529@0.28.2560.1529" timestamp="1594883540197" />
+ <state x="745" y="737" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog" timestamp="1594953388252">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state x="707" y="657" width="431" height="302" key="#com.intellij.refactoring.move.moveInner.MoveInnerDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594860490819" />
+ <state x="745" y="737" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594953388252" />
+ <state x="707" y="657" width="431" height="302" key="#com.intellij.refactoring.move.moveInner.MoveInnerDialog" timestamp="1594953392347">
+ <screen x="0" y="28" width="2560" height="1529" />
+ </state>
+ <state x="707" y="657" width="431" height="302" key="#com.intellij.refactoring.move.moveInner.MoveInnerDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594953392347" />
<state x="567" y="28" width="711" height="1526" key="#com.intellij.refactoring.rename.AutomaticRenamingDialog" timestamp="1594858565314">
<screen x="0" y="28" width="2560" height="1529" />
</state>
<state x="710" y="704" width="425" height="207" key="DetectedRootsChooserDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594872764235" />
- <state width="1479" height="406" key="GridCell.Tab.0.bottom" timestamp="1594881867827">
+ <state width="1479" height="330" key="GridCell.Tab.0.bottom" timestamp="1594954347951">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.0.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867827" />
- <state width="1479" height="406" key="GridCell.Tab.0.center" timestamp="1594881867826">
+ <state width="1479" height="330" key="GridCell.Tab.0.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1594954347951" />
+ <state width="1479" height="330" key="GridCell.Tab.0.center" timestamp="1594954347950">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.0.center/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867826" />
- <state width="1479" height="406" key="GridCell.Tab.0.left" timestamp="1594881867826">
+ <state width="1479" height="330" key="GridCell.Tab.0.center/0.28.2560.1529@0.28.2560.1529" timestamp="1594954347950" />
+ <state width="1479" height="330" key="GridCell.Tab.0.left" timestamp="1594954347950">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.0.left/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867826" />
- <state width="1479" height="406" key="GridCell.Tab.0.right" timestamp="1594881867827">
+ <state width="1479" height="330" key="GridCell.Tab.0.left/0.28.2560.1529@0.28.2560.1529" timestamp="1594954347950" />
+ <state width="1479" height="330" key="GridCell.Tab.0.right" timestamp="1594954347951">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.0.right/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867827" />
- <state width="1479" height="406" key="GridCell.Tab.1.bottom" timestamp="1594881867827">
+ <state width="1479" height="330" key="GridCell.Tab.0.right/0.28.2560.1529@0.28.2560.1529" timestamp="1594954347951" />
+ <state width="1479" height="406" key="GridCell.Tab.1.bottom" timestamp="1594948352434">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.1.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867827" />
- <state width="1479" height="406" key="GridCell.Tab.1.center" timestamp="1594881867827">
+ <state width="1479" height="406" key="GridCell.Tab.1.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1594948352434" />
+ <state width="1479" height="406" key="GridCell.Tab.1.center" timestamp="1594948352433">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.1.center/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867827" />
- <state width="1479" height="406" key="GridCell.Tab.1.left" timestamp="1594881867827">
+ <state width="1479" height="406" key="GridCell.Tab.1.center/0.28.2560.1529@0.28.2560.1529" timestamp="1594948352433" />
+ <state width="1479" height="406" key="GridCell.Tab.1.left" timestamp="1594948352432">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.1.left/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867827" />
- <state width="1479" height="406" key="GridCell.Tab.1.right" timestamp="1594881867827">
+ <state width="1479" height="406" key="GridCell.Tab.1.left/0.28.2560.1529@0.28.2560.1529" timestamp="1594948352432" />
+ <state width="1479" height="406" key="GridCell.Tab.1.right" timestamp="1594948352433">
<screen x="0" y="28" width="2560" height="1529" />
</state>
- <state width="1479" height="406" key="GridCell.Tab.1.right/0.28.2560.1529@0.28.2560.1529" timestamp="1594881867827" />
+ <state width="1479" height="406" key="GridCell.Tab.1.right/0.28.2560.1529@0.28.2560.1529" timestamp="1594948352433" />
<state x="423" y="444" width="1026" height="842" key="SettingsEditor" timestamp="1594876039235">
<screen x="0" y="28" width="2560" height="1529" />
</state>
<state x="423" y="444" width="1026" height="842" key="SettingsEditor/0.28.2560.1529@0.28.2560.1529" timestamp="1594876039235" />
+ <state width="761" height="601" key="XDebugger.FullValuePopup" timestamp="1594940367498">
+ <screen x="0" y="28" width="2560" height="1529" />
+ </state>
+ <state width="761" height="601" key="XDebugger.FullValuePopup/0.28.2560.1529@0.28.2560.1529" timestamp="1594940367498" />
<state x="265" y="308" width="1323" height="1003" key="com.intellij.history.integration.ui.views.FileHistoryDialog" timestamp="1594881438574">
<screen x="0" y="28" width="2560" height="1529" />
</state>
<state x="265" y="308" width="1323" height="1003" key="com.intellij.history.integration.ui.views.FileHistoryDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594881438573" />
+ <state x="840" y="410" width="564" height="770" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser" timestamp="1594946566725">
+ <screen x="0" y="28" width="2560" height="1529" />
+ </state>
+ <state x="840" y="410" width="564" height="770" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser/0.28.2560.1529@0.28.2560.1529" timestamp="1594946566725" />
+ <state x="833" y="545" width="586" height="502" key="find.popup" timestamp="1594949350582">
+ <screen x="0" y="28" width="2560" height="1529" />
+ </state>
+ <state x="833" y="545" width="586" height="502" key="find.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1594949350582" />
<state x="589" y="382" key="run.anything.popup" timestamp="1594881668952">
<screen x="0" y="28" width="2560" height="1529" />
</state>
<state x="589" y="382" key="run.anything.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1594881668952" />
<state x="589" y="477" width="672" height="678" key="search.everywhere.popup" timestamp="1594880500645">
<screen x="0" y="28" width="2560" height="1529" />
</state>
<state x="589" y="477" width="672" height="678" key="search.everywhere.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1594880500645" />
- </component>
- <component name="XDebuggerManager">
- <watches-manager>
- <configuration name="Remote">
- <watch expression="mClassLoader.getResource(&quot;/images/key/dn/shift.svg&quot;)" language="JAVA" />
- </configuration>
- </watches-manager>
</component>
</project>
src/main/com/whitemagicsoftware/kmcaster/AppImage.java
+/*
+ * Copyright 2020 White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
package com.whitemagicsoftware.kmcaster;
import com.kitfox.svg.SVGDiagram;
+import com.kitfox.svg.SVGException;
import com.kitfox.svg.SVGUniverse;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
import java.net.URL;
+import java.util.Map;
+import static com.whitemagicsoftware.kmcaster.KmCaster.rethrow;
+import static java.awt.RenderingHints.*;
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
import static java.lang.String.format;
/**
* Responsible for loading vector graphics representations of application
* images. The images provide an on-screen interface that indicate to the user
* what key or mouse events are being triggered.
*/
public class AppImage {
+ public final static Map<Object, Object> RENDERING_HINTS = Map.of(
+ KEY_ANTIALIASING,
+ VALUE_ANTIALIAS_ON,
+ KEY_ALPHA_INTERPOLATION,
+ VALUE_ALPHA_INTERPOLATION_QUALITY,
+ KEY_COLOR_RENDERING,
+ VALUE_COLOR_RENDER_QUALITY,
+ KEY_DITHERING,
+ VALUE_DITHER_DISABLE,
+ KEY_FRACTIONALMETRICS,
+ VALUE_FRACTIONALMETRICS_ON,
+ KEY_INTERPOLATION,
+ VALUE_INTERPOLATION_BICUBIC,
+ KEY_RENDERING,
+ VALUE_RENDER_QUALITY,
+ KEY_STROKE_CONTROL,
+ VALUE_STROKE_PURE,
+ KEY_TEXT_ANTIALIASING,
+ VALUE_TEXT_ANTIALIAS_ON
+ );
+
private final static String IMAGES = "/images";
private final static String IMAGES_KEY = IMAGES + "/key";
private final static String IMAGES_MOUSE = IMAGES + "/mouse";
- public static final AppImage MOUSE_LEFT = mouseImage( "0" );
- public static final AppImage MOUSE_RIGHT = mouseImage( "1" );
+ public static final AppImage MOUSE_REST = mouseImage( "0" );
+ public static final AppImage MOUSE_LEFT = mouseImage( "1" );
public static final AppImage MOUSE_CHORD = mouseImage( "2" );
- public static final AppImage MOUSE_WHEEL = mouseImage( "3" );
- public static final AppImage MOUSE_ALL = mouseImage( "all" );
+ public static final AppImage MOUSE_RIGHT = mouseImage( "3" );
+ public static final AppImage MOUSE_LR = mouseImage( "1-3" );
public static final AppImage KEY_UP_SHIFT = keyUpImage( "long" );
public static final AppImage KEY_UP_ALT = keyUpImage( "medium" );
}
- public SVGDiagram getImage() {
+ public JComponent toComponent( final Dimension dimension ) {
+ final var image = toImage( dimension );
+
+ return new JComponent() {
+ @Override
+ public Dimension getPreferredSize() {
+ return new Dimension(
+ image.getWidth( null ), image.getHeight( null )
+ );
+ }
+
+ @Override
+ protected void paintComponent( final Graphics graphics ) {
+ super.paintComponent( graphics );
+
+ final var g = (Graphics2D) graphics.create();
+ g.drawImage( image, 0, 0, this );
+ }
+ };
+ }
+
+ public Image toImage( final Dimension dstDim ) {
+ final var diagram = loadDiagram();
+ final var diaWidth = diagram.getWidth();
+ final var diaHeight = diagram.getHeight();
+ final var srcDim = new Dimension( (int) diaWidth, (int) diaHeight );
+
+ final var scaledDim = scale( srcDim, dstDim );
+ final var w = (int) scaledDim.getWidth();
+ final var h = (int) scaledDim.getHeight();
+
+ final var image = new BufferedImage( w, h, TYPE_INT_ARGB );
+
+ try {
+ final Graphics2D g = image.createGraphics();
+ g.setRenderingHints( RENDERING_HINTS );
+
+ final AffineTransform transform = g.getTransform();
+ transform.setToScale( w / diaWidth, h / diaHeight );
+
+ g.setTransform( transform );
+ diagram.render( g );
+ g.dispose();
+ } catch( final SVGException e ) {
+ rethrow( e );
+ }
+
+ return image;
+ }
+
+ private SVGDiagram loadDiagram() {
final var url = getResourceUrl();
- return mRenderer.getDiagram( mRenderer.loadSVG( url ) );
+ return applySettings( mRenderer.getDiagram( mRenderer.loadSVG( url ) ) );
+ }
+
+ private SVGDiagram applySettings( final SVGDiagram diagram ) {
+ diagram.setIgnoringClipHeuristic( true );
+ return diagram;
}
private URL getResourceUrl() {
return AppImage.class.getResource( getPath() );
}
private String getPath() {
return mPath;
+ }
+
+ private Dimension scale( final Dimension src, final Dimension dst ) {
+ final var srcWidth = src.getWidth();
+ final var srcHeight = src.getHeight();
+ final var dstHeight = dst.getHeight();
+
+ var newWidth = srcWidth;
+ var newHeight = srcHeight;
+
+ if( newHeight < dstHeight ) {
+ newHeight = dstHeight;
+ newWidth = (newHeight * srcWidth) / srcHeight;
+ }
+
+ return new Dimension( (int) newWidth, (int) newHeight );
}
}
src/main/com/whitemagicsoftware/kmcaster/EventFrame.java
+/*
+ * Copyright 2020 White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
package com.whitemagicsoftware.kmcaster;
import com.whitemagicsoftware.kmcaster.listeners.FrameDragListener;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
+
+import static com.whitemagicsoftware.kmcaster.AppImage.*;
class EventFrame extends JFrame {
private static final float ARC = 8;
- private static final int FRAME_WIDTH = 380;
- private static final int FRAME_HEIGHT = 60;
+ private static final Dimension FRAME_DIMENSIONS = new Dimension( 484, 70 );
+ private static final Color TRANSLUCENT = new Color( .2f, .2f, .2f, 0.5f );
+ private static final Color TRANSPARENT = new Color( 0, 0, 0, 0 );
public EventFrame() {
+ setLocationRelativeTo( null );
setUndecorated( true );
setAlwaysOnTop( true );
- setBackground( new Color( .2f, .2f, .2f, 0.5f ) );
-
- setLocationRelativeTo( null );
- setSize( FRAME_WIDTH, FRAME_HEIGHT );
- setShape( new RoundRectangle2D.Double(
- 0, 0, getWidth(), getHeight(), ARC, ARC ) );
+ setBackground( TRANSPARENT );
+ setSize( FRAME_DIMENSIONS );
+ setShape( createShape() );
final var frameDragListener = new FrameDragListener( this );
addMouseListener( frameDragListener );
addMouseMotionListener( frameDragListener );
+
+ final var dimensions = new Dimension( getWidth(), getHeight() - 10 );
+ final var mouseView = MOUSE_LR.toComponent( dimensions );
+ final var shiftView = KEY_UP_SHIFT.toComponent( dimensions );
+ final var ctrlView = KEY_UP_CTRL.toComponent( dimensions );
+ final var altView = KEY_UP_ALT.toComponent( dimensions );
+ final var regularView = KEY_UP_REGULAR.toComponent( dimensions );
+
+ final var panel = new JPanel();
+ panel.setAlignmentX( Component.CENTER_ALIGNMENT );
+ panel.setBackground( TRANSLUCENT );
+ panel.add( mouseView );
+ panel.add( shiftView );
+ panel.add( ctrlView );
+ panel.add( altView );
+ panel.add( regularView );
+
+ final var content = getContentPane();
+ final var layout = new BoxLayout( content, BoxLayout.Y_AXIS );
+
+ content.setLayout( layout );
+ content.add( panel );
+ }
+
+ private Shape createShape() {
+ return new RoundRectangle2D.Double(
+ 0, 0, getWidth(), getHeight(), ARC, ARC
+ );
}
}
src/main/com/whitemagicsoftware/kmcaster/KmCaster.java
import static java.util.logging.Logger.getLogger;
+import static javax.swing.SwingUtilities.invokeLater;
import static org.jnativehook.GlobalScreen.*;
initNativeHook();
- final var kc = new KmCaster();
- SwingUtilities.invokeLater( () -> kc.setVisible( true ) );
+ invokeLater( () -> {
+ final var kc = new KmCaster();
+ kc.setVisible( true );
+ } );
}
src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardListener.java
+/*
+ * Copyright 2020 White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
package com.whitemagicsoftware.kmcaster.listeners;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
+import java.util.List;
import java.util.Map;
-import java.util.Set;
import static java.util.Map.entry;
import static org.jnativehook.NativeInputEvent.*;
import static org.jnativehook.keyboard.NativeKeyEvent.getKeyText;
public class KeyboardListener implements NativeKeyListener {
- public final static String KEY_NAME_ALT = "ALT";
- public final static String KEY_NAME_CTRL = "CTRL";
- public final static String KEY_NAME_SHIFT = "SHIFT";
- public final static String KEY_NAME_REGULAR = "REGULAR";
+ public final static String KEY_NAME_REGULAR = "regular";
- /**
- * The library has default names for these modifiers, which will be
- * converted to lower case for comparison with this set.
- */
- private final static Set<String> modifiers = Set.of(
- "alt", "ctrl", "shift"
+ private final List<Modifier> mModifiers = List.of(
+ new Modifier( "alt", ALT_MASK ),
+ new Modifier( "ctrl", CTRL_MASK ),
+ new Modifier( "shift", SHIFT_MASK )
);
);
- private boolean mAltHeld;
- private boolean mCtrlHeld;
- private boolean mShiftHeld;
private String mRegularHeld = "";
}
+ @SuppressWarnings("unused")
public void removePropertyChangeListener(
final PropertyChangeListener listener ) {
mDispatcher.removePropertyChangeListener( listener );
}
@Override
public void nativeKeyPressed( final NativeKeyEvent e ) {
final String regularHeld = getDisplayText( e );
- // If it's not a modifier key, broadcast the regular value.
- if( !modifiers.contains( regularHeld.toLowerCase() ) ) {
- tryFire( KEY_NAME_REGULAR, mRegularHeld, regularHeld );
- mRegularHeld = regularHeld;
+ for( final var modifier : mModifiers ) {
+ // If it's not a modifier key, broadcast the regular value.
+ if( modifier.isName( regularHeld.toLowerCase() ) ) {
+ tryFire( KEY_NAME_REGULAR, mRegularHeld, regularHeld );
+ mRegularHeld = regularHeld;
+ }
}
- // If the modifier has changed, it'll be caught here.
updateModifiers( e );
}
}
+ /**
+ * Notifies of any modifier state changes.
+ *
+ * @param e The keyboard event that was most recently triggered.
+ */
private void updateModifiers( final NativeKeyEvent e ) {
- final boolean alt = isAltDown( e );
- final boolean ctrl = isControlDown( e );
- final boolean shift = isShiftDown( e );
-
- tryFire( KEY_NAME_ALT, mAltHeld, alt );
- tryFire( KEY_NAME_CTRL, mCtrlHeld, ctrl );
- tryFire( KEY_NAME_SHIFT, mShiftHeld, shift );
-
- mAltHeld = alt;
- mCtrlHeld = ctrl;
- mShiftHeld = shift;
+ for( final var modifier : mModifiers ) {
+ final boolean down = modifier.matches( e );
+ tryFire( modifier.toString(), modifier.isHeld(), down );
+ modifier.setHeld( down );
+ }
}
/**
* Called to fire the property change with the two given values differ.
*
* @param name The name of the property that has changed.
* @param o Old property value.
* @param n New property value.
*/
- @SuppressWarnings("SameParameterValue")
private void tryFire( final String name, final String o, final String n ) {
if( !o.equals( n ) ) {
mDispatcher.firePropertyChange( name, o, n );
}
}
/**
- * Called to fire the property change with the two given values differ.
+ * Delegates to {@link #tryFire(String, String, String)} with {@link Boolean}
+ * values as strings.
*
* @param name The name of the property that has changed.
* @param o Old property value.
* @param n New property value.
*/
private void tryFire( final String name, final boolean o, final boolean n ) {
- if( o != n ) {
- mDispatcher.firePropertyChange( name, o, n );
- }
+ tryFire( name, Boolean.toString( o ), Boolean.toString( n ) );
}
e.getRawCode(), getKeyText( e.getKeyCode() )
);
- }
-
- private boolean isModifierDown( final NativeKeyEvent e, final int mask ) {
- return (e.getModifiers() & mask) != 0;
- }
-
- private boolean isAltDown( final NativeKeyEvent e ) {
- return isModifierDown( e, ALT_MASK );
- }
-
- private boolean isControlDown( final NativeKeyEvent e ) {
- return isModifierDown( e, CTRL_MASK );
- }
-
- private boolean isShiftDown( final NativeKeyEvent e ) {
- return isModifierDown( e, SHIFT_MASK );
}
}
src/main/com/whitemagicsoftware/kmcaster/listeners/Modifier.java
+/*
+ * Copyright 2020 White Magic Software, Ltd.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.whitemagicsoftware.kmcaster.listeners;
+
+import org.jnativehook.keyboard.NativeKeyEvent;
+
+import javax.swing.event.ChangeListener;
+
+/**
+ * Responsible for tracking the state of modifier keys.
+ */
+class Modifier {
+ private boolean mHeld;
+ private final int mMask;
+ private final String mName;
+
+ Modifier( final String name, final int mask ) {
+ assert name != null;
+
+ mName = name.toLowerCase();
+ mMask = mask;
+ }
+
+ public boolean isName( final String name ) {
+ return mName.equals( name );
+ }
+
+ public boolean isHeld() {
+ return mHeld;
+ }
+
+ public void setHeld( final boolean held ) {
+ mHeld = held;
+ }
+
+ /**
+ * Answers whether the given {@link NativeKeyEvent} modifiers are set to
+ * include the modifiers defined by this instance.
+ *
+ * @param e The event that has modifiers to check.
+ * @return {@code true} This modifier matches the given event's modifiers.
+ */
+ public boolean matches( final NativeKeyEvent e ) {
+ return (e.getModifiers() & mMask) != 0;
+ }
+
+ /**
+ * Returns a unique identifier for this modifier that can be used by
+ * a {@link ChangeListener}.
+ *
+ * @return The modifier key name, in lowercase.
+ */
+ @Override
+ public String toString() {
+ return mName;
+ }
+}
src/main/com/whitemagicsoftware/kmcaster/listeners/MouseListener.java
public void nativeMouseDragged( NativeMouseEvent e ) {
- System.out.println( "Mouse Dragged: " + e.getX() + ", " + e.getY() );
+// System.out.println( "Mouse Dragged: " + e.getX() + ", " + e.getY() );
}
src/main/resources/images/mouse/0.svg
-<svg height="9.563195mm" viewBox="0 0 6.2268581 9.5631952" width="6.226858mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="675.16781095345" x2="726.91040621336" y1="193.5974546131" y2="193.5974546131"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="694.62630016049" x2="706.30293766591" y1="170.04024231137" y2="170.04024231137"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><g transform="matrix(.11019174 0 0 .11019174 -74.07199 -16.55124)"><path d="m728.71927 191.15556c0 25.31414-12.65005 45.83532-28.25465 45.83532s-28.25465-20.52118-28.25465-45.83532 12.87203-40.95154 28.47664-40.95154c15.6046 0 28.03266 15.6374 28.03266 40.95154z" fill="url(#a)"/><g stroke="#333"><path d="m700.46462 150.76421v38.00227" fill="none" stroke-linecap="round"/><path d="m727.85189 184.1953c-16.7914 6.29576-36.32862 6.68664-54.76049 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="25.528791" rx="5.216753" width="10.433506" x="695.24786" y="157.27585"/></g></g></svg>
+<svg height="60" viewBox="0 0 40 60" width="40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" xlink:href="#c" y1="1.7" y2="1.7"/><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="9.5" x2="14.098" xlink:href="#c" y1="6.46" y2="6.46"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" y1="1.7" y2="1.7"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><linearGradient id="d" gradientUnits="userSpaceOnUse" x1=".95001" x2="22.686" y1="16.492" y2="16.492"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><g transform="scale(1.6949 1.6438)"><g stroke="#333" stroke-width=".11" transform="matrix(3.8 0 0 3.8 .00001 -.000006)"><path d="m3.1.062v4.2" fill="none" stroke-linecap="round"/><path d="m6.1 3.7c-1.9.69-4 .74-6 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#a)" fill-rule="evenodd" height="2.8" rx=".57" width="1.1" x="2.5" y=".78"/></g><g stroke-width=".418"><path d="m23.6 17.1c0 10.6-5.32 19.4-11.8 19.4s-11.8-8.74-11.8-19.4 5.32-17.1 11.8-17.1 11.8 6.46 11.8 17.1z" fill="url(#d)"/><g stroke="#333"><path d="m11.8.236v16" fill="none" stroke-linecap="round"/><path d="m23.2 14.2c-7.22 2.62-15.2 2.81-22.8 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="10.6" rx="2.17" width="4.18" x="9.69" y="2.96"/></g></g></g></svg>
src/main/resources/images/mouse/1-3.svg
-
+<svg height="60" viewBox="0 0 40 60" width="40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" xlink:href="#c" y1="1.7" y2="1.7"/><linearGradient id="b" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1="9.5" x2="14.098" xlink:href="#c" y1="6.46" y2="6.46"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" y1="1.7" y2="1.7"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><linearGradient id="d" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1=".95001" x2="22.686" y1="16.492" y2="16.492"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><g stroke="#333" stroke-width=".11" transform="matrix(6.4406 0 0 6.2464 .000017 -.00001)"><path d="m3.1.062v4.2" fill="none" stroke-linecap="round"/><path d="m6.1 3.7c-1.9.69-4 .74-6 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#a)" fill-rule="evenodd" height="2.8" rx=".57" width="1.1" x="2.5" y=".78"/></g><path d="m40 28.109c0 17.424-9.0169 31.89-20 31.89s-20-14.367-20-31.89 9.0169-28.109 20-28.109 20 10.619 20 28.109z" fill="url(#d)" stroke-width=".69771"/><path d="m20.002 0c6.4635 0 11.4 3.5947 14.536 8.0789 3.2687 4.6251 4.7135 10.298 5.2149 15.107-6.9009 2.2617-12.492 3.5032-19.751 3.4974z" fill="#40ff00" fill-opacity=".25" fill-rule="evenodd" stroke-width="1.7077"/><path d="m19.947 0c-6.2319 0-11.204 3.4235-14.462 8.0337-3.2687 4.6251-4.7622 10.391-5.2389 15.204 6.9009 2.2617 12.442 3.4521 19.701 3.4463z" fill="#0096ff" fill-opacity=".25" fill-rule="evenodd" stroke-width="1.7077"/><g stroke="#333" stroke-width=".69771"><path d="m20 .38794v26.301" fill="none" stroke-linecap="round"/><path d="m39.322 23.342c-12.237 4.3068-25.762 4.6191-38.644 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="17.424" rx="3.6779" width="7.0847" x="16.424" y="4.8656"/></g></svg>
src/main/resources/images/mouse/1.svg
-<svg height="9.563195mm" viewBox="0 0 6.2268581 9.5631952" width="6.226858mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="-218.23392109934" x2="-212.53231434848" y1="188.98810309963" y2="188.98810309963"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-216.08975626054" x2="-214.80308722328" y1="186.3922928196" y2="186.3922928196"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><g transform="translate(218.55985 -184.2065)"><path d="m-212.333 188.71902c0 2.78941-1.39393 5.05068-3.11342 5.05068-1.7195 0-3.11343-2.26127-3.11343-5.05068s1.41839-4.51252 3.13789-4.51252c1.71949 0 3.08896 1.72311 3.08896 4.51252z" fill="url(#a)"/><path d="m-215.44807 184.20884c-.95261 0-1.73653.55323-2.24371 1.28803-.50884.73718-.75361 1.66727-.82783 2.43428 1.07427.36049 1.94146.53162 3.07154.53069z" fill="#0096ff" fill-opacity=".25" fill-rule="evenodd" stroke-width=".268987"/><g stroke="#333" stroke-width=".110192"><path d="m-215.44642 184.26823v4.18754" fill="none" stroke-linecap="round"/><path d="m-212.42857 187.95206c-1.85028.69374-4.00312.73681-6.03416 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="2.813062" rx=".574843" width="1.149686" x="-216.02127" y="184.98576"/></g></g></svg>
+<svg height="60" viewBox="0 0 40 60" width="40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" xlink:href="#c" y1="1.7" y2="1.7"/><linearGradient id="b" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1="9.5" x2="14.098" xlink:href="#c" y1="6.46" y2="6.46"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" y1="1.7" y2="1.7"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><linearGradient id="d" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1=".95001" x2="22.686" y1="16.492" y2="16.492"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><g stroke="#333" stroke-width=".11" transform="matrix(6.4406 0 0 6.2464 .000017 -.00001)"><path d="m3.1.062v4.2" fill="none" stroke-linecap="round"/><path d="m6.1 3.7c-1.9.69-4 .74-6 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#a)" fill-rule="evenodd" height="2.8" rx=".57" width="1.1" x="2.5" y=".78"/></g><path d="m40 28.109c0 17.424-9.0169 31.89-20 31.89s-20-14.367-20-31.89 9.0169-28.109 20-28.109 20 10.619 20 28.109z" fill="url(#d)" stroke-width=".69771"/><path d="m19.947 0c-6.2319 0-11.204 3.4235-14.462 8.0337-3.2687 4.6251-4.7622 10.391-5.2389 15.204 6.9009 2.2617 12.442 3.4521 19.701 3.4463z" fill="#0096ff" fill-opacity=".25" fill-rule="evenodd" stroke-width="1.7077"/><g stroke="#333" stroke-width=".69771"><path d="m20 .38794v26.301" fill="none" stroke-linecap="round"/><path d="m39.322 23.342c-12.237 4.3068-25.762 4.6191-38.644 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="17.424" rx="3.6779" width="7.0847" x="16.424" y="4.8656"/></g></svg>
src/main/resources/images/mouse/2.svg
-<svg height="9.563195mm" viewBox="0 0 6.2268581 9.5631952" width="6.226858mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="-218.23392109934" x2="-212.53231434848" y1="188.98810309963" y2="188.98810309963"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-216.08975626054" x2="-214.80308722328" y1="186.3922928196" y2="186.3922928196"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><g transform="translate(218.55985 -184.2065)"><path d="m-212.333 188.71902c0 2.78941-1.39393 5.05068-3.11342 5.05068-1.7195 0-3.11343-2.26127-3.11343-5.05068s1.41839-4.51252 3.13789-4.51252c1.71949 0 3.08896 1.72311 3.08896 4.51252z" fill="url(#a)"/><g stroke="#333" stroke-width=".110192"><path d="m-215.44642 184.26823v4.18754" fill="none" stroke-linecap="round"/><path d="m-212.42857 187.95206c-1.85028.69374-4.00312.73681-6.03416 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="2.813062" rx=".574843" width="1.149686" x="-216.02127" y="184.98576"/></g><rect fill="#ff1500" fill-opacity=".25" fill-rule="evenodd" height="2.700655" rx=".537201" ry=".551873" width="1.074401" x="-215.98364" y="185.04196"/></g></svg>
+<svg height="60" viewBox="0 0 40 60" width="40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(6.4406 0 0 6.2464 .000017 -.00001)" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" xlink:href="#c" y1="1.7" y2="1.7"/><linearGradient id="b" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1="9.5" x2="14.098" xlink:href="#c" y1="6.46" y2="6.46"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" y1="1.7" y2="1.7"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><linearGradient id="d" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1=".95001" x2="22.686" y1="16.492" y2="16.492"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><g stroke-width=".69771"><g stroke="#333"><path d="m19.966.38727v26.235" fill="none" stroke-linecap="round"/><path d="m39.288 23.112c-12.237 4.31-25.762 4.6224-38.644 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#a)" fill-rule="evenodd" height="17.49" rx="3.6712" width="7.0847" x="16.102" y="4.8722"/></g><path d="m40 28.109c0 17.424-9.0169 31.89-20 31.89s-20-14.367-20-31.89 9.0169-28.109 20-28.109 20 10.619 20 28.109z" fill="url(#d)"/><g stroke="#333"><path d="m20 .38794v26.301" fill="none" stroke-linecap="round"/><path d="m39.322 23.342c-12.237 4.3068-25.762 4.6191-38.644 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="17.424" rx="3.6779" width="7.0847" x="16.424" y="4.8656"/></g></g><rect fill="#ff1500" fill-opacity=".25" fill-rule="evenodd" height="16.944" rx="3.2245" ry="3.4625" stroke-width="6.1367" width="6.4489" x="16.741" y="5.1058"/></svg>
src/main/resources/images/mouse/3.svg
-<svg height="9.563195mm" viewBox="0 0 6.2268581 9.5631952" width="6.226858mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="-218.23392109934" x2="-212.53231434848" y1="188.98810309963" y2="188.98810309963"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-216.08975626054" x2="-214.80308722328" y1="186.3922928196" y2="186.3922928196"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><g transform="translate(218.55985 -184.2065)"><path d="m-212.333 188.71902c0 2.78941-1.39393 5.05068-3.11342 5.05068-1.7195 0-3.11343-2.26127-3.11343-5.05068s1.41839-4.51252 3.13789-4.51252c1.71949 0 3.08896 1.72311 3.08896 4.51252z" fill="url(#a)"/><path d="m-215.44627 184.21104c1.05121 0 1.75568.55287 2.26286 1.28767.50884.73718.73446 1.66763.80868 2.43464-1.07427.36049-1.94146.53162-3.07154.53069z" fill="#40ff00" fill-opacity=".25" fill-rule="evenodd" stroke-width=".268987"/><g stroke="#333" stroke-width=".110192"><path d="m-215.44642 184.26823v4.18754" fill="none" stroke-linecap="round"/><path d="m-212.42857 187.95206c-1.85028.69374-4.00312.73681-6.03416 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="2.813062" rx=".574843" width="1.149686" x="-216.02127" y="184.98576"/></g></g></svg>
+<svg height="60" viewBox="0 0 40 60" width="40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(6.4406 0 0 6.2464 .000017 -.00001)" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" xlink:href="#c" y1="1.7" y2="1.7"/><linearGradient id="b" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1="9.5" x2="14.098" xlink:href="#c" y1="6.46" y2="6.46"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="2.45" x2="3.66" y1="1.7" y2="1.7"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><linearGradient id="d" gradientTransform="scale(1.6949 1.6438)" gradientUnits="userSpaceOnUse" x1=".95001" x2="22.686" y1="16.492" y2="16.492"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><g stroke="#333" stroke-width=".69771"><path d="m19.966.38727v26.235" fill="none" stroke-linecap="round"/><path d="m39.288 23.112c-12.237 4.31-25.762 4.6224-38.644 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#a)" fill-rule="evenodd" height="17.49" rx="3.6712" width="7.0847" x="16.102" y="4.8722"/></g><path d="m40 28.109c0 17.424-9.0169 31.89-20 31.89s-20-14.367-20-31.89 9.0169-28.109 20-28.109 20 10.619 20 28.109z" fill="url(#d)" stroke-width=".69771"/><path d="m20 .0051998c6.4635 0 11.4 3.5947 14.536 8.0789 3.2687 4.6251 4.7135 10.298 5.2149 15.107-6.9009 2.2617-12.492 3.5032-19.751 3.4974z" fill="#40ff00" fill-opacity=".25" fill-rule="evenodd" stroke-width="1.7077"/><g stroke="#333" stroke-width=".69771"><path d="m20 .38794v26.301" fill="none" stroke-linecap="round"/><path d="m39.322 23.342c-12.237 4.3068-25.762 4.6191-38.644 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="17.424" rx="3.6779" width="7.0847" x="16.424" y="4.8656"/></g></svg>
src/main/resources/images/mouse/all.svg
-<svg height="9.563195mm" viewBox="0 0 6.2268581 9.5631952" width="6.226858mm" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="-218.23392109934" x2="-212.53231434848" y1="188.98810309963" y2="188.98810309963"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#656565"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="-216.08975626054" x2="-214.80308722328" y1="186.3922928196" y2="186.3922928196"><stop offset="0" stop-color="#d6d6d6"/><stop offset="1" stop-color="#939393"/></linearGradient><g transform="translate(218.55985 -184.2065)"><path d="m-212.333 188.71902c0 2.78941-1.39393 5.05068-3.11342 5.05068-1.7195 0-3.11343-2.26127-3.11343-5.05068s1.41839-4.51252 3.13789-4.51252c1.71949 0 3.08896 1.72311 3.08896 4.51252z" fill="url(#a)"/><path d="m-215.44627 184.21104c1.05121 0 1.75568.55287 2.26286 1.28767.50884.73718.73446 1.66763.80868 2.43464-1.07427.36049-1.94146.53162-3.07154.53069z" fill="#40ff00" fill-opacity=".25" fill-rule="evenodd" stroke-width=".268987"/><path d="m-215.44807 184.20884c-.95261 0-1.73653.55323-2.24371 1.28803-.50884.73718-.75361 1.66727-.82783 2.43428 1.07427.36049 1.94146.53162 3.07154.53069z" fill="#0096ff" fill-opacity=".25" fill-rule="evenodd" stroke-width=".268987"/><g stroke="#333" stroke-width=".110192"><path d="m-215.44642 184.26823v4.18754" fill="none" stroke-linecap="round"/><path d="m-212.42857 187.95206c-1.85028.69374-4.00312.73681-6.03416 0" fill="none" stroke-linecap="round" stroke-linejoin="bevel"/><rect fill="url(#b)" fill-rule="evenodd" height="2.813062" rx=".574843" width="1.149686" x="-216.02127" y="184.98576"/></g><rect fill="#ff1500" fill-opacity=".25" fill-rule="evenodd" height="2.700655" rx=".537201" ry=".551873" width="1.074401" x="-215.98364" y="185.04196"/></g></svg>
+
Delta 401 lines added, 119 lines removed, 282-line increase