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.lethe.ui.controls;
18  
19  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
20  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateResource;
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.metis.data.MetisDataDifference;
25  import io.github.tonywasher.joceanus.metis.ui.MetisIcon;
26  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.base.MoneyWiseAnalysisAttribute;
27  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
28  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisType;
29  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter;
30  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter.MoneyWiseAnalysisAllFilter;
31  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisView;
32  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseAnalysisColumnSet;
33  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
34  import io.github.tonywasher.joceanus.moneywise.views.MoneyWiseView;
35  import io.github.tonywasher.joceanus.prometheus.views.PrometheusDataEvent;
36  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIArrowIconId;
37  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
38  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
39  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButton;
40  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButtonFactory;
41  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIDateRangeSelector;
42  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIScrollButtonManager;
43  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControlFactory;
44  import io.github.tonywasher.joceanus.tethys.api.control.TethysUILabel;
45  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
46  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
47  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBoxPaneManager;
48  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUICardPaneManager;
49  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIPaneFactory;
50  
51  import java.util.EnumMap;
52  import java.util.Map;
53  import java.util.Map.Entry;
54  
55  /**
56   * Selection panel for Analysis Statement.
57   */
58  public class MoneyWiseAnalysisSelect
59          implements OceanusEventProvider<PrometheusDataEvent>, TethysUIComponent {
60      /**
61       * Text for DateRange Label.
62       */
63      private static final String NLS_RANGE = MoneyWiseUIResource.ANALYSIS_PROMPT_RANGE.getValue();
64  
65      /**
66       * Text for Filter Label.
67       */
68      private static final String NLS_FILTER = MoneyWiseUIResource.ANALYSIS_PROMPT_FILTER.getValue();
69  
70      /**
71       * Text for FilterType Label.
72       */
73      private static final String NLS_FILTERTYPE = MoneyWiseUIResource.ANALYSIS_PROMPT_FILTERTYPE.getValue();
74  
75      /**
76       * Text for ColumnSet Label.
77       */
78      private static final String NLS_COLUMNS = MoneyWiseUIResource.ANALYSIS_PROMPT_COLUMNSET.getValue();
79  
80      /**
81       * Text for BucketType Label.
82       */
83      private static final String NLS_BUCKET = MoneyWiseUIResource.ANALYSIS_PROMPT_BUCKET.getValue();
84  
85      /**
86       * Text for Title.
87       */
88      private static final String NLS_TITLE = MoneyWiseUIResource.ANALYSIS_TITLE.getValue();
89  
90      /**
91       * Text for NoBucket.
92       */
93      private static final String NLS_NONE = MoneyWiseUIResource.ANALYSIS_BUCKET_NONE.getValue();
94  
95      /**
96       * Text for Title.
97       */
98      private static final String NLS_FILTERTITLE = MoneyWiseUIResource.ANALYSIS_FILTER_TITLE.getValue();
99  
100     /**
101      * Text for Box Title.
102      */
103     private static final String NLS_RANGETITLE = OceanusDateResource.TITLE_BOX.getValue();
104 
105     /**
106      * The Event Manager.
107      */
108     private final OceanusEventManager<PrometheusDataEvent> theEventManager;
109 
110     /**
111      * View.
112      */
113     private final MoneyWiseView theView;
114 
115     /**
116      * Panel.
117      */
118     private final TethysUIBoxPaneManager thePanel;
119 
120     /**
121      * Range Button.
122      */
123     private final TethysUIButton theRangeButton;
124 
125     /**
126      * Filter Button.
127      */
128     private final TethysUIButton theFilterButton;
129 
130     /**
131      * Filter Type Button.
132      */
133     private final TethysUIScrollButtonManager<MoneyWiseAnalysisType> theFilterTypeButton;
134 
135     /**
136      * Bucket Type Button.
137      */
138     private final TethysUIScrollButtonManager<MoneyWiseAnalysisAttribute> theBucketButton;
139 
140     /**
141      * ColumnSet Button.
142      */
143     private final TethysUIScrollButtonManager<MoneyWiseAnalysisColumnSet> theColumnButton;
144 
145     /**
146      * The bucket label.
147      */
148     private final TethysUILabel theBucketLabel;
149 
150     /**
151      * The column label.
152      */
153     private final TethysUILabel theColumnLabel;
154 
155     /**
156      * The filter detail panel.
157      */
158     private final TethysUIBoxPaneManager theFilterDetail;
159 
160     /**
161      * DateRange Select Panel.
162      */
163     private final TethysUIDateRangeSelector theRangeSelect;
164 
165     /**
166      * Filter Select Panel.
167      */
168     private final TethysUIBoxPaneManager theFilterSelect;
169 
170     /**
171      * Deposit Select Panel.
172      */
173     private final MoneyWiseDepositAnalysisSelect theDepositSelect;
174 
175     /**
176      * Cash Select Panel.
177      */
178     private final MoneyWiseCashAnalysisSelect theCashSelect;
179 
180     /**
181      * Loan Select Panel.
182      */
183     private final MoneyWiseLoanAnalysisSelect theLoanSelect;
184 
185     /**
186      * Security Select Panel.
187      */
188     private final MoneyWiseSecurityAnalysisSelect theSecuritySelect;
189 
190     /**
191      * Portfolio Select Panel.
192      */
193     private final MoneyWisePortfolioAnalysisSelect thePortfolioSelect;
194 
195     /**
196      * Payee Select Panel.
197      */
198     private final MoneyWisePayeeAnalysisSelect thePayeeSelect;
199 
200     /**
201      * TransCategory Select Panel.
202      */
203     private final MoneyWiseTransCategoryAnalysisSelect theCategorySelect;
204 
205     /**
206      * TaxBasis Select Panel.
207      */
208     private final MoneyWiseTaxBasisAnalysisSelect theTaxBasisSelect;
209 
210     /**
211      * TransactionTag Select Panel.
212      */
213     private final MoneyWiseTransTagSelect theTagSelect;
214 
215     /**
216      * All Select Panel.
217      */
218     private final MoneyWiseAllSelect theAllSelect;
219 
220     /**
221      * The card panel.
222      */
223     private final TethysUICardPaneManager<MoneyWiseAnalysisFilterSelection> theCardPanel;
224 
225     /**
226      * The analysis view.
227      */
228     private final MoneyWiseAnalysisView theAnalysisView;
229 
230     /**
231      * Select panel map.
232      */
233     private final Map<MoneyWiseAnalysisType, MoneyWiseAnalysisFilterSelection> theMap;
234 
235     /**
236      * AnalysisType menu.
237      */
238     private final TethysUIScrollMenu<MoneyWiseAnalysisType> theTypeMenu;
239 
240     /**
241      * Bucket menu.
242      */
243     private final TethysUIScrollMenu<MoneyWiseAnalysisAttribute> theBucketMenu;
244 
245     /**
246      * Column menu.
247      */
248     private final TethysUIScrollMenu<MoneyWiseAnalysisColumnSet> theColumnMenu;
249 
250     /**
251      * Analysis.
252      */
253     private MoneyWiseAnalysis theAnalysis;
254 
255     /**
256      * Analysis State.
257      */
258     private MoneyWiseAnalysisState theState;
259 
260     /**
261      * The savePoint.
262      */
263     private MoneyWiseAnalysisState theSavePoint;
264 
265     /**
266      * Is the control refreshing?
267      */
268     private boolean isRefreshing;
269 
270     /**
271      * Is the range visible?
272      */
273     private boolean isRangeVisible;
274 
275     /**
276      * Is the filter visible?
277      */
278     private boolean isFilterVisible;
279 
280     /**
281      * Constructor.
282      *
283      * @param pFactory      the GUI factory
284      * @param pView         the view
285      * @param pAnalysisView the analysis view
286      * @param pNewButton    the new button
287      */
288     public MoneyWiseAnalysisSelect(final TethysUIFactory<?> pFactory,
289                                    final MoneyWiseView pView,
290                                    final MoneyWiseAnalysisView pAnalysisView,
291                                    final TethysUIButton pNewButton) {
292         /* Access the analysis manager */
293         theView = pView;
294         theAnalysisView = pAnalysisView;
295 
296         /* Create Event Manager */
297         theEventManager = new OceanusEventManager<>();
298 
299         /* Create the range button */
300         final TethysUIButtonFactory<?> myButtons = pFactory.buttonFactory();
301         theRangeButton = myButtons.newButton();
302         theRangeButton.setIcon(TethysUIArrowIconId.DOWN);
303         theRangeButton.setTextAndIcon();
304 
305         /* Create the filter button */
306         theFilterButton = myButtons.newButton();
307         theFilterButton.setIcon(TethysUIArrowIconId.DOWN);
308         theFilterButton.setTextAndIcon();
309 
310         /* Create the filter type button */
311         theFilterTypeButton = myButtons.newScrollButton(MoneyWiseAnalysisType.class);
312 
313         /* Create the columnSet button */
314         final TethysUIControlFactory myControls = pFactory.controlFactory();
315         theColumnLabel = myControls.newLabel(NLS_COLUMNS);
316         theColumnButton = myButtons.newScrollButton(MoneyWiseAnalysisColumnSet.class);
317 
318         /* Create the bucket button */
319         theBucketLabel = myControls.newLabel(NLS_BUCKET);
320         theBucketButton = myButtons.newScrollButton(MoneyWiseAnalysisAttribute.class);
321 
322         /* Create the Range Select panel */
323         theRangeSelect = myButtons.newDateRangeSelector();
324         theRangeSelect.setBorderTitle(NLS_RANGETITLE);
325 
326         /* Create the panel map */
327         theMap = new EnumMap<>(MoneyWiseAnalysisType.class);
328 
329         /* Create the filter selection panels */
330         theDepositSelect = new MoneyWiseDepositAnalysisSelect(pFactory);
331         theCashSelect = new MoneyWiseCashAnalysisSelect(pFactory);
332         theLoanSelect = new MoneyWiseLoanAnalysisSelect(pFactory);
333         theSecuritySelect = new MoneyWiseSecurityAnalysisSelect(pFactory);
334         thePortfolioSelect = new MoneyWisePortfolioAnalysisSelect(pFactory);
335         thePayeeSelect = new MoneyWisePayeeAnalysisSelect(pFactory);
336         theCategorySelect = new MoneyWiseTransCategoryAnalysisSelect(pFactory);
337         theTaxBasisSelect = new MoneyWiseTaxBasisAnalysisSelect(pFactory);
338         theTagSelect = new MoneyWiseTransTagSelect(pFactory);
339         theAllSelect = new MoneyWiseAllSelect(pFactory);
340 
341         /* Create the card panel */
342         final TethysUIPaneFactory myPanes = pFactory.paneFactory();
343         theCardPanel = myPanes.newCardPane();
344 
345         /* Create the filter detail panel */
346         theFilterDetail = buildFilterDetail(pFactory);
347 
348         /* Create the filter selection panel */
349         theFilterSelect = buildFilterSelect(pFactory);
350 
351         /* Create the control panel */
352         final TethysUIBoxPaneManager myPanel = buildControlPanel(pFactory, pNewButton);
353 
354         /* Create the panel */
355         thePanel = myPanes.newVBoxPane();
356         thePanel.addNode(myPanel);
357         thePanel.addNode(theRangeSelect);
358         thePanel.addNode(theFilterSelect);
359 
360         /* Initially hide the select boxes */
361         theRangeSelect.setVisible(false);
362         theFilterSelect.setVisible(false);
363 
364         /* Create initial state */
365         theState = new MoneyWiseAnalysisState();
366         theState.showColumns(true);
367         final MoneyWiseStatementSelect mySelect = new MoneyWiseStatementSelect(theRangeSelect, new MoneyWiseAnalysisAllFilter());
368         selectStatement(mySelect);
369 
370         /* Access the menus */
371         theTypeMenu = theFilterTypeButton.getMenu();
372         theBucketMenu = theBucketButton.getMenu();
373         theColumnMenu = theColumnButton.getMenu();
374 
375         /* Create the listeners */
376         OceanusEventRegistrar<TethysUIEvent> myRegistrar = theFilterTypeButton.getEventRegistrar();
377         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleFilterType());
378         theFilterTypeButton.setMenuConfigurator(e -> buildAnalysisTypeMenu());
379         myRegistrar = theBucketButton.getEventRegistrar();
380         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewBucket());
381         theBucketButton.setMenuConfigurator(e -> buildBucketMenu());
382         myRegistrar = theColumnButton.getEventRegistrar();
383         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewColumns());
384         theColumnButton.setMenuConfigurator(e -> buildColumnsMenu());
385         theAnalysisView.getEventRegistrar().addEventListener(e -> setAnalysisView());
386 
387         /* Handle buttons */
388         theRangeButton.getEventRegistrar().addEventListener(e -> setRangeVisibility(!isRangeVisible));
389         theFilterButton.getEventRegistrar().addEventListener(e -> setFilterVisibility(!isFilterVisible));
390         theRangeSelect.getEventRegistrar().addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewRange());
391 
392         /* handle sub-selections */
393         theDepositSelect.getEventRegistrar().addEventListener(e -> buildDepositFilter());
394         theCashSelect.getEventRegistrar().addEventListener(e -> buildCashFilter());
395         theLoanSelect.getEventRegistrar().addEventListener(e -> buildLoanFilter());
396         theSecuritySelect.getEventRegistrar().addEventListener(e -> buildSecurityFilter());
397         thePortfolioSelect.getEventRegistrar().addEventListener(e -> buildPortfolioFilter());
398         thePayeeSelect.getEventRegistrar().addEventListener(e -> buildPayeeFilter());
399         theCategorySelect.getEventRegistrar().addEventListener(e -> buildCategoryFilter());
400         theTaxBasisSelect.getEventRegistrar().addEventListener(e -> buildTaxBasisFilter());
401         theTagSelect.getEventRegistrar().addEventListener(e -> buildTagFilter());
402     }
403 
404     @Override
405     public TethysUIComponent getUnderlying() {
406         return thePanel;
407     }
408 
409     @Override
410     public OceanusEventRegistrar<PrometheusDataEvent> getEventRegistrar() {
411         return theEventManager.getEventRegistrar();
412     }
413 
414     /**
415      * Obtain the DateDayRange.
416      *
417      * @return the range.
418      */
419     public OceanusDateRange getRange() {
420         return theState.getRange();
421     }
422 
423     /**
424      * Obtain the analysis.
425      *
426      * @return the range.
427      */
428     public MoneyWiseAnalysis getAnalysis() {
429         return theAnalysis;
430     }
431 
432     /**
433      * Obtain the Filter.
434      *
435      * @return the filter.
436      */
437     public MoneyWiseAnalysisFilter<?, ?> getFilter() {
438         return theState.getFilter();
439     }
440 
441     /**
442      * Obtain the ColumnSet.
443      *
444      * @return the columnSet.
445      */
446     public MoneyWiseAnalysisColumnSet getColumns() {
447         return theState.getColumns();
448     }
449 
450     /**
451      * Are we showing columns?
452      *
453      * @return true/false.
454      */
455     public boolean showColumns() {
456         return theState.showColumns();
457     }
458 
459     /**
460      * Create control panel.
461      *
462      * @param pFactory   the GUI factory
463      * @param pNewButton the new button
464      * @return the panel
465      */
466     private TethysUIBoxPaneManager buildControlPanel(final TethysUIFactory<?> pFactory,
467                                                      final TethysUIButton pNewButton) {
468         /* Create the control panel */
469         final TethysUIBoxPaneManager myPanel = pFactory.paneFactory().newHBoxPane();
470 
471         /* Create the labels */
472         final TethysUILabel myRangeLabel = pFactory.controlFactory().newLabel(NLS_RANGE);
473 
474         /* Create save button */
475         final TethysUIButton mySave = pFactory.buttonFactory().newButton();
476         MetisIcon.configureSaveIconButton(mySave);
477 
478         /* Create the panel */
479         myPanel.setBorderTitle(NLS_TITLE);
480         myPanel.addNode(myRangeLabel);
481         myPanel.addNode(theRangeButton);
482         myPanel.addSpacer();
483         myPanel.addNode(theFilterDetail);
484         myPanel.addSpacer();
485         myPanel.addNode(mySave);
486         myPanel.addNode(pNewButton);
487 
488         /* Pass through the save event */
489         final OceanusEventRegistrar<TethysUIEvent> myRegistrar = mySave.getEventRegistrar();
490         myRegistrar.addEventListener(e -> theEventManager.fireEvent(PrometheusDataEvent.SAVETOFILE));
491 
492         /* Return the panel */
493         return myPanel;
494     }
495 
496     /**
497      * Create filter detail panel.
498      *
499      * @param pFactory the GUI factory
500      * @return the panel
501      */
502     private TethysUIBoxPaneManager buildFilterDetail(final TethysUIFactory<?> pFactory) {
503         /* Create the control panel */
504         final TethysUIBoxPaneManager myPanel = pFactory.paneFactory().newHBoxPane();
505 
506         /* Create the labels */
507         final TethysUILabel myFilterLabel = pFactory.controlFactory().newLabel(NLS_FILTER);
508 
509         /* Create the panel */
510         myPanel.addNode(myFilterLabel);
511         myPanel.addNode(theFilterButton);
512         myPanel.addSpacer();
513         myPanel.addNode(theBucketLabel);
514         myPanel.addNode(theColumnLabel);
515         myPanel.addNode(theBucketButton);
516         myPanel.addNode(theColumnButton);
517 
518         /* Return the panel */
519         return myPanel;
520     }
521 
522     /**
523      * Create filter select panel.
524      *
525      * @param pFactory the GUI factory
526      * @return the panel
527      */
528     private TethysUIBoxPaneManager buildFilterSelect(final TethysUIFactory<?> pFactory) {
529         /* Create the filter panel */
530         final TethysUIBoxPaneManager myPanel = pFactory.paneFactory().newHBoxPane();
531 
532         /* Create the labels */
533         final TethysUILabel myTypeLabel = pFactory.controlFactory().newLabel(NLS_FILTERTYPE);
534 
535         /* Add to the card panels */
536         theCardPanel.addCard(MoneyWiseAnalysisType.DEPOSIT.name(), theDepositSelect);
537         theCardPanel.addCard(MoneyWiseAnalysisType.CASH.name(), theCashSelect);
538         theCardPanel.addCard(MoneyWiseAnalysisType.LOAN.name(), theLoanSelect);
539         theCardPanel.addCard(MoneyWiseAnalysisType.SECURITY.name(), theSecuritySelect);
540         theCardPanel.addCard(MoneyWiseAnalysisType.PORTFOLIO.name(), thePortfolioSelect);
541         theCardPanel.addCard(MoneyWiseAnalysisType.PAYEE.name(), thePayeeSelect);
542         theCardPanel.addCard(MoneyWiseAnalysisType.CATEGORY.name(), theCategorySelect);
543         theCardPanel.addCard(MoneyWiseAnalysisType.TAXBASIS.name(), theTaxBasisSelect);
544         theCardPanel.addCard(MoneyWiseAnalysisType.TRANSTAG.name(), theTagSelect);
545         theCardPanel.addCard(MoneyWiseAnalysisType.ALL.name(), theAllSelect);
546 
547         /* Build the map */
548         theMap.put(MoneyWiseAnalysisType.DEPOSIT, theDepositSelect);
549         theMap.put(MoneyWiseAnalysisType.CASH, theCashSelect);
550         theMap.put(MoneyWiseAnalysisType.LOAN, theLoanSelect);
551         theMap.put(MoneyWiseAnalysisType.SECURITY, theSecuritySelect);
552         theMap.put(MoneyWiseAnalysisType.PORTFOLIO, thePortfolioSelect);
553         theMap.put(MoneyWiseAnalysisType.PAYEE, thePayeeSelect);
554         theMap.put(MoneyWiseAnalysisType.CATEGORY, theCategorySelect);
555         theMap.put(MoneyWiseAnalysisType.TAXBASIS, theTaxBasisSelect);
556         theMap.put(MoneyWiseAnalysisType.TRANSTAG, theTagSelect);
557         theMap.put(MoneyWiseAnalysisType.ALL, theAllSelect);
558 
559         /* Create the panel */
560         myPanel.setBorderTitle(NLS_FILTERTITLE);
561         myPanel.addNode(myTypeLabel);
562         myPanel.addNode(theFilterTypeButton);
563         myPanel.addSpacer();
564         myPanel.addNode(theCardPanel);
565 
566         /* Return the panel */
567         return myPanel;
568     }
569 
570     /**
571      * Select Statement.
572      *
573      * @param pSelect the selection
574      */
575     public void selectStatement(final MoneyWiseStatementSelect pSelect) {
576         /* Set refreshing flag */
577         isRefreshing = true;
578 
579         /* Update the range */
580         final TethysUIDateRangeSelector mySelect = pSelect.getRangeSelect();
581         if (mySelect != null) {
582             theRangeSelect.setSelection(mySelect);
583             theRangeSelect.lockPeriod(false);
584             theState.setRange(mySelect);
585 
586             /* Update the analysis */
587             theAnalysisView.setRange(mySelect.getRange());
588             theAnalysis = theAnalysisView.getAnalysis();
589             setAnalysis();
590         }
591 
592         /* Access the filter and the selection panel */
593         final MoneyWiseAnalysisFilter<?, ?> myFilter = pSelect.getFilter();
594         final MoneyWiseAnalysisType myType = myFilter.getAnalysisType();
595         final MoneyWiseAnalysisFilterSelection myPanel = theMap.get(myType);
596 
597         /* Move correct card to front and update it */
598         theCardPanel.selectCard(myType.name());
599         myPanel.setFilter(myFilter);
600 
601         /* Determine the state */
602         theState.determineState(myPanel);
603 
604         /* Clear refreshing flag */
605         isRefreshing = false;
606     }
607 
608     /**
609      * Refresh data.
610      */
611     public void refreshData() {
612         /* Update the range selection */
613         final OceanusDateRange myRange = theView.getRange();
614         theRangeSelect.setOverallRange(myRange);
615 
616         /* Refresh the analysisView */
617         theAnalysisView.refreshData();
618 
619         /* Update the filter selection */
620         checkType();
621     }
622 
623     /**
624      * Declare analysis.
625      */
626     private void setAnalysis() {
627         /* Only update if we have an analysis */
628         if (theAnalysis != null) {
629             /* Update filters */
630             theDepositSelect.setAnalysis(theAnalysis);
631             theCashSelect.setAnalysis(theAnalysis);
632             theLoanSelect.setAnalysis(theAnalysis);
633             theSecuritySelect.setAnalysis(theAnalysis);
634             thePortfolioSelect.setAnalysis(theAnalysis);
635             theCategorySelect.setAnalysis(theAnalysis);
636             thePayeeSelect.setAnalysis(theAnalysis);
637             theTaxBasisSelect.setAnalysis(theAnalysis);
638             theTagSelect.setAnalysis(theAnalysis);
639             theAllSelect.setAnalysis(theAnalysis);
640 
641             /* Update the filter */
642             updateFilter();
643         }
644     }
645 
646     /**
647      * Update the filter.
648      */
649     private void updateFilter() {
650         /* Access the active panel */
651         final MoneyWiseAnalysisType myType = theState.getType();
652         final MoneyWiseAnalysisFilterSelection myPanel = theMap.get(myType);
653 
654         /* Update filters */
655         if (myPanel != null) {
656             final MoneyWiseAnalysisFilter<?, ?> myFilter = myPanel.getFilter();
657             myFilter.setCurrentAttribute(theState.getBucket());
658             theState.setFilter(myFilter);
659         }
660 
661         /* Notify updated filter */
662         theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
663     }
664 
665     /**
666      * Create SavePoint.
667      */
668     protected void createSavePoint() {
669         /* Create the savePoint */
670         theSavePoint = new MoneyWiseAnalysisState(theState);
671     }
672 
673     /**
674      * Restore SavePoint.
675      */
676     protected void restoreSavePoint() {
677         /* Restore the savePoint */
678         theState = new MoneyWiseAnalysisState(theSavePoint);
679 
680         /* Apply the state */
681         theState.applyState();
682     }
683 
684     @Override
685     public void setEnabled(final boolean bEnabled) {
686         /* If there are filters available */
687         if (isAvailable()) {
688             /* Enabled disable range selection */
689             theRangeButton.setEnabled(bEnabled);
690 
691             /* Enable filter detail */
692             theFilterDetail.setVisible(true);
693             theFilterButton.setEnabled(bEnabled);
694             theColumnButton.setEnabled(bEnabled);
695             theBucketButton.setEnabled(bEnabled);
696 
697             /* If we are disabling */
698             if (!bEnabled) {
699                 /* Hide panels */
700                 setRangeVisibility(false);
701                 setFilterVisibility(false);
702             }
703 
704             /* else no filters available */
705         } else {
706             /* Enabled disable range selection */
707             theRangeButton.setEnabled(false);
708 
709             /* Hide panels */
710             setRangeVisibility(false);
711             setFilterVisibility(false);
712             theFilterDetail.setVisible(false);
713             theRangeSelect.setEnabled(bEnabled);
714         }
715     }
716 
717     @Override
718     public void setVisible(final boolean pVisible) {
719         thePanel.setVisible(pVisible);
720     }
721 
722     /**
723      * Is there any filter available?
724      *
725      * @return true/false
726      */
727     private boolean isAvailable() {
728         /* Loop through the panels */
729         for (MoneyWiseAnalysisFilterSelection myEntry : theMap.values()) {
730             /* If the filter is possible */
731             if (myEntry.isAvailable()) {
732                 /* Filter available */
733                 return true;
734             }
735         }
736 
737         /* No available filters */
738         return false;
739     }
740 
741     /**
742      * Check analysis type.
743      */
744     private void checkType() {
745         /* If the type is not appropriate */
746         MoneyWiseAnalysisType myType = theState.getType();
747 
748         /* If the type is selected */
749         if (myType != null) {
750             /* Check that the filter is appropriate */
751             final MoneyWiseAnalysisFilterSelection myPanel = theMap.get(myType);
752             if (myPanel.isAvailable()) {
753                 /* We are OK */
754                 final MoneyWiseAnalysisFilter<?, ?> myFilter = myPanel.getFilter();
755                 theState.setFilter(myFilter);
756                 return;
757             }
758         }
759 
760         /* Loop through the panels */
761         for (Entry<MoneyWiseAnalysisType, MoneyWiseAnalysisFilterSelection> myEntry : theMap.entrySet()) {
762             /* If the filter is possible */
763             final MoneyWiseAnalysisFilterSelection myPanel = myEntry.getValue();
764             if (myPanel.isAvailable()) {
765                 /* Access Analysis type */
766                 myType = myEntry.getKey();
767 
768                 /* Move correct card to front */
769                 theCardPanel.selectCard(myType.name());
770 
771                 /* Obtain the relevant filter */
772                 final MoneyWiseAnalysisFilter<?, ?> myFilter = myPanel.getFilter();
773                 final MoneyWiseAnalysisAttribute myDefault = myType.getDefaultValue();
774                 if (myFilter != null) {
775                     myFilter.setCurrentAttribute(myDefault);
776                 }
777 
778                 /* Set new bucket type and apply state */
779                 theState.setAnalysisType(myType);
780                 theState.setFilter(myFilter);
781                 theState.setBucket(myDefault);
782                 theState.applyState();
783 
784                 /* Filter available */
785                 return;
786             }
787         }
788 
789         /* No available filters */
790         theState.setFilter(null);
791         theState.setAnalysisType(null);
792         theState.setBucket(null);
793     }
794 
795     /**
796      * Build AnalysisType menu.
797      */
798     private void buildAnalysisTypeMenu() {
799         /* Reset the popUp menu */
800         theTypeMenu.removeAllItems();
801 
802         /* Loop through the panels */
803         for (Entry<MoneyWiseAnalysisType, MoneyWiseAnalysisFilterSelection> myEntry : theMap.entrySet()) {
804             /* If the filter is possible */
805             if (myEntry.getValue().isAvailable()) {
806                 /* Create a new MenuItem and add it to the popUp */
807                 theTypeMenu.addItem(myEntry.getKey());
808             }
809         }
810     }
811 
812     /**
813      * Build Bucket menu.
814      */
815     private void buildBucketMenu() {
816         /* Reset the popUp menu */
817         theBucketMenu.removeAllItems();
818 
819         /* Loop through the buckets */
820         final MoneyWiseAnalysisFilter<?, ?> myFilter = theState.getFilter();
821         for (MoneyWiseAnalysisAttribute myAttr : theState.getType().getValues()) {
822             /* If the value is a counter */
823             if (myAttr.isCounter()
824                     && myFilter.isRelevantCounter(myAttr)) {
825                 /* Create a new MenuItem and add it to the popUp */
826                 theBucketMenu.addItem(myAttr);
827             }
828         }
829 
830         /* Add the entry for null bucket */
831         theBucketMenu.addNullItem(NLS_NONE);
832     }
833 
834     /**
835      * Build Columns menu.
836      */
837     private void buildColumnsMenu() {
838         /* Reset the popUp menu */
839         theColumnMenu.removeAllItems();
840 
841         /* Determine whether we have balances */
842         final boolean hasBalances = theState.getType().hasBalances();
843 
844         /* Loop through the sets */
845         for (MoneyWiseAnalysisColumnSet mySet : MoneyWiseAnalysisColumnSet.values()) {
846             /* if we have balances or this is not the balance set */
847             if (hasBalances
848                     || !mySet.isBalance()) {
849                 /* Add the item */
850                 theColumnMenu.addItem(mySet);
851             }
852         }
853     }
854 
855     /**
856      * Build Deposit Filter.
857      */
858     private void buildDepositFilter() {
859         applyFilter(theDepositSelect.getFilter());
860     }
861 
862     /**
863      * Build Cash Filter.
864      */
865     private void buildCashFilter() {
866         applyFilter(theCashSelect.getFilter());
867     }
868 
869     /**
870      * Build Loan Filter.
871      */
872     private void buildLoanFilter() {
873         applyFilter(theLoanSelect.getFilter());
874     }
875 
876     /**
877      * Build Security Filter.
878      */
879     private void buildSecurityFilter() {
880         applyFilter(theSecuritySelect.getFilter());
881     }
882 
883     /**
884      * Build Portfolio Filter.
885      */
886     private void buildPortfolioFilter() {
887         applyFilter(thePortfolioSelect.getFilter());
888     }
889 
890     /**
891      * Build Payee Filter.
892      */
893     private void buildPayeeFilter() {
894         applyFilter(thePayeeSelect.getFilter());
895     }
896 
897     /**
898      * Build Category Filter.
899      */
900     private void buildCategoryFilter() {
901         applyFilter(theCategorySelect.getFilter());
902     }
903 
904     /**
905      * Build TaxBasis Filter.
906      */
907     private void buildTaxBasisFilter() {
908         applyFilter(theTaxBasisSelect.getFilter());
909     }
910 
911     /**
912      * Build Tag Filter.
913      */
914     private void buildTagFilter() {
915         applyFilter(theTagSelect.getFilter());
916     }
917 
918     /**
919      * Apply Filter.
920      *
921      * @param pFilter the filter
922      */
923     private void applyFilter(final MoneyWiseAnalysisFilter<?, ?> pFilter) {
924         /* Ignore if we are refreshing */
925         if (!isRefreshing) {
926             final MoneyWiseAnalysisAttribute myBucket = theState.getBucket();
927             if (pFilter.isRelevantCounter(myBucket)) {
928                 pFilter.setCurrentAttribute(theState.getBucket());
929             } else {
930                 theState.setBucket(pFilter.getCurrentAttribute());
931             }
932             theState.setFilter(pFilter);
933             theState.applyState();
934             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
935         }
936     }
937 
938     /**
939      * Set AnalysisView.
940      */
941     private void setAnalysisView() {
942         /* Ignore if we are refreshing */
943         if (!isRefreshing) {
944             /* Declare the analysis */
945             theAnalysis = theAnalysisView.getAnalysis();
946             setAnalysis();
947 
948             /* Validate state and apply */
949             checkType();
950             theState.applyState();
951 
952             /* Notify listeners */
953             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
954         }
955     }
956 
957     /**
958      * Handle New Range.
959      */
960     private void handleNewRange() {
961         /* Ignore if we are refreshing */
962         if (isRefreshing) {
963             return;
964         }
965 
966         /* If we have a change to the range */
967         if (theState.setRange(theRangeSelect)) {
968             /* Note that we are refreshing */
969             isRefreshing = true;
970 
971             /* Obtain new analysis */
972             theAnalysisView.setRange(getRange());
973             theAnalysis = theAnalysisView.getAnalysis();
974             setAnalysis();
975 
976             /* Validate state and apply */
977             checkType();
978             theState.applyState();
979 
980             /* Remove refreshing flag and notify listeners */
981             isRefreshing = false;
982             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
983         }
984     }
985 
986     /**
987      * Set RangeSelect visibility.
988      *
989      * @param pVisible the visibility setting
990      */
991     private void setRangeVisibility(final boolean pVisible) {
992         theRangeButton.setIcon(pVisible
993                 ? TethysUIArrowIconId.UP
994                 : TethysUIArrowIconId.DOWN);
995         theRangeSelect.setVisible(pVisible);
996         isRangeVisible = pVisible;
997     }
998 
999     /**
1000      * Set FilterSelect visibility.
1001      *
1002      * @param pVisible the visibility setting
1003      */
1004     private void setFilterVisibility(final boolean pVisible) {
1005         theFilterButton.setIcon(pVisible
1006                 ? TethysUIArrowIconId.UP
1007                 : TethysUIArrowIconId.DOWN);
1008         theFilterSelect.setVisible(pVisible);
1009         isFilterVisible = pVisible;
1010     }
1011 
1012     /**
1013      * Handle New Bucket.
1014      */
1015     private void handleNewBucket() {
1016         /* Ignore if we are refreshing */
1017         if (isRefreshing) {
1018             return;
1019         }
1020 
1021         final MoneyWiseAnalysisAttribute myBucket = theBucketButton.getValue();
1022         if (theState.setBucket(myBucket)) {
1023             final MoneyWiseAnalysisFilter<?, ?> myFilter = theState.getFilter();
1024             if (myBucket != null) {
1025                 myFilter.setCurrentAttribute(myBucket);
1026             }
1027             theState.applyState();
1028             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
1029         }
1030     }
1031 
1032     /**
1033      * Handle New Columns.
1034      */
1035     private void handleNewColumns() {
1036         /* Ignore if we are refreshing */
1037         if (isRefreshing) {
1038             return;
1039         }
1040 
1041         /* Record the columns */
1042         final MoneyWiseAnalysisColumnSet mySet = theColumnButton.getValue();
1043         if (theState.setColumns(mySet)) {
1044             theState.applyState();
1045             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
1046         }
1047     }
1048 
1049     /**
1050      * Handle FilterType.
1051      */
1052     private void handleFilterType() {
1053         /* Ignore if we are refreshing */
1054         if (isRefreshing) {
1055             return;
1056         }
1057 
1058         /* If the type has changed */
1059         final MoneyWiseAnalysisType myType = theFilterTypeButton.getValue();
1060         if (theState.setAnalysisType(myType)) {
1061             /* Determine whether we are showing balances */
1062             final boolean showingBalances = !theState.showColumns();
1063             if (showingBalances && !myType.hasBalances()) {
1064                 /* Move to columns if we have no balances */
1065                 theState.showColumns(true);
1066             }
1067 
1068             /* Move correct card to front */
1069             theCardPanel.selectCard(myType.name());
1070 
1071             /* Obtain the relevant filter */
1072             final MoneyWiseAnalysisFilterSelection myPanel = theMap.get(myType);
1073             final MoneyWiseAnalysisFilter<?, ?> myFilter = myPanel.getFilter();
1074             myFilter.setCurrentAttribute(myType.getDefaultValue());
1075 
1076             /* Set new bucket type and apply state */
1077             theState.setFilter(myFilter);
1078             theState.setBucket(myFilter.getCurrentAttribute());
1079             theState.applyState();
1080             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
1081         }
1082     }
1083 
1084     /**
1085      * SavePoint values.
1086      */
1087     private final class MoneyWiseAnalysisState {
1088         /**
1089          * The Range.
1090          */
1091         private OceanusDateRange theRange;
1092 
1093         /**
1094          * The AnalysisType.
1095          */
1096         private MoneyWiseAnalysisType theType;
1097 
1098         /**
1099          * The BucketAttribute.
1100          */
1101         private MoneyWiseAnalysisAttribute theBucket;
1102 
1103         /**
1104          * The ColumnSet.
1105          */
1106         private MoneyWiseAnalysisColumnSet theColumns;
1107 
1108         /**
1109          * The filter.
1110          */
1111         private MoneyWiseAnalysisFilter<?, ?> theFilter;
1112 
1113         /**
1114          * Are we showing Columns?
1115          */
1116         private boolean showColumns;
1117 
1118         /**
1119          * Constructor.
1120          */
1121         private MoneyWiseAnalysisState() {
1122             theRange = null;
1123             theFilter = null;
1124             theType = null;
1125             theBucket = null;
1126             theColumns = MoneyWiseAnalysisColumnSet.STANDARD;
1127             showColumns = true;
1128         }
1129 
1130         /**
1131          * Constructor.
1132          *
1133          * @param pState state to copy from
1134          */
1135         private MoneyWiseAnalysisState(final MoneyWiseAnalysisState pState) {
1136             theRange = pState.getRange();
1137             theFilter = pState.getFilter();
1138             theType = pState.getType();
1139             theBucket = pState.getBucket();
1140             theColumns = pState.getColumns();
1141             showColumns = pState.showColumns();
1142         }
1143 
1144         /**
1145          * Obtain the DateDayRange.
1146          *
1147          * @return the range.
1148          */
1149         private OceanusDateRange getRange() {
1150             return theRange;
1151         }
1152 
1153         /**
1154          * Obtain the AnalysisType.
1155          *
1156          * @return the analysis type.
1157          */
1158         private MoneyWiseAnalysisType getType() {
1159             return theType;
1160         }
1161 
1162         /**
1163          * Obtain the BucketType.
1164          *
1165          * @return the bucket type.
1166          */
1167         private MoneyWiseAnalysisAttribute getBucket() {
1168             return theBucket;
1169         }
1170 
1171         /**
1172          * Obtain the Columns.
1173          *
1174          * @return the columns.
1175          */
1176         private MoneyWiseAnalysisColumnSet getColumns() {
1177             return theColumns;
1178         }
1179 
1180         /**
1181          * Obtain the Filter.
1182          *
1183          * @return the filter.
1184          */
1185         private MoneyWiseAnalysisFilter<?, ?> getFilter() {
1186             return theFilter;
1187         }
1188 
1189         /**
1190          * Are we showing columns?
1191          *
1192          * @return true/false.
1193          */
1194         private boolean showColumns() {
1195             return showColumns;
1196         }
1197 
1198         /**
1199          * Determine selection from panels.
1200          *
1201          * @param pFilter selection panel
1202          */
1203         private void determineState(final MoneyWiseAnalysisFilterSelection pFilter) {
1204             /* Update the selection panels */
1205             theRange = theRangeSelect.getRange();
1206             theFilter = pFilter.getFilter();
1207             theType = theFilter.getAnalysisType();
1208             theBucket = theFilter.getCurrentAttribute();
1209             showColumns = theBucket == null;
1210             applyState();
1211         }
1212 
1213         /**
1214          * Set new Range from select panel.
1215          *
1216          * @param pSelect the selection panel
1217          * @return true/false did a change occur
1218          */
1219         private boolean setRange(final TethysUIDateRangeSelector pSelect) {
1220             /* Adjust the selected account */
1221             final OceanusDateRange myRange = pSelect.getRange();
1222             if (!MetisDataDifference.isEqual(myRange, theRange)) {
1223                 theRange = myRange;
1224                 return true;
1225             }
1226             return false;
1227         }
1228 
1229         /**
1230          * Set new analysis type.
1231          *
1232          * @param pType the analysis type
1233          * @return true/false did a change occur
1234          */
1235         private boolean setAnalysisType(final MoneyWiseAnalysisType pType) {
1236             if (!MetisDataDifference.isEqual(pType, theType)) {
1237                 theType = pType;
1238                 return true;
1239             }
1240             return false;
1241         }
1242 
1243         /**
1244          * Set new bucket type.
1245          *
1246          * @param pBucket the bucket type
1247          * @return true/false did a change occur
1248          */
1249         private boolean setBucket(final MoneyWiseAnalysisAttribute pBucket) {
1250             if (!MetisDataDifference.isEqual(pBucket, theBucket)) {
1251                 /* If this is the null bucket */
1252                 if (pBucket == null) {
1253                     showColumns = true;
1254                 } else {
1255                     theBucket = pBucket;
1256                 }
1257                 return true;
1258             }
1259             return false;
1260         }
1261 
1262         /**
1263          * Set new column set.
1264          *
1265          * @param pColumnSet the column set
1266          * @return true/false did a change occur
1267          */
1268         private boolean setColumns(final MoneyWiseAnalysisColumnSet pColumnSet) {
1269             if (!MetisDataDifference.isEqual(pColumnSet, theColumns)) {
1270                 /* If this is the balance bucket */
1271                 if (pColumnSet.equals(MoneyWiseAnalysisColumnSet.BALANCE)) {
1272                     showColumns = false;
1273                 } else {
1274                     theColumns = pColumnSet;
1275                 }
1276                 return true;
1277             }
1278             return false;
1279         }
1280 
1281         /**
1282          * Set filter.
1283          *
1284          * @param pFilter the filter
1285          */
1286         private void setFilter(final MoneyWiseAnalysisFilter<?, ?> pFilter) {
1287             theFilter = pFilter;
1288         }
1289 
1290         /**
1291          * Apply the State.
1292          */
1293         private void applyState() {
1294             /* Adjust the lock-down */
1295             setEnabled(true);
1296             theRangeButton.setText(theRange.toString());
1297             theFilterButton.setText((theFilter == null)
1298                     ? null
1299                     : theFilter.getName());
1300             theFilterTypeButton.setValue(theType);
1301             theBucketButton.setValue(theBucket);
1302             theColumnButton.setValue(theColumns);
1303             showColumns(showColumns);
1304         }
1305 
1306         /**
1307          * Show Columns.
1308          *
1309          * @param pShow true/false
1310          */
1311         private void showColumns(final boolean pShow) {
1312             /* Show columns */
1313             theColumnLabel.setVisible(pShow);
1314             theColumnButton.setVisible(pShow);
1315 
1316             /* Hide buckets */
1317             theBucketLabel.setVisible(!pShow);
1318             theBucketButton.setVisible(!pShow);
1319 
1320             /* Record details */
1321             showColumns = pShow;
1322         }
1323     }
1324 
1325     /**
1326      * The Statement Select class.
1327      */
1328     public static final class MoneyWiseStatementSelect {
1329         /**
1330          * The Range Selection.
1331          */
1332         private final TethysUIDateRangeSelector theRangeSelect;
1333 
1334         /**
1335          * The AnalysisFilter.
1336          */
1337         private final MoneyWiseAnalysisFilter<?, ?> theFilter;
1338 
1339         /**
1340          * Constructor.
1341          *
1342          * @param pRangeSelect the range selection
1343          * @param pFilter      the analysis filter
1344          */
1345         public MoneyWiseStatementSelect(final TethysUIDateRangeSelector pRangeSelect,
1346                                         final MoneyWiseAnalysisFilter<?, ?> pFilter) {
1347             /* Store parameters */
1348             theRangeSelect = pRangeSelect;
1349             theFilter = pFilter;
1350         }
1351 
1352         /**
1353          * Obtain the RangeSelection.
1354          *
1355          * @return the filter
1356          */
1357         public TethysUIDateRangeSelector getRangeSelect() {
1358             return theRangeSelect;
1359         }
1360 
1361         /**
1362          * Obtain the Filter.
1363          *
1364          * @return the filter
1365          */
1366         public MoneyWiseAnalysisFilter<?, ?> getFilter() {
1367             return theFilter;
1368         }
1369     }
1370 }