001package jmri.jmrit.symbolicprog.tabbedframe; 002 003import java.util.ArrayList; 004import java.util.List; 005import javax.swing.JLabel; 006import jmri.InstanceManager; 007import jmri.Programmer; 008import jmri.jmrit.decoderdefn.DecoderFile; 009import jmri.jmrit.decoderdefn.DecoderIndexFile; 010import jmri.jmrit.roster.RosterEntry; 011import jmri.jmrit.symbolicprog.CvTableModel; 012import jmri.jmrit.symbolicprog.ResetTableModel; 013import jmri.jmrit.symbolicprog.SymbolicProgBundle; 014import jmri.jmrit.symbolicprog.VariableTableModel; 015import org.jdom2.*; 016import java.io.*; 017import org.slf4j.Logger; 018import org.slf4j.LoggerFactory; 019 020/** 021 * Interface for the container of a set of PaneProgPanes. The panes use services 022 * provided here to work with buttons and the busy cursor. 023 * <p> 024 * TODO: Several methods are copied from PaneProgFrame and should be refactored 025 * No programmer support yet No glass pane support Need better support for 026 * visible/non-visible panes Special panes (Roster entry, attributes, graphics) 027 * not included 028 * 029 * @see apps.gui3.dp3.DecoderPro3Window 030 * 031 * @author Bob Jacobsen Copyright (C) 2010 032 */ 033public class PaneSet { 034 035 List<PaneProgPane> paneList = new ArrayList<>(); 036 PaneContainer container; 037 Programmer mProgrammer; 038 CvTableModel cvModel = null; 039 VariableTableModel variableModel; 040 ResetTableModel resetModel = null; 041 JLabel progStatus = new JLabel(SymbolicProgBundle.getMessage("StateIdle")); 042 043 /** 044 * The 'model' element representing the decoder type 045 */ 046 Element modelElem = null; 047 048 public PaneSet(PaneContainer container, RosterEntry re, Programmer programmer) { 049 this.container = container; 050 this.mProgrammer = programmer; 051 052 cvModel = new CvTableModel(progStatus, mProgrammer); 053 054 variableModel = new VariableTableModel(progStatus, new String[]{"Name", "Value"}, 055 cvModel); 056 057 resetModel = new ResetTableModel(progStatus, mProgrammer); 058 059 if (re.getFileName() != null) { 060 // set the loco file name in the roster entry 061 re.readFile(); // read, but don't yet process 062 } 063 064 // load from decoder file 065 loadDecoderFromLoco(re); 066 067 // finally fill the Variable and CV values from the specific loco file 068 if (re.getFileName() != null) { 069 re.loadCvModel(variableModel, cvModel); 070 } 071 } 072 073 // copied from PaneProgFrame 074 protected void loadDecoderFromLoco(RosterEntry r) { 075 // get a DecoderFile from the locomotive xml 076 String decoderModel = r.getDecoderModel(); 077 String decoderFamily = r.getDecoderFamily(); 078 if (log.isDebugEnabled()) { 079 log.debug("selected loco uses decoder {} {}", decoderFamily, decoderModel); 080 } 081 // locate a decoder like that. 082 List<DecoderFile> l = InstanceManager.getDefault(DecoderIndexFile.class).matchingDecoderList(null, decoderFamily, null, null, null, decoderModel); 083 if (log.isDebugEnabled()) { 084 log.debug("found {} matches", l.size()); 085 } 086 if (l.size() == 0) { 087 log.debug("Loco uses {} {} decoder, but no such decoder defined", decoderFamily, decoderModel); 088 // fall back to use just the decoder name, not family 089 l = InstanceManager.getDefault(DecoderIndexFile.class).matchingDecoderList(null, null, null, null, null, decoderModel); 090 if (log.isDebugEnabled()) { 091 log.debug("found {} matches without family key", l.size()); 092 } 093 } 094 if (l.size() > 0) { 095 DecoderFile d = l.get(0); 096 loadDecoderFile(d, r); 097 } else { 098 if (decoderModel.equals("")) { 099 log.debug("blank decoderModel requested, so nothing loaded"); 100 } else { 101 log.warn("no matching \"{}\" decoder found for loco, no decoder info loaded", decoderModel); 102 } 103 } 104 } 105 106 protected void loadDecoderFile(DecoderFile df, RosterEntry re) { 107 if (df == null) { 108 log.warn("loadDecoder file invoked with null object"); 109 return; 110 } 111 if (log.isDebugEnabled()) { 112 log.debug("loadDecoderFile from {} {}", DecoderFile.fileLocation, df.getFileName()); 113 } 114 115 try { 116 decoderRoot = df.rootFromName(DecoderFile.fileLocation + df.getFileName()); 117 } catch (JDOMException | IOException e) { 118 log.error("Exception while loading decoder XML file: {}", df.getFileName(), e); 119 } 120 // load variables from decoder tree 121 df.getProductID(); 122 df.loadVariableModel(decoderRoot.getChild("decoder"), variableModel); 123 124 // load reset from decoder tree 125 df.loadResetModel(decoderRoot.getChild("decoder"), resetModel); 126 127 // load function names 128 re.loadFunctions(decoderRoot.getChild("decoder").getChild("family").getChild("functionlabels")); 129 130// // get the showEmptyPanes attribute, if yes/no update our state 131// if (decoderRoot.getAttribute("showEmptyPanes") != null) { 132// if (log.isDebugEnabled()) log.debug("Found in decoder "+decoderRoot.getAttribute("showEmptyPanes").getValue()); 133// if (decoderRoot.getAttribute("showEmptyPanes").getValue().equals("yes")) 134// setShowEmptyPanes(true); 135// else if (decoderRoot.getAttribute("showEmptyPanes").getValue().equals("no")) 136// setShowEmptyPanes(false); 137// // leave alone for "default" value 138// if (log.isDebugEnabled()) log.debug("result "+getShowEmptyPanes()); 139// } 140 // save the pointer to the model element 141 modelElem = df.getModelElement(); 142 } 143 Element decoderRoot = null; 144 145 /** 146 * Create a set of panes from a programmer definition and roster entry 147 * 148 * @param root Root element of programmer XML definition 149 * @param r Locomotive to load from 150 */ 151 public void makePanes(Element root, RosterEntry r) { 152 // check for "programmer" element at start 153 Element base; 154 if ((base = root.getChild("programmer")) == null) { 155 log.error("xml file top element is not programmer"); 156 return; 157 } 158 159 List<Element> paneList = base.getChildren("pane"); 160 161 if (log.isDebugEnabled()) { 162 log.debug("will process {} pane definitions", paneList.size()); 163 } 164 165 for (Element e : paneList) { 166 // load each pane 167 String name = e.getAttribute("name").getValue(); 168 newPane(name, e, modelElem, r); 169 } 170 } 171 172 /** 173 * Create a single pane from a "pane" element in programmer or decoder 174 * definition 175 * @param name pane name. 176 * @param pane pane element. 177 * @param modelElem model element. 178 * @param r roster entry. 179 */ 180 public void newPane(String name, Element pane, Element modelElem, RosterEntry r) { 181 if (log.isDebugEnabled()) { 182 log.debug("newPane {}", name); 183 } 184 // create a panel to hold columns 185 PaneProgPane p = new PaneProgPane(container, name, pane, cvModel, variableModel, modelElem, r); 186 187 // and remember it for programming 188 paneList.add(p); 189 } 190 191 public List<PaneProgPane> getList() { 192 return paneList; 193 } 194 195 /** 196 * Store current content to file. 197 * @param re roster entry to store. 198 */ 199 public void storeFile(RosterEntry re) { 200 // set up file write 201 re.ensureFilenameExists(); 202 203 // write the RosterEntry to its file 204 re.writeFile(cvModel, variableModel); 205 } 206 207 private final static Logger log = LoggerFactory.getLogger(PaneSet.class); 208}