/* Copywrite 2016 Will Winder This file is part of Universal Gcode Sender (UGS). UGS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. UGS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with UGS. If not, see <http://www.gnu.org/licenses/>. */ package com.willwinder.universalgcodesender.uielements.macros; import com.willwinder.universalgcodesender.MacroHelper; import com.willwinder.universalgcodesender.listeners.UGSEventListener; import com.willwinder.universalgcodesender.model.BackendAPI; import com.willwinder.universalgcodesender.types.Macro; import com.willwinder.universalgcodesender.utils.Settings; import java.awt.BorderLayout; import java.awt.Dimension; import net.miginfocom.swing.MigLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ScrollPaneConstants; import org.apache.commons.lang3.StringUtils; public class MacroActionPanel extends JPanel implements UGSEventListener { private static final int INSET = 10; private static final int PADDING = 10; private final BackendAPI backend; private final List<JButton> customGcodeButtons = new ArrayList<>(); private final ArrayList<Macro> macros = new ArrayList<>(); JPanel macroPanel = new JPanel(); // Indicates that the macro list needs to be refreshed. private boolean macrosDirty = true; public MacroActionPanel(BackendAPI backend) { if (backend == null) { throw new RuntimeException("BackendAPI must be provided."); } setMinimumSize(new Dimension(50,0)); this.backend = backend; backend.addUGSEventListener(this); // Insert a scrollpane in case the buttons wont fit. JScrollPane scrollPane = new JScrollPane(macroPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setBorder(BorderFactory.createEmptyBorder()); setLayout(new BorderLayout()); add(scrollPane, BorderLayout.CENTER); } @Override public void doLayout() { Settings s = backend.getSettings(); // Lookup macros. if (macrosDirty) { Integer lastMacroIndex = s.getLastMacroIndex()+1; macros.clear(); for (int i = 0 ; i < lastMacroIndex; i++) { Macro m = s.getMacro(i); if (StringUtils.isNotEmpty(m.getGcode())) { macros.add(s.getMacro(i)); } } } // Cache the largest width amongst the buttons. int maxWidth = 0; int maxHeight = 0; // Create buttons. for (int i = 0; i < macros.size() ; i++) { final int index = i; Macro macro = macros.get(i); JButton button; if (customGcodeButtons.size() <= i) { button = new JButton(i+""); button.setEnabled(false); customGcodeButtons.add(button); // Add action listener button.addActionListener((ActionEvent evt) -> { customGcodeButtonActionPerformed(index); }); } else { button = customGcodeButtons.get(i); } if (!StringUtils.isEmpty(macro.getName())) { button.setText(macro.getName()); } else if (!StringUtils.isEmpty(macro.getDescription())) { button.setText(macro.getDescription()); } if (!StringUtils.isEmpty(macro.getDescription())) { button.setToolTipText(macro.getDescription()); } if (button.getPreferredSize().width > maxWidth) maxWidth = button.getPreferredSize().width; if (button.getPreferredSize().height > maxHeight) maxHeight = button.getPreferredSize().height; } // If button count was reduced, clear out any extras. if (customGcodeButtons.size() > macros.size()) { this.macroPanel.removeAll(); this.macroPanel.repaint(); for (int i = customGcodeButtons.size(); i > macros.size(); i--) { JButton b = customGcodeButtons.remove(i-1); } } // Calculate columns/rows which can fit in the space we have. int columns = (getWidth() - (2 * INSET)) / (maxWidth + PADDING); int rows = (getHeight() - (2 * INSET)) / (maxHeight + PADDING); // At least one column. columns = Math.max(columns, 1); // Update number of rows if more are needed. if (columns * rows < customGcodeButtons.size()) { rows = customGcodeButtons.size() / columns; if (customGcodeButtons.size() % columns != 0) rows++; } // Layout for buttons. StringBuilder columnConstraint = new StringBuilder(); for (int i = 0; i < columns; i++) { if (i > 0) { columnConstraint.append("unrelated"); } columnConstraint.append("[fill, sg 1]"); } MigLayout layout = new MigLayout("fill, wrap "+columns + ", inset " + INSET, columnConstraint.toString()); macroPanel.setLayout(layout); // Put buttons in grid. int x = 0; int y = 0; for (JButton button : customGcodeButtons) { macroPanel.add(button, "cell " + x + " " + y); y++; if (y == rows) { x++; y = 0; } } super.doLayout(); } private void customGcodeButtonActionPerformed(int macroIndex) { Macro m = backend.getSettings().getMacro(macroIndex); MacroHelper.executeCustomGcode(m.getGcode(), backend); } private void updateCustomGcodeControls(boolean enabled) { for (JButton button : customGcodeButtons) { button.setEnabled(enabled); } } @Override public void UGSEvent(com.willwinder.universalgcodesender.model.UGSEvent evt) { if (evt.isSettingChangeEvent()) { macrosDirty = true; doLayout(); } else { updateCustomGcodeControls(backend.isIdle()); } } }