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.ui.controls;
18  
19  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
20  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
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.data.basic.MoneyWiseBasicDataType;
27  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
28  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
29  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisManager;
30  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket;
31  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket.MoneyWiseAnalysisPortfolioBucketList;
32  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
33  import io.github.tonywasher.joceanus.moneywise.views.MoneyWiseView;
34  import io.github.tonywasher.joceanus.prometheus.views.PrometheusDataEvent;
35  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIArrowIconId;
36  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
37  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIConstant;
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.TethysUIDateButtonManager;
42  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIScrollButtonManager;
43  import io.github.tonywasher.joceanus.tethys.api.control.TethysUICheckBox;
44  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControlFactory;
45  import io.github.tonywasher.joceanus.tethys.api.control.TethysUILabel;
46  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
47  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollItem;
48  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
49  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBoxPaneManager;
50  
51  import java.util.Iterator;
52  
53  /**
54   * SpotPrice selection panel.
55   */
56  public class MoneyWiseSpotPricesSelect
57          implements OceanusEventProvider<PrometheusDataEvent>, TethysUIComponent {
58      /**
59       * Text for Date Label.
60       */
61      private static final String NLS_DATE = MoneyWiseUIResource.SPOTEVENT_DATE.getValue();
62  
63      /**
64       * Text for Portfolio Label.
65       */
66      private static final String NLS_PORT = MoneyWiseBasicDataType.PORTFOLIO.getItemName() + TethysUIConstant.STR_COLON;
67  
68      /**
69       * Text for Show Closed.
70       */
71      private static final String NLS_CLOSED = MoneyWiseUIResource.UI_PROMPT_SHOWCLOSED.getValue();
72  
73      /**
74       * Text for Title.
75       */
76      private static final String NLS_TITLE = MoneyWiseUIResource.SPOTPRICE_TITLE.getValue();
77  
78      /**
79       * Text for Next toolTip.
80       */
81      private static final String NLS_NEXTTIP = MoneyWiseUIResource.SPOTPRICE_NEXT.getValue();
82  
83      /**
84       * Text for Previous toolTip.
85       */
86      private static final String NLS_PREVTIP = MoneyWiseUIResource.SPOTPRICE_PREV.getValue();
87  
88      /**
89       * The Event Manager.
90       */
91      private final OceanusEventManager<PrometheusDataEvent> theEventManager;
92  
93      /**
94       * The panel.
95       */
96      private final TethysUIBoxPaneManager thePanel;
97  
98      /**
99       * The data view.
100      */
101     private final MoneyWiseView theView;
102 
103     /**
104      * The date button.
105      */
106     private final TethysUIDateButtonManager theDateButton;
107 
108     /**
109      * The showClosed checkBox.
110      */
111     private final TethysUICheckBox theShowClosed;
112 
113     /**
114      * The next button.
115      */
116     private final TethysUIButton theNext;
117 
118     /**
119      * The previous button.
120      */
121     private final TethysUIButton thePrev;
122 
123     /**
124      * The download button.
125      */
126     private final TethysUIButton theDownloadButton;
127 
128     /**
129      * The portfolio button.
130      */
131     private final TethysUIScrollButtonManager<MoneyWiseAnalysisPortfolioBucket> thePortButton;
132 
133     /**
134      * The portfolio menu.
135      */
136     private final TethysUIScrollMenu<MoneyWiseAnalysisPortfolioBucket> thePortMenu;
137 
138     /**
139      * The Portfolio list.
140      */
141     private MoneyWiseAnalysisPortfolioBucketList thePortfolios;
142 
143     /**
144      * The current state.
145      */
146     private MoneyWiseSpotPricesState theState;
147 
148     /**
149      * The saved state.
150      */
151     private MoneyWiseSpotPricesState theSavePoint;
152 
153     /**
154      * Are we refreshing data?
155      */
156     private boolean refreshingData;
157 
158     /**
159      * Constructor.
160      *
161      * @param pFactory the GUI factory
162      * @param pView    the data view
163      */
164     public MoneyWiseSpotPricesSelect(final TethysUIFactory<?> pFactory,
165                                      final MoneyWiseView pView) {
166         /* Store table and view details */
167         theView = pView;
168 
169         /* Create Event Manager */
170         theEventManager = new OceanusEventManager<>();
171 
172         /* Create Labels */
173         final TethysUIControlFactory myControls = pFactory.controlFactory();
174         final TethysUILabel myDate = myControls.newLabel(NLS_DATE);
175         final TethysUILabel myPort = myControls.newLabel(NLS_PORT);
176 
177         /* Create the check box */
178         theShowClosed = myControls.newCheckBox(NLS_CLOSED);
179 
180         /* Create the DateButton */
181         final TethysUIButtonFactory<?> myButtons = pFactory.buttonFactory();
182         theDateButton = myButtons.newDateButton();
183 
184         /* Create the Download Button */
185         theDownloadButton = myButtons.newButton();
186         MetisIcon.configureDownloadIconButton(theDownloadButton);
187 
188         /* Create the Buttons */
189         theNext = myButtons.newButton();
190         theNext.setIcon(TethysUIArrowIconId.RIGHT);
191         theNext.setToolTip(NLS_NEXTTIP);
192         thePrev = myButtons.newButton();
193         thePrev.setIcon(TethysUIArrowIconId.LEFT);
194         thePrev.setToolTip(NLS_PREVTIP);
195 
196         /* Create the portfolio button */
197         thePortButton = myButtons.newScrollButton(MoneyWiseAnalysisPortfolioBucket.class);
198 
199         /* Create initial state */
200         theState = new MoneyWiseSpotPricesState();
201 
202         /* Create the panel */
203         thePanel = pFactory.paneFactory().newHBoxPane();
204         thePanel.setBorderTitle(NLS_TITLE);
205 
206         /* Define the layout */
207         thePanel.addNode(myDate);
208         thePanel.addNode(thePrev);
209         thePanel.addNode(theDateButton);
210         thePanel.addNode(theNext);
211         thePanel.addSpacer();
212         thePanel.addNode(myPort);
213         thePanel.addNode(thePortButton);
214         thePanel.addSpacer();
215         thePanel.addNode(theShowClosed);
216         thePanel.addSpacer();
217         thePanel.addNode(theDownloadButton);
218 
219         /* Initialise the data from the view */
220         refreshData();
221 
222         /* Apply the current state */
223         theState.applyState();
224 
225         /* Access the menus */
226         thePortMenu = thePortButton.getMenu();
227 
228         /* Add the listeners */
229         final OceanusEventRegistrar<TethysUIEvent> myRegistrar = thePortButton.getEventRegistrar();
230         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewPortfolio());
231         thePortButton.setMenuConfigurator(e -> buildPortfolioMenu());
232         theDownloadButton.getEventRegistrar().addEventListener(TethysUIEvent.PRESSED, e -> theEventManager.fireEvent(PrometheusDataEvent.DOWNLOAD));
233         theDateButton.getEventRegistrar().addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewDate());
234         theShowClosed.getEventRegistrar().addEventListener(e -> handleNewClosed());
235         theNext.getEventRegistrar().addEventListener(e -> {
236             theState.setNext();
237             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
238         });
239         thePrev.getEventRegistrar().addEventListener(e -> {
240             theState.setPrev();
241             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
242         });
243     }
244 
245     @Override
246     public TethysUIComponent getUnderlying() {
247         return thePanel;
248     }
249 
250     @Override
251     public OceanusEventRegistrar<PrometheusDataEvent> getEventRegistrar() {
252         return theEventManager.getEventRegistrar();
253     }
254 
255     /**
256      * Get the selected date.
257      *
258      * @return the date
259      */
260     public OceanusDate getDate() {
261         return theState.getDate();
262     }
263 
264     /**
265      * Get the selected portfolio.
266      *
267      * @return the portfolio
268      */
269     public final MoneyWisePortfolio getPortfolio() {
270         final MoneyWiseAnalysisPortfolioBucket myBucket = theState.getPortfolio();
271         return myBucket == null
272                 ? null
273                 : myBucket.getPortfolio();
274     }
275 
276     /**
277      * Do we show closed accounts?.
278      *
279      * @return the date
280      */
281     public boolean getShowClosed() {
282         return theState.showClosed();
283     }
284 
285     /**
286      * Refresh data.
287      */
288     public final void refreshData() {
289         /* Access the data */
290         final OceanusDateRange myRange = theView.getRange();
291 
292         /* Set the range for the Date Button */
293         setRange(myRange);
294 
295         /* Access portfolio list */
296         final MoneyWiseAnalysisManager myManager = theView.getAnalysisManager();
297         final MoneyWiseAnalysis myAnalysis = myManager.getAnalysis();
298         thePortfolios = myAnalysis.getPortfolios();
299 
300         /* Note that we are refreshing data */
301         refreshingData = true;
302 
303         /* Obtain the current portfolio */
304         MoneyWiseAnalysisPortfolioBucket myPortfolio = theState.getPortfolio();
305 
306         /* Switch to portfolio in this analysis */
307         myPortfolio = myPortfolio != null
308                 ? thePortfolios.getMatchingPortfolio(myPortfolio.getPortfolio())
309                 : thePortfolios.getDefaultPortfolio();
310 
311         /* Set the portfolio */
312         theState.setPortfolio(myPortfolio);
313         theState.applyState();
314 
315         /* Note that we have finished refreshing data */
316         refreshingData = false;
317     }
318 
319     /**
320      * Set the range for the date box.
321      *
322      * @param pRange the Range to set
323      */
324     public final void setRange(final OceanusDateRange pRange) {
325         final OceanusDate myStart = (pRange == null)
326                 ? null
327                 : pRange.getStart();
328         final OceanusDate myEnd = (pRange == null)
329                 ? null
330                 : pRange.getEnd();
331 
332         /* Set up range */
333         theDateButton.setEarliestDate(myStart);
334         theDateButton.setLatestDate(myEnd);
335     }
336 
337     @Override
338     public void setEnabled(final boolean bEnabled) {
339         theNext.setEnabled(bEnabled && (theState.getNextDate() != null));
340         thePrev.setEnabled(bEnabled && (theState.getPrevDate() != null));
341         theDateButton.setEnabled(bEnabled);
342         thePortButton.setEnabled(bEnabled);
343         theDownloadButton.setEnabled(bEnabled);
344         theShowClosed.setEnabled(bEnabled);
345     }
346 
347     @Override
348     public void setVisible(final boolean pVisible) {
349         thePanel.setVisible(pVisible);
350     }
351 
352     /**
353      * Create SavePoint.
354      */
355     public void createSavePoint() {
356         /* Create the savePoint */
357         theSavePoint = new MoneyWiseSpotPricesState(theState);
358     }
359 
360     /**
361      * Restore SavePoint.
362      */
363     public void restoreSavePoint() {
364         /* Restore the savePoint */
365         theState = new MoneyWiseSpotPricesState(theSavePoint);
366 
367         /* Apply the state */
368         theState.applyState();
369     }
370 
371     /**
372      * Set Adjacent dates.
373      *
374      * @param pPrev the previous Date
375      * @param pNext the next Date
376      */
377     public void setAdjacent(final OceanusDate pPrev,
378                             final OceanusDate pNext) {
379         /* Record the dates */
380         theState.setAdjacent(pPrev, pNext);
381     }
382 
383     /**
384      * Build the portfolio menu.
385      */
386     private void buildPortfolioMenu() {
387         /* Reset the popUp menu */
388         thePortMenu.removeAllItems();
389 
390         /* Record active item */
391         TethysUIScrollItem<MoneyWiseAnalysisPortfolioBucket> myActive = null;
392         final MoneyWiseAnalysisPortfolioBucket myCurr = theState.getPortfolio();
393 
394         /* Loop through the available portfolio values */
395         final Iterator<MoneyWiseAnalysisPortfolioBucket> myIterator = thePortfolios.iterator();
396         while (myIterator.hasNext()) {
397             final MoneyWiseAnalysisPortfolioBucket myBucket = myIterator.next();
398 
399             /* Skip if the portfolio is closed and we are not showing closed accounts */
400             if (!myBucket.isActive()
401                     && !theState.showClosed()) {
402                 continue;
403             }
404 
405             /* Create a new MenuItem and add it to the popUp */
406             final TethysUIScrollItem<MoneyWiseAnalysisPortfolioBucket> myItem = thePortMenu.addItem(myBucket);
407 
408             /* If this is the active bucket */
409             if (myBucket.equals(myCurr)) {
410                 /* Record it */
411                 myActive = myItem;
412             }
413         }
414 
415         /* Ensure active item is visible */
416         if (myActive != null) {
417             myActive.scrollToItem();
418         }
419     }
420 
421     /**
422      * Handle new Date.
423      */
424     private void handleNewDate() {
425         /* Select the new date */
426         if (theState.setDate(theDateButton)) {
427             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
428         }
429     }
430 
431     /**
432      * Handle new Portfolio.
433      */
434     private void handleNewPortfolio() {
435         /* Select the new portfolio */
436         if (theState.setPortfolio(thePortButton.getValue())) {
437             theState.applyState();
438             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
439         }
440     }
441 
442     /**
443      * Handle new Closed.
444      */
445     private void handleNewClosed() {
446         if (!refreshingData) {
447             theState.setShowClosed(theShowClosed.isSelected());
448             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
449         }
450     }
451 
452     /**
453      * SavePoint values.
454      */
455     private final class MoneyWiseSpotPricesState {
456         /**
457          * Portfolio.
458          */
459         private MoneyWiseAnalysisPortfolioBucket thePortfolio;
460 
461         /**
462          * Selected date.
463          */
464         private OceanusDate theDate;
465 
466         /**
467          * Next date.
468          */
469         private OceanusDate theNextDate;
470 
471         /**
472          * Previous date.
473          */
474         private OceanusDate thePrevDate;
475 
476         /**
477          * showClosed.
478          */
479         private boolean showClosed;
480 
481         /**
482          * Constructor.
483          */
484         private MoneyWiseSpotPricesState() {
485             theDate = new OceanusDate();
486         }
487 
488         /**
489          * Constructor.
490          *
491          * @param pState state to copy from
492          */
493         private MoneyWiseSpotPricesState(final MoneyWiseSpotPricesState pState) {
494             thePortfolio = pState.getPortfolio();
495             theDate = new OceanusDate(pState.getDate());
496             if (pState.getNextDate() != null) {
497                 theNextDate = new OceanusDate(pState.getNextDate());
498             }
499             if (pState.getPrevDate() != null) {
500                 thePrevDate = new OceanusDate(pState.getPrevDate());
501             }
502             showClosed = pState.showClosed();
503         }
504 
505         /**
506          * Get the portfolio.
507          *
508          * @return the portfolio
509          */
510         private MoneyWiseAnalysisPortfolioBucket getPortfolio() {
511             return thePortfolio;
512         }
513 
514         /**
515          * Get the selected date.
516          *
517          * @return the date
518          */
519         private OceanusDate getDate() {
520             return theDate;
521         }
522 
523         /**
524          * Get the next date.
525          *
526          * @return the date
527          */
528         private OceanusDate getNextDate() {
529             return theNextDate;
530         }
531 
532         /**
533          * Get the previous date.
534          *
535          * @return the date
536          */
537         private OceanusDate getPrevDate() {
538             return thePrevDate;
539         }
540 
541         /**
542          * Get the showClosed flag.
543          *
544          * @return the showClosed
545          */
546         private boolean showClosed() {
547             return showClosed;
548         }
549 
550         /**
551          * Set new Portfolio.
552          *
553          * @param pPortfolio the Portfolio
554          * @return true/false did a change occur
555          */
556         private boolean setPortfolio(final MoneyWiseAnalysisPortfolioBucket pPortfolio) {
557             /* Adjust the selected portfolio */
558             if (!MetisDataDifference.isEqual(pPortfolio, thePortfolio)) {
559                 thePortfolio = pPortfolio;
560                 return true;
561             }
562             return false;
563         }
564 
565         /**
566          * Set new Date.
567          *
568          * @param pButton the Button with the new date
569          * @return true/false did a change occur
570          */
571         private boolean setDate(final TethysUIDateButtonManager pButton) {
572             /* Adjust the date and build the new range */
573             final OceanusDate myDate = new OceanusDate(pButton.getSelectedDate());
574             if (!MetisDataDifference.isEqual(myDate, theDate)) {
575                 theDate = myDate;
576                 return true;
577             }
578             return false;
579         }
580 
581         /**
582          * Set Next Date.
583          */
584         private void setNext() {
585             /* Copy date */
586             theDate = new OceanusDate(theNextDate);
587             applyState();
588         }
589 
590         /**
591          * Set Previous Date.
592          */
593         private void setPrev() {
594             /* Copy date */
595             theDate = new OceanusDate(thePrevDate);
596             applyState();
597         }
598 
599         /**
600          * Set showClosed.
601          *
602          * @param pShowClosed true/false
603          */
604         private void setShowClosed(final boolean pShowClosed) {
605             /* Set flag */
606             showClosed = pShowClosed;
607             applyState();
608         }
609 
610         /**
611          * Set Adjacent dates.
612          *
613          * @param pPrev the previous Date
614          * @param pNext the next Date
615          */
616         private void setAdjacent(final OceanusDate pPrev,
617                                  final OceanusDate pNext) {
618             /* Record the dates */
619             thePrevDate = pPrev;
620             theNextDate = pNext;
621 
622             /* Adjust values */
623             setEnabled(true);
624         }
625 
626         /**
627          * Apply the State.
628          */
629         private void applyState() {
630             /* Adjust the lock-down */
631             setEnabled(true);
632             theDateButton.setSelectedDate(theDate);
633             thePortButton.setValue(thePortfolio);
634             theShowClosed.setSelected(showClosed);
635 
636             /* Determine whether we are todays date */
637             final boolean isToday = MetisDataDifference.isEqual(theDate, new OceanusDate());
638             final boolean isActive = thePortfolio != null && thePortfolio.isActive();
639             theDownloadButton.setVisible(isToday && isActive);
640         }
641     }
642 }