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