View Javadoc
1   /*
2    * MoneyWise: Finance Application
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.moneywise.atlas.ui.controls;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysis;
21  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisBucketResource;
22  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisTaxBasisAccountBucket;
23  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisTaxBasisBaseBucket;
24  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisTaxBasisBucket;
25  import io.github.tonywasher.joceanus.moneywise.atlas.data.analysis.buckets.MoneyWiseXAnalysisTaxBasisBucket.MoneyWiseXAnalysisTaxBasisBucketList;
26  import io.github.tonywasher.joceanus.moneywise.atlas.views.MoneyWiseXAnalysisFilter;
27  import io.github.tonywasher.joceanus.moneywise.atlas.views.MoneyWiseXAnalysisFilter.MoneyWiseXAnalysisTaxBasisFilter;
28  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
29  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTaxBasis;
30  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
31  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
32  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
33  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
34  import io.github.tonywasher.joceanus.prometheus.views.PrometheusDataEvent;
35  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
36  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIConstant;
37  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
38  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButtonFactory;
39  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIScrollButtonManager;
40  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControlFactory;
41  import io.github.tonywasher.joceanus.tethys.api.control.TethysUILabel;
42  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
43  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollItem;
44  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
45  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBoxPaneManager;
46  
47  import java.util.Iterator;
48  
49  /**
50   * TaxBasisAnalysis Selection.
51   */
52  public class MoneyWiseUIXTaxBasisAnalysisSelect
53          implements MoneyWiseUIXAnalysisFilterSelection, OceanusEventProvider<PrometheusDataEvent> {
54      /**
55       * Text for TaxBasis Label.
56       */
57      private static final String NLS_BASIS = MoneyWiseStaticDataType.TAXBASIS.getItemName();
58  
59      /**
60       * Text for Account Label.
61       */
62      private static final String NLS_ACCOUNT = MoneyWiseXAnalysisBucketResource.BUCKET_ACCOUNT.getValue();
63  
64      /**
65       * Text for All Item.
66       */
67      private static final String NLS_ALL = "All";
68  
69      /**
70       * The Event Manager.
71       */
72      private final OceanusEventManager<PrometheusDataEvent> theEventManager;
73  
74      /**
75       * The panel.
76       */
77      private final TethysUIBoxPaneManager thePanel;
78  
79      /**
80       * The basis button.
81       */
82      private final TethysUIScrollButtonManager<MoneyWiseXAnalysisTaxBasisBucket> theBasisButton;
83  
84      /**
85       * The account button.
86       */
87      private final TethysUIScrollButtonManager<MoneyWiseXAnalysisTaxBasisAccountBucket> theAccountButton;
88  
89      /**
90       * Tax menu.
91       */
92      private final TethysUIScrollMenu<MoneyWiseXAnalysisTaxBasisBucket> theTaxMenu;
93  
94      /**
95       * Account menu.
96       */
97      private final TethysUIScrollMenu<MoneyWiseXAnalysisTaxBasisAccountBucket> theAccountMenu;
98  
99      /**
100      * The active tax basis bucket list.
101      */
102     private MoneyWiseXAnalysisTaxBasisBucketList theTaxBases;
103 
104     /**
105      * The state.
106      */
107     private MoneyWiseTaxBasisState theState;
108 
109     /**
110      * The savePoint.
111      */
112     private MoneyWiseTaxBasisState theSavePoint;
113 
114     /**
115      * Constructor.
116      *
117      * @param pFactory the GUI factory
118      */
119     protected MoneyWiseUIXTaxBasisAnalysisSelect(final TethysUIFactory<?> pFactory) {
120         /* Create the buttons */
121         final TethysUIButtonFactory<?> myButtons = pFactory.buttonFactory();
122         theBasisButton = myButtons.newScrollButton(MoneyWiseXAnalysisTaxBasisBucket.class);
123         theAccountButton = myButtons.newScrollButton(MoneyWiseXAnalysisTaxBasisAccountBucket.class);
124 
125         /* Create Event Manager */
126         theEventManager = new OceanusEventManager<>();
127 
128         /* Create the labels */
129         final TethysUIControlFactory myControls = pFactory.controlFactory();
130         final TethysUILabel myBasisLabel = myControls.newLabel(NLS_BASIS + TethysUIConstant.STR_COLON);
131         final TethysUILabel myAccountLabel = myControls.newLabel(NLS_ACCOUNT + TethysUIConstant.STR_COLON);
132 
133         /* Define the layout */
134         thePanel = pFactory.paneFactory().newHBoxPane();
135         thePanel.addSpacer();
136         thePanel.addNode(myBasisLabel);
137         thePanel.addNode(theBasisButton);
138         thePanel.addStrut();
139         thePanel.addNode(myAccountLabel);
140         thePanel.addNode(theAccountButton);
141 
142         /* Create initial state */
143         theState = new MoneyWiseTaxBasisState();
144         theState.applyState();
145 
146         /* Access the menus */
147         theTaxMenu = theBasisButton.getMenu();
148         theAccountMenu = theAccountButton.getMenu();
149 
150         /* Create the listener */
151         OceanusEventRegistrar<TethysUIEvent> myRegistrar = theBasisButton.getEventRegistrar();
152         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewBasis());
153         theBasisButton.setMenuConfigurator(e -> buildBasisMenu());
154         myRegistrar = theAccountButton.getEventRegistrar();
155         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewAccount());
156         theAccountButton.setMenuConfigurator(e -> buildAccountMenu());
157     }
158 
159     @Override
160     public TethysUIComponent getUnderlying() {
161         return thePanel;
162     }
163 
164     @Override
165     public OceanusEventRegistrar<PrometheusDataEvent> getEventRegistrar() {
166         return theEventManager.getEventRegistrar();
167     }
168 
169     @Override
170     public MoneyWiseXAnalysisTaxBasisFilter getFilter() {
171         return theState.getFilter();
172     }
173 
174     @Override
175     public boolean isAvailable() {
176         return theTaxBases != null
177                 && !theTaxBases.isEmpty();
178     }
179 
180     /**
181      * Create SavePoint.
182      */
183     public void createSavePoint() {
184         /* Create the savePoint */
185         theSavePoint = new MoneyWiseTaxBasisState(theState);
186     }
187 
188     /**
189      * Restore SavePoint.
190      */
191     public void restoreSavePoint() {
192         /* Restore the savePoint */
193         theState = new MoneyWiseTaxBasisState(theSavePoint);
194 
195         /* Apply the state */
196         theState.applyState();
197     }
198 
199     @Override
200     public void setEnabled(final boolean bEnabled) {
201         /* Pass call on to basis button */
202         theBasisButton.setEnabled(bEnabled && isAvailable());
203     }
204 
205     @Override
206     public void setVisible(final boolean pVisible) {
207         thePanel.setVisible(pVisible);
208     }
209 
210     /**
211      * Set analysis.
212      *
213      * @param pAnalysis the analysis.
214      */
215     public void setAnalysis(final MoneyWiseXAnalysis pAnalysis) {
216         /* Access buckets */
217         theTaxBases = pAnalysis.getTaxBasis();
218 
219         /* Obtain the current basis */
220         MoneyWiseXAnalysisTaxBasisBaseBucket myBasis = theState.getTaxBasis();
221 
222         /* Switch to versions from the analysis */
223         myBasis = myBasis != null
224                 ? theTaxBases.getMatchingBasis(myBasis)
225                 : theTaxBases.getDefaultBasis();
226 
227         /* Set the basis */
228         theState.setTheTaxBasis(myBasis);
229         theState.setDateRange(pAnalysis.getDateRange());
230         theState.applyState();
231     }
232 
233     @Override
234     public void setFilter(final MoneyWiseXAnalysisFilter<?, ?> pFilter) {
235         /* If this is the correct filter type */
236         if (pFilter instanceof MoneyWiseXAnalysisTaxBasisFilter myFilter) {
237             /* Obtain the filter bucket */
238             MoneyWiseXAnalysisTaxBasisBaseBucket myTaxBasis = myFilter.getBucket();
239 
240             /* Obtain equivalent bucket */
241             myTaxBasis = theTaxBases.getMatchingBasis(myTaxBasis);
242 
243             /* Set the taxBasis */
244             theState.setTheTaxBasis(myTaxBasis);
245             theState.setDateRange(myFilter.getDateRange());
246             theState.applyState();
247         }
248     }
249 
250     /**
251      * Handle new Basis.
252      */
253     private void handleNewBasis() {
254         /* Select the new taxBasis */
255         if (theState.setTaxBasis(theBasisButton.getValue())) {
256             theState.applyState();
257             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
258         }
259     }
260 
261     /**
262      * Handle new Account.
263      */
264     private void handleNewAccount() {
265         /* Select the new account */
266         if (theState.setTaxBasis(theAccountButton.getValue())) {
267             theState.applyState();
268             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
269         }
270     }
271 
272     /**
273      * Build Basis menu.
274      */
275     private void buildBasisMenu() {
276         /* Reset the popUp menu */
277         theTaxMenu.removeAllItems();
278 
279         /* Record active item */
280         TethysUIScrollItem<MoneyWiseXAnalysisTaxBasisBucket> myActive = null;
281         final MoneyWiseXAnalysisTaxBasisBucket myCurr = theState.getTaxBasis();
282 
283         /* Loop through the available basis values */
284         final Iterator<MoneyWiseXAnalysisTaxBasisBucket> myIterator = theTaxBases.iterator();
285         while (myIterator.hasNext()) {
286             final MoneyWiseXAnalysisTaxBasisBucket myBucket = myIterator.next();
287 
288             /* Create a new MenuItem and add it to the popUp */
289             final TethysUIScrollItem<MoneyWiseXAnalysisTaxBasisBucket> myItem = theTaxMenu.addItem(myBucket);
290 
291             /* If this is the active bucket */
292             if (myBucket.equals(myCurr)) {
293                 /* Record it */
294                 myActive = myItem;
295             }
296         }
297 
298         /* Ensure active item is visible */
299         if (myActive != null) {
300             myActive.scrollToItem();
301         }
302     }
303 
304     /**
305      * Build Account menu.
306      */
307     private void buildAccountMenu() {
308         /* Reset the popUp menu */
309         theAccountMenu.removeAllItems();
310 
311         /* Record active item */
312         final MoneyWiseXAnalysisTaxBasisBucket myBasis = theState.getTaxBasis();
313         final MoneyWiseXAnalysisTaxBasisAccountBucket myCurr = theState.getAccount();
314 
315         /* Add the all item menu */
316         TethysUIScrollItem<MoneyWiseXAnalysisTaxBasisAccountBucket> myActive = theAccountMenu.addItem(null, NLS_ALL);
317 
318         /* Loop through the available account values */
319         final Iterator<MoneyWiseXAnalysisTaxBasisAccountBucket> myIterator = myBasis.accountIterator();
320         while (myIterator.hasNext()) {
321             final MoneyWiseXAnalysisTaxBasisAccountBucket myBucket = myIterator.next();
322 
323             /* Create a new MenuItem and add it to the popUp */
324             final TethysUIScrollItem<MoneyWiseXAnalysisTaxBasisAccountBucket> myItem = theAccountMenu.addItem(myBucket, myBucket.getSimpleName());
325 
326             /* If this is the active bucket */
327             if (myBucket.equals(myCurr)) {
328                 /* Record it */
329                 myActive = myItem;
330             }
331         }
332 
333         /* Ensure active item is visible */
334         if (myActive != null) {
335             myActive.scrollToItem();
336         }
337     }
338 
339     /**
340      * SavePoint values.
341      */
342     private final class MoneyWiseTaxBasisState {
343         /**
344          * The active TaxBasisBucket.
345          */
346         private MoneyWiseXAnalysisTaxBasisBucket theBasis;
347 
348         /**
349          * The account TaxBasisBucket.
350          */
351         private MoneyWiseXAnalysisTaxBasisAccountBucket theAccount;
352 
353         /**
354          * The dateRange.
355          */
356         private OceanusDateRange theDateRange;
357 
358         /**
359          * The active filter.
360          */
361         private MoneyWiseXAnalysisTaxBasisFilter theFilter;
362 
363         /**
364          * Constructor.
365          */
366         private MoneyWiseTaxBasisState() {
367         }
368 
369         /**
370          * Constructor.
371          *
372          * @param pState state to copy from
373          */
374         private MoneyWiseTaxBasisState(final MoneyWiseTaxBasisState pState) {
375             /* Initialise state */
376             theBasis = pState.getTaxBasis();
377             theAccount = pState.getAccount();
378             theDateRange = pState.getDateRange();
379             theFilter = pState.getFilter();
380         }
381 
382         /**
383          * Obtain the TaxBasis Bucket.
384          *
385          * @return the Basis
386          */
387         private MoneyWiseXAnalysisTaxBasisBucket getTaxBasis() {
388             return theBasis;
389         }
390 
391         /**
392          * Obtain the Account Bucket.
393          *
394          * @return the Account
395          */
396         private MoneyWiseXAnalysisTaxBasisAccountBucket getAccount() {
397             return theAccount;
398         }
399 
400         /**
401          * Obtain the dateRange.
402          *
403          * @return the dateRange
404          */
405         private OceanusDateRange getDateRange() {
406             return theDateRange;
407         }
408 
409         /**
410          * Obtain the Filter.
411          *
412          * @return the Filter
413          */
414         private MoneyWiseXAnalysisTaxBasisFilter getFilter() {
415             return theFilter;
416         }
417 
418         /**
419          * Set new TaxBasis.
420          *
421          * @param pTaxBasis the TaxBasis
422          * @return true/false did a change occur
423          */
424         private boolean setTaxBasis(final MoneyWiseXAnalysisTaxBasisBaseBucket pTaxBasis) {
425             /* Adjust the selected taxBasis */
426             if (!MetisDataDifference.isEqual(pTaxBasis, theBasis)) {
427                 setTheTaxBasis(pTaxBasis);
428                 return true;
429             }
430             return false;
431         }
432 
433         /**
434          * Set the TaxBasis.
435          *
436          * @param pTaxBasis the TaxBasis
437          */
438         private void setTheTaxBasis(final MoneyWiseXAnalysisTaxBasisBaseBucket pTaxBasis) {
439             /* Adjust the selected taxBasis */
440             if (pTaxBasis instanceof MoneyWiseXAnalysisTaxBasisAccountBucket myBucket) {
441                 theAccount = myBucket;
442                 final MoneyWiseTaxBasis myTaxBasis = theAccount.getTaxBasis();
443                 theBasis = theTaxBases.findItemById(myTaxBasis.getIndexedId());
444                 theFilter = new MoneyWiseXAnalysisTaxBasisFilter(theAccount);
445                 theFilter.setDateRange(theDateRange);
446             } else {
447                 theAccount = null;
448                 theBasis = (MoneyWiseXAnalysisTaxBasisBucket) pTaxBasis;
449                 if (theBasis != null) {
450                     theFilter = new MoneyWiseXAnalysisTaxBasisFilter(theBasis);
451                     theFilter.setDateRange(theDateRange);
452                 } else {
453                     theFilter = null;
454                 }
455             }
456         }
457 
458         /**
459          * Set the dateRange.
460          *
461          * @param pRange the dateRange
462          */
463         private void setDateRange(final OceanusDateRange pRange) {
464             /* Store the dateRange */
465             theDateRange = pRange;
466             if (theFilter != null) {
467                 theFilter.setDateRange(theDateRange);
468             }
469         }
470 
471         /**
472          * Apply the State.
473          */
474         private void applyState() {
475             /* Adjust the lock-down */
476             setEnabled(true);
477             theBasisButton.setValue(theBasis);
478             if (theAccount == null) {
479                 theAccountButton.setValue(null, NLS_ALL);
480             } else {
481                 theAccountButton.setValue(theAccount, theAccount.getSimpleName());
482             }
483             theAccountButton.setEnabled((theBasis != null) && theBasis.hasAccounts());
484         }
485     }
486 }