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.viewer;
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.event.OceanusEventRegistration;
25  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
26  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIHTMLManager;
27  import io.github.tonywasher.joceanus.tethys.api.control.TethysUISplitTreeManager;
28  import io.github.tonywasher.joceanus.tethys.api.control.TethysUITreeManager;
29  import io.github.tonywasher.joceanus.tethys.api.control.TethysUITreeManager.TethysUITreeItem;
30  import io.github.tonywasher.joceanus.tethys.api.dialog.TethysUIChildDialog;
31  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
32  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBorderPaneManager;
33  
34  import java.util.ArrayList;
35  import java.util.Iterator;
36  import java.util.List;
37  
38  /**
39   * Viewer Manager class, responsible for displaying the debug view.
40   */
41  public class MetisViewerWindow
42          implements OceanusEventProvider<TethysUIEvent> {
43      /**
44       * The Name of the current page.
45       */
46      private static final String NAME_CURRENT = "Current";
47  
48      /**
49       * The Height of the window.
50       */
51      private static final int WINDOW_WIDTH = 900;
52  
53      /**
54       * The Height of the window.
55       */
56      private static final int WINDOW_HEIGHT = 600;
57  
58      /**
59       * The Factory.
60       */
61      private final TethysUIFactory<?> theFactory;
62  
63      /**
64       * The Data Manager.
65       */
66      private final MetisViewerManager theDataManager;
67  
68      /**
69       * The Event Manager.
70       */
71      private final OceanusEventManager<TethysUIEvent> theEventManager;
72  
73      /**
74       * The split tree.
75       */
76      private final TethysUISplitTreeManager<MetisViewerEntry> theSplitTree;
77  
78      /**
79       * The tree manager.
80       */
81      private final TethysUITreeManager<MetisViewerEntry> theTree;
82  
83      /**
84       * The HTML manager.
85       */
86      private final TethysUIHTMLManager theHtml;
87  
88      /**
89       * The Formatter.
90       */
91      private final MetisViewerFormatter theFormatter;
92  
93      /**
94       * The Registrations.
95       */
96      private final List<OceanusEventRegistration<MetisViewerEvent>> theRegistrations;
97  
98      /**
99       * The Control Window.
100      */
101     private final MetisViewerControl theControl;
102 
103     /**
104      * The viewer dialog.
105      */
106     private TethysUIChildDialog theDialog;
107 
108     /**
109      * The Active page.
110      */
111     private MetisViewerPage theActive;
112 
113     /**
114      * Constructor.
115      *
116      * @param pFactory     the GUI factory
117      * @param pDataManager the viewer data manager
118      * @throws OceanusException on error
119      */
120     public MetisViewerWindow(final TethysUIFactory<?> pFactory,
121                              final MetisViewerManager pDataManager) throws OceanusException {
122         /* Record the parameters */
123         theFactory = pFactory;
124         theDataManager = pDataManager;
125 
126         /* Create the event manager */
127         theEventManager = new OceanusEventManager<>();
128 
129         /* Create the splitTree and obtain details */
130         theSplitTree = pFactory.controlFactory().newSplitTreeManager();
131         theTree = theSplitTree.getTreeManager();
132         theHtml = theSplitTree.getHTMLManager();
133 
134         /* Create the Control */
135         theControl = new MetisViewerControl(pFactory, this);
136         theSplitTree.setControlPane(theControl);
137 
138         /* Create the registration lists */
139         theRegistrations = new ArrayList<>();
140 
141         /* Create the formatter */
142         theFormatter = new MetisViewerFormatter(pFactory.getDataFormatter());
143 
144         /* Load the CSS */
145         theHtml.setCSSContent(MetisViewerStyleSheet.CSS_VIEWER);
146 
147         /* Initialise the tree */
148         initialiseTree();
149 
150         /* Listen to the TreeManager */
151         theSplitTree.getEventRegistrar().addEventListener(this::handleSplitTreeAction);
152     }
153 
154     @Override
155     public OceanusEventRegistrar<TethysUIEvent> getEventRegistrar() {
156         return theEventManager.getEventRegistrar();
157     }
158 
159     /**
160      * Obtain the SplitTree Manager.
161      *
162      * @return the tree manager
163      */
164     public TethysUISplitTreeManager<MetisViewerEntry> getSplitTreeManager() {
165         return theSplitTree;
166     }
167 
168     /**
169      * Obtain the Tree Manager.
170      *
171      * @return the tree manager
172      */
173     public TethysUITreeManager<MetisViewerEntry> getTreeManager() {
174         return theTree;
175     }
176 
177     /**
178      * Obtain the HTML Manager.
179      *
180      * @return the HTML manager
181      */
182     public TethysUIHTMLManager getHTMLManager() {
183         return theHtml;
184     }
185 
186     /**
187      * Initialise tree.
188      */
189     private void initialiseTree() {
190         /* Set up root */
191         final TethysUITreeItem<MetisViewerEntry> myRoot = theTree.getRoot();
192         theTree.setRootName(MetisViewerResource.VIEWER_ROOT.getValue());
193         theTree.setRootVisible();
194 
195         /* Loop through the root children */
196         final Iterator<MetisViewerEntry> myIterator = theDataManager.rootIterator();
197         while (myIterator.hasNext()) {
198             final MetisViewerEntry myEntry = myIterator.next();
199 
200             /* Create a new root entry */
201             final TethysUITreeItem<MetisViewerEntry> myTreeItem = theTree.addChildItem(myRoot, myEntry.getUniqueName(), myEntry);
202             myTreeItem.setVisible(myEntry.isVisible());
203 
204             /* Create child entries */
205             createChildEntries(myTreeItem);
206         }
207 
208         /* Listen to the data manager */
209         final OceanusEventRegistrar<MetisViewerEvent> myRegistrar = theDataManager.getEventRegistrar();
210         theRegistrations.add(myRegistrar.addEventListener(MetisViewerEvent.FOCUS, this::handleFocusEvent));
211         theRegistrations.add(myRegistrar.addEventListener(MetisViewerEvent.VISIBILITY, this::handleVisibilityEvent));
212         theRegistrations.add(myRegistrar.addEventListener(MetisViewerEvent.VALUE, this::handleValueEvent));
213         theRegistrations.add(myRegistrar.addEventListener(MetisViewerEvent.ENTRY, this::handleEntryEvent));
214 
215         /* Select the focused item */
216         final MetisViewerEntry myEntry = theDataManager.getFocus();
217         if (myEntry != null) {
218             theTree.lookUpAndSelectItem(myEntry.getUniqueName());
219         }
220     }
221 
222     /**
223      * Terminate tree.
224      */
225     protected void terminateTree() {
226         /* Loop through registrations */
227         final OceanusEventRegistrar<MetisViewerEvent> myRegistrar = theDataManager.getEventRegistrar();
228         final Iterator<OceanusEventRegistration<MetisViewerEvent>> myIterator = theRegistrations.iterator();
229         while (myIterator.hasNext()) {
230             final OceanusEventRegistration<MetisViewerEvent> myRegistration = myIterator.next();
231 
232             /* Remove the registration */
233             myRegistrar.removeEventListener(myRegistration);
234             myIterator.remove();
235         }
236 
237         /* Remove all tree entries */
238         theTree.getRoot().removeChildren();
239     }
240 
241     /**
242      * create child items.
243      *
244      * @param pItem the parent of the child items
245      */
246     private void createChildEntries(final TethysUITreeItem<MetisViewerEntry> pItem) {
247         /* Access the item */
248         final MetisViewerEntry myItem = pItem.getItem();
249 
250         /* Loop through the children */
251         final Iterator<MetisViewerEntry> myIterator = myItem.childIterator();
252         while (myIterator.hasNext()) {
253             final MetisViewerEntry myEntry = myIterator.next();
254 
255             /* Create a new child entry */
256             final TethysUITreeItem<MetisViewerEntry> myTreeItem = theTree.addChildItem(pItem, myEntry.getUniqueName(), myEntry);
257             myTreeItem.setVisible(myEntry.isVisible());
258 
259             /* Create child entries */
260             createChildEntries(myTreeItem);
261         }
262     }
263 
264     /**
265      * Handle focus event.
266      *
267      * @param pEvent the event
268      */
269     private void handleFocusEvent(final OceanusEvent<MetisViewerEvent> pEvent) {
270         /* Access item and check whether it is the currently selected item */
271         final MetisViewerEntry myEntry = pEvent.getDetails(MetisViewerEntry.class);
272         final boolean isSelected = myEntry.equals(theTree.getSelectedItem());
273 
274         /* If it is not the selected item */
275         if (!isSelected) {
276             /* Select the item */
277             theTree.lookUpAndSelectItem(myEntry.getUniqueName());
278         }
279     }
280 
281     /**
282      * Handle visibility event.
283      *
284      * @param pEvent the event
285      */
286     private void handleVisibilityEvent(final OceanusEvent<MetisViewerEvent> pEvent) {
287         /* Look up item and set visibility */
288         final MetisViewerEntry myEntry = pEvent.getDetails(MetisViewerEntry.class);
289         final TethysUITreeItem<MetisViewerEntry> myTreeItem = theTree.lookUpItem(myEntry.getUniqueName());
290         if (myTreeItem != null) {
291             myTreeItem.setVisible(myEntry.isVisible());
292         }
293     }
294 
295     /**
296      * Handle value event.
297      *
298      * @param pEvent the event
299      */
300     private void handleValueEvent(final OceanusEvent<MetisViewerEvent> pEvent) {
301         /* Look up item and rebuild */
302         final MetisViewerEntry myEntry = pEvent.getDetails(MetisViewerEntry.class);
303         final boolean isSelected = myEntry.equals(theTree.getSelectedItem());
304         final TethysUITreeItem<MetisViewerEntry> myTreeItem = theTree.lookUpItem(myEntry.getUniqueName());
305 
306         /* Remove the children of the item and rebuild them */
307         myTreeItem.removeChildren();
308         createChildEntries(myTreeItem);
309 
310         /* If we are selected */
311         if (isSelected) {
312             /* Refresh the page */
313             handleNewTreeItem(myEntry);
314         }
315     }
316 
317     /**
318      * Handle entry event.
319      *
320      * @param pEvent the event
321      */
322     private void handleEntryEvent(final OceanusEvent<MetisViewerEvent> pEvent) {
323         /* Look up parent item */
324         final MetisViewerEntry myEntry = pEvent.getDetails(MetisViewerEntry.class);
325         final MetisViewerEntry myParent = myEntry.getParent();
326 
327         /* If there is no parent */
328         if (myParent == null) {
329             /* Add as root Item */
330             theTree.addRootItem(myEntry.getUniqueName(), myEntry);
331 
332             /* else we are a child */
333         } else {
334             /* Look up the parent and add child */
335             final TethysUITreeItem<MetisViewerEntry> myTreeItem = theTree.lookUpItem(myParent.getUniqueName());
336             theTree.addChildItem(myTreeItem, myEntry.getUniqueName(), myEntry);
337         }
338     }
339 
340     /**
341      * Fire event.
342      *
343      * @param pEventId the eventId
344      * @param pValue   the relevant value
345      */
346     protected void fireEvent(final TethysUIEvent pEventId,
347                              final Object pValue) {
348         theEventManager.fireEvent(pEventId, pValue);
349     }
350 
351     /**
352      * show the dialog.
353      */
354     public void showDialog() {
355         /* If the dialog does not exist */
356         if (theDialog == null) {
357             /* Create a new dialog */
358             theDialog = theFactory.dialogFactory().newChildDialog();
359             theDialog.setTitle(MetisViewerResource.VIEWER_TITLE.getValue());
360 
361             /* Create the help panel */
362             final TethysUIBorderPaneManager myPanel = theFactory.paneFactory().newBorderPane();
363             myPanel.setCentre(theSplitTree);
364             myPanel.setPreferredWidth(WINDOW_WIDTH);
365             myPanel.setPreferredHeight(WINDOW_HEIGHT);
366             theDialog.setContent(myPanel);
367 
368             /* Set listener */
369             theDialog.getEventRegistrar().addEventListener(TethysUIEvent.WINDOWCLOSED, e -> {
370                 theTree.setVisible(false);
371                 fireEvent(TethysUIEvent.WINDOWCLOSED, null);
372             });
373         }
374 
375         /* If the dialog is not showing */
376         if (!theDialog.isShowing()) {
377             /* Make sure that the dialog is showing */
378             theTree.setVisible(true);
379             theDialog.showDialog();
380         }
381     }
382 
383     /**
384      * Hide the dialog.
385      */
386     public void hideDialog() {
387         /* If the dialog exists */
388         if (theDialog != null
389                 && theDialog.isShowing()) {
390             /* Make sure that the dialog is hidden */
391             theDialog.hideDialog();
392 
393             /* Terminate the tree */
394             terminateTree();
395         }
396     }
397 
398     /**
399      * CloseWindow on parent termination.
400      */
401     public void closeWindow() {
402         hideDialog();
403         if (theDialog != null) {
404             theDialog.closeDialog();
405         }
406     }
407 
408     /**
409      * Handle the split tree action event.
410      *
411      * @param pEvent the event
412      */
413     protected void handleSplitTreeAction(final OceanusEvent<TethysUIEvent> pEvent) {
414         switch (pEvent.getEventId()) {
415             case NEWVALUE:
416                 handleNewTreeItem(pEvent.getDetails(MetisViewerEntry.class));
417                 break;
418             case BUILDPAGE:
419                 handleLink(pEvent.getDetails(String.class));
420                 pEvent.consume();
421                 break;
422             default:
423                 break;
424         }
425     }
426 
427     /**
428      * Handle the new tree item.
429      *
430      * @param pEntry the new entry
431      */
432     private void handleNewTreeItem(final MetisViewerEntry pEntry) {
433         if (pEntry != null) {
434             /* Create the page and ensure that we remember the focus */
435             theActive = new MetisViewerPage(pEntry);
436             updatePage();
437             pEntry.setFocus();
438         }
439     }
440 
441     /**
442      * Handle the parent page.
443      */
444     protected void handleParentPage() {
445         /* Update the page */
446         theActive = theActive.getParent();
447         updatePage();
448     }
449 
450     /**
451      * Handle the next page.
452      */
453     protected void handleNextPage() {
454         /* Update the page */
455         theActive.next();
456         updatePage();
457     }
458 
459     /**
460      * Handle the previous page.
461      */
462     protected void handlePreviousPage() {
463         /* Update the page */
464         theActive.previous();
465         updatePage();
466     }
467 
468     /**
469      * Handle the explicit page.
470      *
471      * @param pIndex the index of the page
472      */
473     protected void handleExplicitPage(final int pIndex) {
474         /* Update the page */
475         theActive.setPageNo(pIndex);
476         updatePage();
477     }
478 
479     /**
480      * Handle the mode.
481      *
482      * @param pMode the new mode
483      */
484     protected void handleMode(final MetisViewerMode pMode) {
485         /* Update the page */
486         theActive.setMode(pMode);
487         updatePage();
488     }
489 
490     /**
491      * Handle the link.
492      *
493      * @param pLink the name of the link
494      */
495     private void handleLink(final String pLink) {
496         /* Update the page */
497         theActive = theActive.newPage(pLink);
498         updatePage();
499     }
500 
501     /**
502      * Update the page.
503      */
504     private void updatePage() {
505         theFormatter.formatPage(theActive);
506         theHtml.setHTMLContent(theActive.getHtml(), NAME_CURRENT);
507         theControl.updateState(theActive);
508     }
509 }