View Javadoc
1   /*
2    * Metis: Java Data Framework
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.metis.ui;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.oceanus.event.OceanusEvent;
21  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
22  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
23  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
24  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogManager;
25  import io.github.tonywasher.joceanus.oceanus.logger.OceanusLogger;
26  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceEvent;
27  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceManager;
28  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceResource;
29  import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet;
30  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
31  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
32  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIGenericWrapper;
33  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButton;
34  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButtonFactory;
35  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIScrollButtonManager;
36  import io.github.tonywasher.joceanus.tethys.api.control.TethysUILabel;
37  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
38  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollItem;
39  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
40  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBorderPaneManager;
41  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBoxPaneManager;
42  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUICardPaneManager;
43  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIPaneFactory;
44  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIScrollPaneManager;
45  
46  import java.util.ArrayList;
47  import java.util.List;
48  
49  /**
50   * Panel for editing preference Sets.
51   */
52  public class MetisPreferenceView
53          implements OceanusEventProvider<MetisPreferenceEvent>, TethysUIComponent {
54      /**
55       * Text for OK.
56       */
57      private static final String NLS_OK = MetisPreferenceResource.UI_BUTTON_OK.getValue();
58  
59      /**
60       * Text for Reset.
61       */
62      private static final String NLS_RESET = MetisPreferenceResource.UI_BUTTON_RESET.getValue();
63  
64      /**
65       * Text for Save.
66       */
67      private static final String NLS_SAVE = MetisPreferenceResource.UI_TITLE_SAVE.getValue();
68  
69      /**
70       * Text for Selection.
71       */
72      private static final String NLS_SELECT = MetisPreferenceResource.UI_TITLE_SELECT.getValue();
73  
74      /**
75       * Text for Set.
76       */
77      private static final String NLS_SET = MetisPreferenceResource.UI_LABEL_SET.getValue();
78  
79      /**
80       * Store data error text.
81       */
82      private static final String ERROR_STORE = MetisPreferenceResource.UI_ERROR_STORE.getValue();
83  
84      /**
85       * Logger.
86       */
87      private static final OceanusLogger LOGGER = OceanusLogManager.getLogger(MetisPreferenceView.class);
88  
89      /**
90       * The Event Manager.
91       */
92      private final OceanusEventManager<MetisPreferenceEvent> theEventManager;
93  
94      /**
95       * The GUI factory.
96       */
97      private final TethysUIFactory<?> theGuiFactory;
98  
99      /**
100      * The Border Pane.
101      */
102     private final TethysUIBorderPaneManager thePane;
103 
104     /**
105      * The selection button.
106      */
107     private final TethysUIScrollButtonManager<TethysUIGenericWrapper> theSelectButton;
108 
109     /**
110      * Preference menu.
111      */
112     private final TethysUIScrollMenu<TethysUIGenericWrapper> thePrefMenu;
113 
114     /**
115      * The Properties Pane.
116      */
117     private final TethysUICardPaneManager<MetisPreferenceSetView> theProperties;
118 
119     /**
120      * The Buttons Pane.
121      */
122     private final TethysUIBoxPaneManager theButtons;
123 
124     /**
125      * The list of views.
126      */
127     private final List<MetisPreferenceSetView> theViews;
128 
129     /**
130      * Constructor.
131      *
132      * @param pFactory       the GUI factory
133      * @param pPreferenceMgr the preference manager
134      */
135     public MetisPreferenceView(final TethysUIFactory<?> pFactory,
136                                final MetisPreferenceManager pPreferenceMgr) {
137         /* Store parameters */
138         theGuiFactory = pFactory;
139 
140         /* Create the event manager */
141         theEventManager = new OceanusEventManager<>();
142 
143         /* Create the buttons */
144         final TethysUIButtonFactory<?> myButtons = theGuiFactory.buttonFactory();
145         final TethysUIButton myOKButton = myButtons.newButton();
146         myOKButton.setTextOnly();
147         myOKButton.setText(NLS_OK);
148         final TethysUIButton myResetButton = myButtons.newButton();
149         myResetButton.setTextOnly();
150         myResetButton.setText(NLS_RESET);
151 
152         /* Add Listeners */
153         myOKButton.getEventRegistrar().addEventListener(e -> saveUpdates());
154         myResetButton.getEventRegistrar().addEventListener(e -> resetUpdates());
155 
156         /* Create the buttons box */
157         final TethysUIPaneFactory myPanes = theGuiFactory.paneFactory();
158         theButtons = myPanes.newHBoxPane();
159         theButtons.setBorderTitle(NLS_SAVE);
160         theButtons.addSpacer();
161         theButtons.addNode(myOKButton);
162         theButtons.addSpacer();
163         theButtons.addNode(myResetButton);
164         theButtons.addSpacer();
165 
166         /* Create the properties pane */
167         theProperties = myPanes.newCardPane();
168 
169         /* Create the view list */
170         theViews = new ArrayList<>();
171 
172         /* Loop through the existing property sets */
173         for (MetisPreferenceSet mySet : pPreferenceMgr.getPreferenceSets()) {
174             /* Register the Set */
175             registerSet(mySet);
176         }
177 
178         /* Add a listener for the addition of subsequent propertySets */
179         pPreferenceMgr.getEventRegistrar().addEventListener(this::handleNewPropertySet);
180 
181         /* Create selection button and label */
182         final TethysUILabel myLabel = theGuiFactory.controlFactory().newLabel(NLS_SET);
183         theSelectButton = myButtons.newScrollButton(TethysUIGenericWrapper.class);
184         thePrefMenu = theSelectButton.getMenu();
185 
186         /* Create the selection panel */
187         final TethysUIBoxPaneManager mySelection = myPanes.newHBoxPane();
188         mySelection.setBorderTitle(NLS_SELECT);
189 
190         /* Create the layout for the selection panel */
191         mySelection.addNode(myLabel);
192         mySelection.addNode(theSelectButton);
193         mySelection.addSpacer();
194 
195         /* Set listeners */
196         final OceanusEventRegistrar<TethysUIEvent> myRegistrar = theSelectButton.getEventRegistrar();
197         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handlePropertySetSelect());
198         theSelectButton.setMenuConfigurator(c -> buildPreferenceMenu());
199 
200         /* Create a new Scroll Pane and add the card to it */
201         final TethysUIScrollPaneManager myScrollPane = myPanes.newScrollPane();
202         myScrollPane.setContent(theProperties);
203 
204         /* Create the border pane */
205         thePane = myPanes.newBorderPane();
206         thePane.setNorth(mySelection);
207         thePane.setCentre(myScrollPane);
208         thePane.setSouth(theButtons);
209 
210         /* Determine the active items */
211         setSelectText();
212         setVisibility();
213     }
214 
215     @Override
216     public TethysUIComponent getUnderlying() {
217         return thePane;
218     }
219 
220     @Override
221     public OceanusEventRegistrar<MetisPreferenceEvent> getEventRegistrar() {
222         return theEventManager.getEventRegistrar();
223     }
224 
225     /**
226      * handle propertySetSelect event.
227      */
228     private void handlePropertySetSelect() {
229         final MetisPreferenceSetView myView = (MetisPreferenceSetView) theSelectButton.getValue().getData();
230         theProperties.selectCard(myView.toString());
231     }
232 
233     /**
234      * handle new propertySet event.
235      *
236      * @param pEvent the event
237      */
238     private void handleNewPropertySet(final OceanusEvent<MetisPreferenceEvent> pEvent) {
239         /* Details is the property set that has been added */
240         final MetisPreferenceSet mySet = pEvent.getDetails(MetisPreferenceSet.class);
241 
242         /* Register the set */
243         registerSet(mySet);
244 
245         /* Note that the panel should be re-displayed */
246         setVisibility();
247     }
248 
249     /**
250      * RegisterSet.
251      *
252      * @param pSet the set to register
253      */
254     private void registerSet(final MetisPreferenceSet pSet) {
255         /* Ignore hidden sets */
256         if (!pSet.isHidden()) {
257             /* Create the underlying view */
258             final MetisPreferenceSetView myView = createView(theGuiFactory, pSet);
259 
260             /* Add the view */
261             theProperties.addCard(myView.toString(), myView);
262             theViews.add(myView);
263 
264             /* Add listener */
265             myView.getEventRegistrar().addEventListener(e -> {
266                 setVisibility();
267                 theEventManager.fireEvent(MetisPreferenceEvent.PREFCHANGED);
268             });
269         }
270     }
271 
272     /**
273      * Create view for preference.
274      *
275      * @param pFactory the gui factory
276      * @param pSet     the set to register
277      * @return the view
278      */
279     protected MetisPreferenceSetView createView(final TethysUIFactory<?> pFactory,
280                                                 final MetisPreferenceSet pSet) {
281         /* Create the underlying view */
282         return new MetisPreferenceSetView(pFactory, pSet);
283     }
284 
285     /**
286      * Determine Focus.
287      */
288     public void determineFocus() {
289         /* Request the focus */
290         final MetisPreferenceSetView myPanel = theProperties.getActiveCard();
291         if (myPanel != null) {
292             myPanel.determineFocus();
293         }
294     }
295 
296     /**
297      * Does the panel have unsaved updates?
298      *
299      * @return true/false
300      */
301     public boolean hasUpdates() {
302         final MetisPreferenceSetView myView = theProperties.getActiveCard();
303         return (myView != null)
304                 && myView.hasChanges();
305     }
306 
307     /**
308      * Has this set of panels got the session focus?
309      *
310      * @return true/false
311      */
312     public boolean hasSession() {
313         return hasUpdates();
314     }
315 
316     /**
317      * Save Updates.
318      */
319     private void saveUpdates() {
320         try {
321             final MetisPreferenceSetView myView = theProperties.getActiveCard();
322             myView.storeChanges();
323         } catch (OceanusException e) {
324             LOGGER.error(ERROR_STORE, e);
325         }
326 
327         /* Set correct visibility */
328         setVisibility();
329 
330         /* Notify that state has changed */
331         theEventManager.fireEvent(MetisPreferenceEvent.PREFCHANGED);
332     }
333 
334     /**
335      * Reset Updates.
336      */
337     private void resetUpdates() {
338         /* Reset all changes */
339         final MetisPreferenceSetView myView = theProperties.getActiveCard();
340         myView.resetChanges();
341 
342         /* Set correct visibility */
343         setVisibility();
344 
345         /* Notify that state has changed */
346         theEventManager.fireEvent(MetisPreferenceEvent.PREFCHANGED);
347     }
348 
349     /**
350      * Set the visibility.
351      */
352     private void setVisibility() {
353         /* Enable selection */
354         final MetisPreferenceSetView myView = theProperties.getActiveCard();
355         theSelectButton.setEnabled((myView != null)
356                 && !myView.hasChanges());
357 
358         /* Show/Hide the buttons */
359         theButtons.setVisible((myView != null)
360                 && myView.hasChanges());
361     }
362 
363     /**
364      * Set the select button text.
365      */
366     private void setSelectText() {
367         /* Show selection text */
368         theSelectButton.setValue(new TethysUIGenericWrapper(theProperties.getActiveCard()));
369     }
370 
371     /**
372      * Show Preference menu.
373      */
374     private void buildPreferenceMenu() {
375         /* Reset the popUp menu */
376         thePrefMenu.removeAllItems();
377 
378         /* Record active item */
379         TethysUIScrollItem<?> myActive = null;
380         final String myActiveName = theProperties.getActiveName();
381 
382         /* Loop through the views */
383         for (MetisPreferenceSetView myView : theViews) {
384             /* Create a new MenuItem and add it to the popUp */
385             final TethysUIScrollItem<?> myItem = thePrefMenu.addItem(new TethysUIGenericWrapper(myView));
386 
387             /* If this is the active panel */
388             if (myView.toString().equals(myActiveName)) {
389                 /* Record it */
390                 myActive = myItem;
391             }
392         }
393 
394         /* Ensure active item is visible */
395         if (myActive != null) {
396             myActive.scrollToItem();
397         }
398     }
399 }