SwingAction.java
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2008-2021 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package griffon.swing.support;
019 
020 import griffon.core.CallableWithArgs;
021 import griffon.core.RunnableWithArgs;
022 
023 import javax.annotation.Nonnull;
024 import javax.annotation.Nullable;
025 import javax.swing.AbstractAction;
026 import javax.swing.Action;
027 import javax.swing.Icon;
028 import javax.swing.KeyStroke;
029 import java.awt.event.ActionEvent;
030 
031 import static griffon.util.GriffonNameUtils.isNotBlank;
032 import static java.util.Objects.requireNonNull;
033 
034 /**
035  * An action implementation that relies on a closure to handle events.
036  *
037  @author Andres Almiray
038  @since 2.0.0
039  */
040 public class SwingAction extends AbstractAction {
041     private static final long serialVersionUID = 4493562556110760713L;
042     private static final String ERROR_CALLABLE_NULL = "Argument 'callable' must not be null";
043     private static final String ERROR_RUNNABLE_NULL = "Argument 'runnable' must not be null";
044     private final transient RunnableWithArgs runnable;
045 
046     /**
047      * Creates a new action.
048      *
049      @param callable the body of the action
050      *
051      @deprecated use the {@code RunnableWithArgs} constructor instead.
052      */
053     @Deprecated
054     public SwingAction(@Nonnull final CallableWithArgs<?> callable) {
055         requireNonNull(callable, ERROR_CALLABLE_NULL);
056         this.runnable = callable::call;
057     }
058 
059     public SwingAction(@Nonnull RunnableWithArgs runnable) {
060         this.runnable = requireNonNull(runnable, ERROR_RUNNABLE_NULL);
061     }
062 
063     @Nonnull
064     public static ActionBuilder action() {
065         return new ActionBuilder();
066     }
067 
068     @Nonnull
069     public static ActionBuilder action(@Nullable Action action) {
070         return new ActionBuilder(action);
071     }
072 
073     @Nonnull
074     public static ActionBuilder action(@Nullable String name) {
075         return new ActionBuilder().withName(name);
076     }
077 
078     public final void actionPerformed(ActionEvent evt) {
079         runnable.run(evt);
080     }
081 
082     /**
083      * A builder for actions.
084      *
085      @author Andres Almiray
086      @since 2.0.0
087      */
088     public static class ActionBuilder {
089         private String name;
090         private int mnemonic;
091         private KeyStroke accelerator;
092         private String shortDescription;
093         private String longDescription;
094         private String command;
095         private Icon smallIcon;
096         private Icon largeIcon;
097         private RunnableWithArgs runnable;
098         private boolean enabled = true;
099         private boolean selected = false;
100 
101         private Action action;
102         private boolean mnemonicSet = false;
103         private boolean enabledSet = false;
104         private boolean selectedSet = false;
105 
106         public ActionBuilder() {
107             this(null);
108         }
109 
110         @Nonnull
111         public ActionBuilder(@Nullable Action action) {
112             this.action = action;
113         }
114 
115         @Nonnull
116         public ActionBuilder withName(@Nullable String name) {
117             this.name = name;
118             return this;
119         }
120 
121         @Nonnull
122         public ActionBuilder withShortDescription(@Nullable String shortDescription) {
123             this.shortDescription = shortDescription;
124             return this;
125         }
126 
127         @Nonnull
128         public ActionBuilder withLongDescription(@Nullable String longDescription) {
129             this.longDescription = longDescription;
130             return this;
131         }
132 
133         @Nonnull
134         public ActionBuilder withCommand(@Nullable String command) {
135             this.command = command;
136             return this;
137         }
138 
139         @Nonnull
140         public ActionBuilder withMnemonic(@Nullable String mnemonic) {
141             if (isNotBlank(mnemonic)) {
142                 this.mnemonic = KeyStroke.getKeyStroke(mnemonic).getKeyCode();
143             }
144             mnemonicSet = true;
145             return this;
146         }
147 
148         @Nonnull
149         public ActionBuilder withMnemonic(int mnemonic) {
150             this.mnemonic = mnemonic;
151             mnemonicSet = true;
152             return this;
153         }
154 
155         @Nonnull
156         public ActionBuilder withAccelerator(@Nullable String accelerator) {
157             if (isNotBlank(accelerator)) {
158                 this.accelerator = KeyStroke.getKeyStroke(accelerator);
159             }
160             return this;
161         }
162 
163         @Nonnull
164         public ActionBuilder withAccelerator(@Nullable KeyStroke accelerator) {
165             this.accelerator = accelerator;
166             return this;
167         }
168 
169         @Nonnull
170         public ActionBuilder withSmallIcon(@Nullable Icon smallIcon) {
171             this.smallIcon = smallIcon;
172             return this;
173         }
174 
175         @Nonnull
176         public ActionBuilder withLargeIcon(@Nullable Icon largeIcon) {
177             this.largeIcon = largeIcon;
178             return this;
179         }
180 
181         @Nonnull
182         @Deprecated
183         public ActionBuilder withRunnable(@Nullable final CallableWithArgs<?> callable) {
184             requireNonNull(callable, ERROR_CALLABLE_NULL);
185             this.runnable = callable::call;
186             return this;
187         }
188 
189         @Nonnull
190         public ActionBuilder withRunnable(@Nullable RunnableWithArgs runnable) {
191             this.runnable = runnable;
192             return this;
193         }
194 
195         @Nonnull
196         public ActionBuilder withEnabled(boolean enabled) {
197             this.enabled = enabled;
198             this.enabledSet = true;
199             return this;
200         }
201 
202         @Nonnull
203         public ActionBuilder withSelected(boolean selected) {
204             this.selected = selected;
205             this.selectedSet = true;
206             return this;
207         }
208 
209         @Nonnull
210         public Action build() {
211             if (runnable == null && action == null) {
212                 throw new IllegalArgumentException("Either runnable, callable, or action must have a value.");
213             }
214             if (action == null) {
215                 action = new SwingAction(runnable);
216             }
217             if (isNotBlank(command)) {
218                 action.putValue(Action.ACTION_COMMAND_KEY, command);
219             }
220             if (isNotBlank(name)) {
221                 action.putValue(Action.NAME, name);
222             }
223             if (mnemonicSet) {
224                 action.putValue(Action.MNEMONIC_KEY, mnemonic);
225             }
226             if (accelerator != null) {
227                 action.putValue(Action.ACCELERATOR_KEY, accelerator);
228             }
229             if (largeIcon != null) {
230                 action.putValue(Action.LARGE_ICON_KEY, largeIcon);
231             }
232             if (smallIcon != null) {
233                 action.putValue(Action.SMALL_ICON, smallIcon);
234             }
235             if (isNotBlank(longDescription)) {
236                 action.putValue(Action.LONG_DESCRIPTION, longDescription);
237             }
238             if (isNotBlank(shortDescription)) {
239                 action.putValue(Action.SHORT_DESCRIPTION, shortDescription);
240             }
241             if (enabledSet) {
242                 action.setEnabled(enabled);
243             }
244             if (selectedSet) {
245                 action.putValue(Action.SELECTED_KEY, selected);
246             }
247             return action;
248         }
249     }
250 }