2929
3030package org .scijava .desktop .options ;
3131
32- import java .io .IOException ;
33- import java .util .List ;
34- import java .util .function .Function ;
32+ import java .util .concurrent .Callable ;
33+ import java .util .stream .Stream ;
3534
35+ import org .scijava .ItemVisibility ;
3636import org .scijava .desktop .DesktopIntegrationProvider ;
3737import org .scijava .log .LogService ;
38+ import org .scijava .module .ModuleItem ;
39+ import org .scijava .module .MutableModuleItem ;
3840import org .scijava .options .OptionsPlugin ;
39- import org .scijava .platform .Platform ;
4041import org .scijava .platform .PlatformService ;
4142import org .scijava .plugin .Parameter ;
4243import org .scijava .plugin .Plugin ;
43- import org .scijava .plugin .PluginInfo ;
44- import org .scijava .plugin .SciJavaPlugin ;
4544
4645/**
4746 * Options plugin for managing desktop integration features.
4847 * <p>
49- * Provides a UI for enabling/disabling web links (URI schemes) and
48+ * Provides controls for enabling/disabling web links (URI schemes) and
5049 * desktop icons. Settings are applied directly to the OS (not persisted
5150 * to preferences), keeping the UI in sync with actual system state.
5251 * </p>
@@ -62,104 +61,135 @@ public class OptionsDesktop extends OptionsPlugin {
6261 @ Parameter (required = false )
6362 private LogService log ;
6463
65- @ Parameter (label = "Enable web links" , persist = false , validater = "validateWebLinks" , //
66- description = "Allow handling of URI link schemes from web browsers" )
67- private boolean webLinksEnabled ;
68-
69- @ Parameter (label = "Add desktop icon" , persist = false , validater = "validateDesktopIcon" , //
70- description = "Install application icon in the system menu" )
71- private boolean desktopIconPresent ;
72-
73- @ Parameter (label = "Enable file type associations" , persist = false , validater = "validateFileExtensions" , //
74- description = "Register supported file extensions with the operating system" )
75- private boolean fileExtensionsEnabled ;
64+ private MutableModuleItem <?> webLinksItem ;
65+ private MutableModuleItem <?> desktopIconItem ;
66+ private MutableModuleItem <?> fileTypesItem ;
7667
7768 @ Override
78- public void load () {
79- webLinksEnabled = true ;
80- desktopIconPresent = true ;
81- fileExtensionsEnabled = true ;
82- for (final Platform platform : platformService .getTargetPlatforms ()) {
83- if (!(platform instanceof DesktopIntegrationProvider )) continue ;
84- final DesktopIntegrationProvider dip = (DesktopIntegrationProvider ) platform ;
85- // If any toggleable platform setting is off, uncheck that box.
86- if (dip .isDesktopIconToggleable () && !dip .isDesktopIconPresent ()) desktopIconPresent = false ;
87- if (dip .isWebLinksToggleable () && !dip .isWebLinksEnabled ()) webLinksEnabled = false ;
88- if (dip .isFileExtensionsToggleable () && !dip .isFileExtensionsEnabled ()) fileExtensionsEnabled = false ;
69+ public void initialize () {
70+ // Create module inputs on first initialization.
71+ if (webLinksItem == null ) {
72+ webLinksItem = createInput ("webLinksEnabled" ,
73+ "Enable web links" ,
74+ "Allow handling of URI link schemes from web browsers" ,
75+ "Web links always enabled" , "Web links always disabled" ,
76+ isWebLinksToggleable (), isWebLinksEnabled ());
77+ }
78+ if (desktopIconItem == null ) {
79+ desktopIconItem = createInput ("desktopIconPresent" ,
80+ "Add desktop icon" ,
81+ "Install application icon in the system menu" ,
82+ "Icon always present" , "Icon installation not implemented" ,
83+ isDesktopIconToggleable (), isDesktopIconPresent ());
84+ }
85+ if (fileTypesItem == null ) {
86+ fileTypesItem = createInput ("fileTypesEnabled" ,
87+ "Enable file type associations" ,
88+ "Register supported file extensions with the operating system" ,
89+ "File types always handled" , "File type registration not supported" ,
90+ isFileExtensionsToggleable (), isFileExtensionsEnabled ());
8991 }
92+
93+ // Set module inputs to match current desktop integration values.
94+ setInputValue (webLinksItem , isWebLinksEnabled ());
95+ setInputValue (desktopIconItem , isDesktopIconPresent ());
96+ setInputValue (fileTypesItem , isFileExtensionsEnabled ());
9097 }
9198
9299 @ Override
93100 public void run () {
94- for (final Platform platform : platformService .getTargetPlatforms ()) {
95- if (!(platform instanceof DesktopIntegrationProvider )) continue ;
96- final DesktopIntegrationProvider dip = (DesktopIntegrationProvider ) platform ;
97- try {
98- dip .setWebLinksEnabled (webLinksEnabled );
99- dip .setDesktopIconPresent (desktopIconPresent );
100- dip .setFileExtensionsEnabled (fileExtensionsEnabled );
101+ desktopPlatforms ().forEach (p -> {
102+ if (p .isWebLinksToggleable ()) {
103+ final Boolean enabled = getInputValue (webLinksItem );
104+ toggle (() -> { p .setWebLinksEnabled (enabled ); return null ; },
105+ enabled ? "enabling web links" : "disabling web links" );
101106 }
102- catch ( final IOException e ) {
103- if ( log != null ) {
104- log . error ( "Error applying desktop integration settings" , e );
105- }
107+ if ( p . isDesktopIconToggleable () ) {
108+ final Boolean enabled = getInputValue ( desktopIconItem );
109+ toggle (() -> { p . setDesktopIconPresent ( enabled ); return null ; },
110+ enabled ? "adding desktop icon" : "removing desktop icon" );
106111 }
107- }
112+ if (p .isFileExtensionsToggleable ()) {
113+ final Boolean enabled = getInputValue (fileTypesItem );
114+ toggle (() -> { p .setFileExtensionsEnabled (enabled ); return null ; },
115+ enabled ? "enabling file type associations" :
116+ "removing file type associations" );
117+ }
118+ });
108119 super .run ();
109120 }
110121
111- // -- Validators --
122+ // -- Helper methods --
112123
113- public void validateWebLinks () {
114- validateSetting (
115- DesktopIntegrationProvider ::isWebLinksToggleable ,
116- DesktopIntegrationProvider ::isWebLinksEnabled ,
117- webLinksEnabled ,
118- "Web links setting" );
124+ private MutableModuleItem <?> createInput (final String name ,
125+ final String label , final String description ,
126+ final String alwaysOnMessage , final String alwaysOffMessage ,
127+ final boolean toggleable , final boolean enabled )
128+ {
129+ final MutableModuleItem <?> item ;
130+ if (toggleable ) {
131+ item = addInput (name , boolean .class );
132+ item .setLabel (label );
133+ }
134+ else {
135+ item = addInput (name , String .class );
136+ item .setVisibility (ItemVisibility .MESSAGE );
137+ item .setRequired (false );
138+ item .setLabel (enabled ?
139+ alwaysOnMessage + " for this platform ✅" :
140+ alwaysOffMessage + " for this platform ❌" );
141+ }
142+ item .setPersisted (false );
143+ item .setDescription (description );
144+ return item ;
119145 }
120146
121- public void validateDesktopIcon () {
122- validateSetting (
123- DesktopIntegrationProvider ::isDesktopIconToggleable ,
124- DesktopIntegrationProvider ::isDesktopIconPresent ,
125- desktopIconPresent ,
126- "Desktop icon presence" );
147+ private void setInputValue (final ModuleItem <?> item , boolean value ) {
148+ if (item .getType () != boolean .class ) return ;
149+ setInput (item .getName (), value );
127150 }
128151
129- public void validateFileExtensions () {
130- validateSetting (
131- DesktopIntegrationProvider ::isFileExtensionsToggleable ,
132- DesktopIntegrationProvider ::isFileExtensionsEnabled ,
133- fileExtensionsEnabled ,
134- "File extensions setting" );
152+ private Boolean getInputValue (final ModuleItem <?> item ) {
153+ if (item .getType () != boolean .class ) return null ;
154+ return (Boolean ) getInput (item .getName ());
135155 }
136156
137- // -- Helper methods --
157+ private void toggle (final Callable <?> toggleAction , final String errorMessage ) {
158+ try {
159+ toggleAction .call ();
160+ }
161+ catch (final Exception e ) {
162+ if (log != null ) log .error ("Error " + errorMessage , e );
163+ }
164+ }
138165
139- private String name (Platform platform ) {
140- final List <PluginInfo <SciJavaPlugin >> infos =
141- pluginService .getPluginsOfClass (platform .getClass ());
142- return infos .isEmpty () ? null : infos .get (0 ).getName ();
166+ private boolean isDesktopIconToggleable () {
167+ return desktopPlatforms ().anyMatch (p -> p .isDesktopIconToggleable ());
143168 }
144169
145- private void validateSetting (
146- Function <DesktopIntegrationProvider , Boolean > mutable ,
147- Function <DesktopIntegrationProvider , Boolean > getter ,
148- boolean value , String settingDescription )
149- {
150- boolean toggleable = false ;
151- boolean enabled = false ;
152- Platform strictPlatform = null ;
153- for (final Platform platform : platformService .getTargetPlatforms ()) {
154- if (!(platform instanceof DesktopIntegrationProvider )) continue ;
155- final DesktopIntegrationProvider dip = (DesktopIntegrationProvider ) platform ;
156- if (mutable .apply (dip )) toggleable = true ;
157- else if (strictPlatform == null ) strictPlatform = platform ;
158- if (getter .apply (dip )) enabled = true ;
159- }
160- if (!toggleable && enabled != value ) {
161- final String platformName = strictPlatform == null ? "this platform" : name (strictPlatform );
162- throw new IllegalArgumentException (settingDescription + " cannot be changed on " + platformName + "." );
163- }
170+ private boolean isDesktopIconPresent () {
171+ return desktopPlatforms ().allMatch (p -> p .isDesktopIconPresent ());
172+ }
173+
174+ private boolean isWebLinksToggleable () {
175+ return desktopPlatforms ().anyMatch (p -> p .isWebLinksToggleable ());
176+ }
177+
178+ private boolean isWebLinksEnabled () {
179+ return desktopPlatforms ().allMatch (p -> p .isWebLinksEnabled ());
180+ }
181+
182+ private boolean isFileExtensionsToggleable () {
183+ return desktopPlatforms ().anyMatch (p -> p .isFileExtensionsToggleable ());
184+ }
185+
186+ private boolean isFileExtensionsEnabled () {
187+ return desktopPlatforms ().allMatch (p -> p .isFileExtensionsEnabled ());
188+ }
189+
190+ private Stream <DesktopIntegrationProvider > desktopPlatforms () {
191+ return platformService .getTargetPlatforms ().stream () //
192+ .filter (p -> p instanceof DesktopIntegrationProvider ) //
193+ .map (p -> (DesktopIntegrationProvider ) p );
164194 }
165195}
0 commit comments