/* * Copyright (C) 2019 Igor Maznitsa * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.igormaznitsa.zxpspritecorrector; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static javax.swing.JOptionPane.OK_OPTION; import static javax.swing.JOptionPane.showConfirmDialog; import com.igormaznitsa.zxpspritecorrector.components.EditorComponent; import com.igormaznitsa.zxpspritecorrector.components.InsideFileView; import com.igormaznitsa.zxpspritecorrector.components.PenWidth; import com.igormaznitsa.zxpspritecorrector.components.SelectInsideDataDialog; import com.igormaznitsa.zxpspritecorrector.components.ZXColorSelector; import com.igormaznitsa.zxpspritecorrector.components.ZXPolyData; import com.igormaznitsa.zxpspritecorrector.files.SessionData; import com.igormaznitsa.zxpspritecorrector.files.Spec256ConfigEditorPanel; import com.igormaznitsa.zxpspritecorrector.files.plugins.AbstractFilePlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.HOBETAPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.LegacySZEPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.SCLPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.SCRPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.SNAPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.SZEPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.Spec256ZipPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.TAPPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.TRDPlugin; import com.igormaznitsa.zxpspritecorrector.files.plugins.Z80InZXPOutPlugin; import com.igormaznitsa.zxpspritecorrector.tools.AbstractTool; import com.igormaznitsa.zxpspritecorrector.tools.ToolButtonModel; import com.igormaznitsa.zxpspritecorrector.tools.ToolColorizer; import com.igormaznitsa.zxpspritecorrector.tools.ToolEraser; import com.igormaznitsa.zxpspritecorrector.tools.ToolPencil; import com.igormaznitsa.zxpspritecorrector.utils.GfxUtils; import com.igormaznitsa.zxpspritecorrector.utils.TransferableImage; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ItemEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Dictionary; import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; import java.util.prefs.Preferences; import javax.swing.BoundedRangeModel; import javax.swing.Box.Filler; import javax.swing.ButtonGroup; import javax.swing.DefaultBoundedRangeModel; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu.Separator; import javax.swing.JRadioButtonMenuItem; import javax.swing.JScrollBar; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.JSpinner; import javax.swing.JToggleButton; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import org.apache.commons.io.FilenameUtils; import org.picocontainer.MutablePicoContainer; import org.picocontainer.PicoBuilder; import org.picocontainer.PicoContainer; import org.picocontainer.injectors.ProviderAdapter; public final class MainFrame extends javax.swing.JFrame { private static final String EXTRA_PROPERTY_DATA_ID = "spec256.config.properties"; private static Properties lastSpec256Properties = new Properties(); private static final long serialVersionUID = -5031012548284731523L; public final MutablePicoContainer container = new PicoBuilder() .withAutomatic() .withAnnotatedMethodInjection() .withAnnotatedFieldInjection() .withConstructorInjection() .withCaching() .build(); final BoundedRangeModel SLIDER_ALL_MODEL = new DefaultBoundedRangeModel(32, 0, 1, 32); final BoundedRangeModel SLIDER_ODD_OR_EVEN_MODEL = new DefaultBoundedRangeModel(16, 0, 1, 16); final Dictionary<Integer, JLabel> SLIDER_ALL_LABELS; final Dictionary<Integer, JLabel> SLIDER_ODD_LABELS; final Dictionary<Integer, JLabel> SLIDER_EVEN_LABELS; private final Cursor CURSOR_BLANK = Toolkit.getDefaultToolkit() .createCustomCursor(new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB), new Point(0, 0), "blank cursor"); private final AtomicReference<AbstractTool> currentAbstractTool = new AtomicReference<>(); private File lastOpenedFile; private File lastExportedFile; private File szeFile; private boolean selectAreaMode = false; private ButtonGroup attributesButtonGroup; private JToggleButton buttonLock; private ZXColorSelector colorSelector; private ButtonGroup columnModeGroup; private Filler filler1; private JPanel jPanel2; private javax.swing.JScrollPane jScrollPane1; private JSeparator jSeparator1; private Separator jSeparator2; private Separator jSeparator3; private Separator jSeparator4; private Separator jSeparator5; private Separator jSeparator6; private Separator jSeparator7; private JLabel labelAddress; private JLabel labelZoom; private EditorComponent mainEditor; private JPanel mainEditorPanel; private JMenuBar menuBar; private JMenu menuEdit; private JMenu menuView; private JMenu menuViewZoom; private JMenuItem menuViewZoomIn; private JMenuItem menuViewZoomOut; private JMenuItem menuEditClear; private JMenuItem menuEditCopyBaseToPlans; private JMenuItem menuEditCopySelectedBaseAsImage; private JMenuItem menuEditCopySelectedZxPolyAsImage; private JMenuItem menuEditPasteImage; private JMenuItem menuEditRedo; private JMenuItem menuEditSelectArea; private JMenuItem menuEditUndo; private JMenu menuFile; private JMenuItem menuFileExit; private JMenu menuFileExportAs; private JMenu menuFileRecentFiles; private JMenuItem menuFileNew; private JMenuItem menuFileOpen; private JMenuItem menuFileSaveAs; private JMenu menuHelp; private JMenuItem menuHelpAbout; private JRadioButtonMenuItem menuOptionDontShowAttributes; private JMenu menuOptions; private JCheckBoxMenuItem menuOptionsColumns; private JRadioButtonMenuItem menuOptionsColumnsAll; private JRadioButtonMenuItem menuOptionsColumnsEven; private JRadioButtonMenuItem menuOptionsColumnsOdd; private JCheckBoxMenuItem menuOptionsGrid; private JCheckBoxMenuItem menuOptionsInvertBase; private JCheckBoxMenuItem menuOptionsMode512; private JRadioButtonMenuItem menuOptionsShow512x384Attributes; private JRadioButtonMenuItem menuOptionsShowBaseAttributes; private JCheckBoxMenuItem menuOptionsZXScreen; private JMenuItem menuSave; private JPanel panelTools; private JScrollBar scrollBarAddress; private JSlider sliderColumns; private PenWidth sliderPenWidth; private JSpinner spinnerCurrentAddress; private ButtonGroup toolsButtonGroup; public MainFrame() { SLIDER_ALL_LABELS = new Hashtable<>(); SLIDER_ODD_LABELS = new Hashtable<>(); SLIDER_EVEN_LABELS = new Hashtable<>(); int even = 1; int odd = 1; for (int i = 1; i <= 32; i++) { final JLabel label = new JLabel(Integer.toString(i)); label.setFont(label.getFont().deriveFont(Font.BOLD)); SLIDER_ALL_LABELS.put(i, label); if ((i & 1) == 0) { SLIDER_EVEN_LABELS.put(even++, label); } else { SLIDER_ODD_LABELS.put(odd++, label); } } initComponents(); this.sliderColumns.setSnapToTicks(true); this.sliderColumns.setModel(SLIDER_ALL_MODEL); this.sliderColumns.setLabelTable(SLIDER_ALL_LABELS); this.sliderColumns.setValue(this.mainEditor.getColumns()); this.container.addAdapter(new ProviderAdapter(new ContextProvider(container))); this.container.addComponent(this); this.container.addComponent(this.colorSelector); this.container.addComponent(SZEPlugin.class); this.container.addComponent(HOBETAPlugin.class); this.container.addComponent(TAPPlugin.class); this.container.addComponent(TRDPlugin.class); this.container.addComponent(SCLPlugin.class); this.container.addComponent(SCRPlugin.class); this.container.addComponent(Z80InZXPOutPlugin.class); this.container.addComponent(SNAPlugin.class); this.container.addComponent(Spec256ZipPlugin.class); this.container.addComponent(LegacySZEPlugin.class); this.container.addComponent(ToolPencil.class); this.container.addComponent(ToolEraser.class); this.container.addComponent(ToolColorizer.class); this.container.start(); this.container.getComponents(AbstractTool.class).stream().forEachOrdered(tool -> { this.panelTools.add(tool); this.toolsButtonGroup.add(tool); tool.addItemListener((ItemEvent e) -> { if (e.getStateChange() == ItemEvent.SELECTED) { this.selectAreaMode = false; this.sliderPenWidth.setModel(((AbstractTool) e.getItem()).getScaleModel()); this.currentAbstractTool.set(tool); } else if (e.getStateChange() == ItemEvent.DESELECTED) { if (this.currentAbstractTool.compareAndSet(tool, null)) { this.sliderPenWidth.setModel(null); } } }); } ); this.container.getComponents(AbstractFilePlugin.class).stream() .filter(AbstractFilePlugin::isExportable) .forEachOrdered(p -> { final JMenuItem menuItem = new JMenuItem(p.getPluginDescription(true)); this.menuFileExportAs.add(menuItem); menuItem.setToolTipText(p.getToolTip(true)); menuItem.addActionListener(e -> exportDataWithPlugin(p)); }); this.setLocationRelativeTo(null); updateAddressScrollBar(); loadStateFromSession(new SessionData(this.mainEditor)); setCurrentSZEFile(null); updateBottomBar(); this.spinnerCurrentAddress.setModel(this.mainEditor); this.setIconImage(GfxUtils.loadImage("ico.png")); for (final Component j : this.menuBar.getComponents()) { if (j instanceof JMenu) { ((JMenu) j).addMenuListener(new MenuListener() { @Override public void menuSelected(MenuEvent e) { selectAreaMode = false; toolsButtonGroup.clearSelection(); } @Override public void menuDeselected(MenuEvent e) { } @Override public void menuCanceled(MenuEvent e) { } }); } } this.menuOptionsMode512.addActionListener(x -> { final boolean mode512 = this.menuOptionsMode512.isSelected(); this.container.getComponents(AbstractTool.class) .forEach((t) -> t.setEnabled(!mode512 || (mode512 && t.doesSupport512x384()))); }); resetOptions(); } public static String toHex(final int value) { final String h = Integer.toHexString(value).toUpperCase(Locale.ENGLISH); return '#' + (h.length() < 4 ? "0000".substring(0, 4 - h.length()) + h : h); } public PicoContainer getPico() { return this.container; } private void deactivateCheckbox(final JCheckBoxMenuItem item) { if (item.isSelected()) { item.doClick(); } } private void resetOptions() { deactivateCheckbox(this.menuOptionsMode512); deactivateCheckbox(this.menuOptionsZXScreen); this.spinnerCurrentAddress.setValue(0); deactivateCheckbox(this.menuOptionsGrid); deactivateCheckbox(this.menuOptionsColumns); deactivateCheckbox(this.menuOptionsInvertBase); this.menuOptionDontShowAttributes.doClick(); this.menuOptionsColumnsAll.doClick(); this.sliderColumns.setValue(32); refreshMenuAndToolState(); } private void refreshMenuAndToolState() { this.menuEditSelectArea.setEnabled(this.mainEditor.hasData()); this.menuEditPasteImage.setEnabled(this.mainEditor.hasData()); this.menuEditCopySelectedBaseAsImage.setEnabled(this.mainEditor.hasSelectedArea()); this.menuEditCopySelectedZxPolyAsImage.setEnabled(this.mainEditor.hasSelectedArea()); final boolean m512 = this.mainEditor.isMode512(); this.container.getComponents(AbstractTool.class).forEach(t -> { if (m512 && !t.doesSupport512x384()) { t.setEnabled(false); } }); } private File ensureExtension(final File file, final AbstractFilePlugin plugin) { final String extension = plugin.getExtension(true); if (extension != null) { if (FilenameUtils.getExtension(file.getName()).isEmpty()) { return new File(file.getParent(), file.getName() + '.' + extension); } } return file; } private static Properties deserializeProperties(final String data) { if (data == null) { return null; } final byte[] array = Base64.getDecoder().decode(data); final Properties result = new Properties(); try { result.load(new StringReader(new String(array, StandardCharsets.UTF_8))); } catch (IOException ex) { throw new Error("Can't load properties", ex); } return result; } private static String serializeProperties(final Properties properties) { if (properties == null) { return null; } final StringWriter writer = new StringWriter(); try { properties.store(writer, null); } catch (IOException ex) { throw new Error("Can't write properties", ex); } return Base64.getEncoder().encodeToString(writer.toString().getBytes(StandardCharsets.UTF_8)); } private void exportDataWithPlugin(final AbstractFilePlugin plugin) { if (!this.mainEditor.hasData()) { JOptionPane.showMessageDialog(this, "There is no data to export!", "There is no data", JOptionPane.WARNING_MESSAGE); return; } final JFileChooser fileChooser = new JFileChooser( this.lastExportedFile == null ? null : this.lastExportedFile.getParentFile()); fileChooser.setAcceptAllFileFilterUsed(false); fileChooser.addChoosableFileFilter(plugin.getExportFileFilter()); if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { this.lastExportedFile = ensureExtension(fileChooser.getSelectedFile(), plugin); try { if (plugin instanceof Spec256ZipPlugin) { Properties properties = lastSpec256Properties; final Spec256ConfigEditorPanel configEditorPanel = new Spec256ConfigEditorPanel(properties); if (JOptionPane.showConfirmDialog(this, configEditorPanel, "Spec256 properties", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == OK_OPTION) { properties = configEditorPanel.make(); lastSpec256Properties = properties; final SessionData sessionDataToSave = makeCurrentSessionData(); plugin.writeTo(this.lastExportedFile, this.mainEditor.getProcessingData(), sessionDataToSave, properties); } else { return; } } else { final SessionData sessionDataToSave = makeCurrentSessionData(); plugin.writeTo(this.lastExportedFile, this.mainEditor.getProcessingData(), sessionDataToSave); } } catch (Exception ex) { ex.printStackTrace(); JOptionPane .showMessageDialog(this, "Can't export data for exception [" + ex.getMessage() + ']', "Error", JOptionPane.ERROR_MESSAGE); } } } private void updateBottomBar() { this.labelZoom.setText("x" + this.mainEditor.getZoom()); } private void loadStateFromSession(final SessionData sessionData) { final int address = sessionData.getBaseAddress(); sessionData.fill(this.mainEditor); Properties foundSpec256Properties = deserializeProperties(sessionData.getExtraProperty(EXTRA_PROPERTY_DATA_ID)); final Properties sessionSpec256Properties; if (foundSpec256Properties == null) { sessionSpec256Properties = Spec256ConfigEditorPanel.makeDefault(); } else { sessionSpec256Properties = foundSpec256Properties; } lastSpec256Properties.clear(); sessionSpec256Properties.stringPropertyNames().forEach(name -> { lastSpec256Properties.setProperty(name, sessionSpec256Properties.getProperty(name)); }); this.menuOptionsColumnsAll.setSelected(true); this.menuOptionsZXScreen.setSelected(sessionData.isZXAddressing()); this.menuOptionsColumns.setSelected(sessionData.isShowColumns()); this.menuOptionsGrid.setSelected(sessionData.isShowGrid()); this.menuOptionsInvertBase.setSelected(sessionData.isInvertBaseShow()); this.menuOptionsMode512.setSelected(sessionData.is512Mode()); switch (sessionData.getAttributeMode()) { case DONT_SHOW: this.menuOptionDontShowAttributes.setSelected(true); break; case SHOW_BASE: this.menuOptionsShowBaseAttributes.setSelected(true); break; case SHOW_512x384_ZXPOLY_PLANES: this.menuOptionsShow512x384Attributes.setSelected(true); break; } updateAddressScrollBar(); this.scrollBarAddress.setValue(address); this.menuOptionsColumnsAll.setSelected(true); this.mainEditor.setColumnMode(EditorComponent.ColumnMode.ALL); this.sliderColumns.setModel(SLIDER_ALL_MODEL); this.sliderColumns.setLabelTable(SLIDER_ALL_LABELS); this.sliderColumns.setValue(this.mainEditor.getColumns()); updateBottomBar(); refreshMenuAndToolState(); } private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; toolsButtonGroup = new ButtonGroup(); attributesButtonGroup = new ButtonGroup(); columnModeGroup = new ButtonGroup(); scrollBarAddress = new JScrollBar(); sliderColumns = new JSlider(); buttonLock = new JToggleButton(); panelTools = new JPanel(); colorSelector = new ZXColorSelector(); sliderPenWidth = new PenWidth(); jScrollPane1 = new javax.swing.JScrollPane(); mainEditorPanel = new JPanel(); mainEditor = new EditorComponent(); jPanel2 = new JPanel(); labelZoom = new JLabel(); filler1 = new Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0)); labelAddress = new JLabel(); spinnerCurrentAddress = new JSpinner(); menuBar = new JMenuBar(); menuFile = new JMenu(); menuFileNew = new JMenuItem(); menuFileOpen = new JMenuItem(); menuSave = new JMenuItem(); menuFileSaveAs = new JMenuItem(); jSeparator4 = new Separator(); menuFileExportAs = new JMenu(); menuFileRecentFiles = new JMenu(); jSeparator1 = new JSeparator(); menuFileExit = new JMenuItem(); menuEdit = new JMenu(); menuView = new JMenu(); menuViewZoom = new JMenu(); menuViewZoomIn = new JMenuItem(); menuViewZoomOut = new JMenuItem(); menuEditUndo = new JMenuItem(); menuEditRedo = new JMenuItem(); jSeparator2 = new Separator(); menuEditSelectArea = new JMenuItem(); menuEditCopySelectedZxPolyAsImage = new JMenuItem(); menuEditCopySelectedBaseAsImage = new JMenuItem(); menuEditPasteImage = new JMenuItem(); jSeparator7 = new Separator(); menuEditCopyBaseToPlans = new JMenuItem(); menuEditClear = new JMenuItem(); menuOptions = new JMenu(); menuOptionsGrid = new JCheckBoxMenuItem(); menuOptionsColumns = new JCheckBoxMenuItem(); menuOptionsInvertBase = new JCheckBoxMenuItem(); jSeparator5 = new Separator(); menuOptionsZXScreen = new JCheckBoxMenuItem(); menuOptionsMode512 = new JCheckBoxMenuItem(); jSeparator6 = new Separator(); menuOptionDontShowAttributes = new JRadioButtonMenuItem(); menuOptionsShowBaseAttributes = new JRadioButtonMenuItem(); menuOptionsShow512x384Attributes = new JRadioButtonMenuItem(); jSeparator3 = new Separator(); menuOptionsColumnsAll = new JRadioButtonMenuItem(); menuOptionsColumnsOdd = new JRadioButtonMenuItem(); menuOptionsColumnsEven = new JRadioButtonMenuItem(); menuHelp = new JMenu(); menuHelpAbout = new JMenuItem(); setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); setTitle("ZX-Poly Sprite Corrector"); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { applicationClosing(evt); } }); scrollBarAddress.setToolTipText("Memory window position"); scrollBarAddress.setFocusable(false); scrollBarAddress.addAdjustmentListener(this::scrollBarAddressAdjustmentValueChanged); sliderColumns.setMajorTickSpacing(1); sliderColumns.setMinorTickSpacing(1); sliderColumns.setPaintLabels(true); sliderColumns.setPaintTicks(true); sliderColumns.setSnapToTicks(true); sliderColumns.setToolTipText("Columns number"); sliderColumns.setExtent(1); sliderColumns.setFocusable(false); sliderColumns.setValueIsAdjusting(true); sliderColumns.addChangeListener(this::sliderColumnsStateChanged); buttonLock.setText("LOCK"); buttonLock.setToolTipText("To lock current memory position and cols number"); buttonLock.setFocusable(false); buttonLock.addActionListener(this::buttonLockActionPerformed); panelTools.setBorder(javax.swing.BorderFactory.createTitledBorder("Tools")); panelTools.setFocusable(false); colorSelector.setToolTipText("Colors for paint (Lft btn - INK, Rght btn - PAPER)"); javax.swing.GroupLayout colorSelectorLayout = new javax.swing.GroupLayout(colorSelector); colorSelector.setLayout(colorSelectorLayout); colorSelectorLayout.setHorizontalGroup( colorSelectorLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 0, Short.MAX_VALUE) ); colorSelectorLayout.setVerticalGroup( colorSelectorLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 110, Short.MAX_VALUE) ); sliderPenWidth.setToolTipText("Width of an operation tool"); sliderPenWidth.setDoubleBuffered(false); sliderPenWidth.setMaximumSize(new Dimension(96, 84)); sliderPenWidth.setMinimumSize(new Dimension(96, 84)); sliderPenWidth.setPreferredSize(new Dimension(96, 84)); mainEditorPanel.setCursor(new java.awt.Cursor(java.awt.Cursor.CROSSHAIR_CURSOR)); mainEditorPanel.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { public void mouseMoved(java.awt.event.MouseEvent evt) { mainEditorPanelMouseMoved(evt); } public void mouseDragged(java.awt.event.MouseEvent evt) { mainEditorPanelMouseDragged(evt); } }); mainEditorPanel.addMouseWheelListener(this::mainEditorPanelMouseWheelMoved); mainEditorPanel.addMouseListener(new java.awt.event.MouseAdapter() { public void mousePressed(java.awt.event.MouseEvent evt) { mainEditorPanelMousePressed(evt); } public void mouseReleased(java.awt.event.MouseEvent evt) { mainEditorPanelMouseReleased(evt); } public void mouseClicked(java.awt.event.MouseEvent evt) { mainEditorPanelMouseClicked(evt); } public void mouseExited(java.awt.event.MouseEvent evt) { mainEditorPanelMouseExited(evt); } public void mouseEntered(java.awt.event.MouseEvent evt) { mainEditorPanelMouseEntered(evt); } }); javax.swing.GroupLayout mainEditorPanelLayout = new javax.swing.GroupLayout(mainEditorPanel); mainEditorPanel.setLayout(mainEditorPanelLayout); mainEditorPanelLayout.setHorizontalGroup( mainEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 598, Short.MAX_VALUE) .addGroup( mainEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(mainEditorPanelLayout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addComponent(mainEditor, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, Short.MAX_VALUE))) ); mainEditorPanelLayout.setVerticalGroup( mainEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 394, Short.MAX_VALUE) .addGroup( mainEditorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(mainEditorPanelLayout.createSequentialGroup() .addGap(0, 0, Short.MAX_VALUE) .addComponent(mainEditor, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, Short.MAX_VALUE))) ); jScrollPane1.setViewportView(mainEditorPanel); jPanel2.setBorder(javax.swing.BorderFactory.createEtchedBorder()); jPanel2.setLayout(new java.awt.GridBagLayout()); labelZoom.setText("Zoom"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 0; jPanel2.add(labelZoom, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; gridBagConstraints.weightx = 1000.0; jPanel2.add(filler1, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; jPanel2.add(labelAddress, gridBagConstraints); spinnerCurrentAddress.setFocusable(false); menuFile.setText("File"); menuFileNew.setText("New"); menuFileNew.addActionListener(this::menuFileNewActionPerformed); menuFile.add(menuFileNew); menuFileOpen.setText("Open"); menuFileOpen.addActionListener(this::menuFileOpenActionPerformed); menuFile.add(menuFileOpen); menuSave.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK)); menuSave.setText("Save"); menuSave.addActionListener(this::menuSaveActionPerformed); menuFile.add(menuSave); menuFileSaveAs.setText("Save As"); menuFileSaveAs.addActionListener(this::menuFileSaveAsActionPerformed); menuFile.add(menuFileSaveAs); menuFile.add(jSeparator4); menuFileRecentFiles.setText("Recent projects"); menuFile.add(menuFileRecentFiles); menuFileRecentFiles.addMenuListener(new MenuListener() { @Override public void menuSelected(MenuEvent e) { final List<String> recentProjects = getRecentProjects(); menuFileRecentFiles.removeAll(); for (final String path : recentProjects) { final JMenuItem projectItem = new JMenuItem(path); projectItem.addActionListener(x -> MainFrame.this.openSzeFileForPath(path)); menuFileRecentFiles.add(projectItem); } } @Override public void menuDeselected(MenuEvent e) { } @Override public void menuCanceled(MenuEvent e) { } }); menuFile.add(new JSeparator()); menuFileExportAs.setText("Export as.."); menuFile.add(menuFileExportAs); menuFile.add(jSeparator1); menuFileExit.setText("Exit"); menuFileExit.setToolTipText("Close application"); menuFileExit.addActionListener(this::menuFileExitActionPerformed); menuFile.add(menuFileExit); menuBar.add(menuFile); menuView.setText("View"); menuViewZoom.setText("Zoom"); menuViewZoomIn.setText("Zoom In"); menuViewZoomIn.addActionListener(e -> this.mainEditor.zoomIn()); menuViewZoomIn.setAccelerator(KeyStroke .getKeyStroke(KeyEvent.VK_EQUALS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); menuViewZoomOut.setText("Zoom Out"); menuViewZoomOut.addActionListener(e -> this.mainEditor.zoomOut()); menuViewZoomOut.setAccelerator(KeyStroke .getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); menuEdit.setText("Edit"); menuEdit.addMenuListener(new javax.swing.event.MenuListener() { public void menuSelected(javax.swing.event.MenuEvent evt) { menuEditMenuSelected(evt); } public void menuDeselected(javax.swing.event.MenuEvent evt) { menuEditMenuDeselected(evt); } public void menuCanceled(javax.swing.event.MenuEvent evt) { } }); menuEditUndo.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK)); menuEditUndo.setText("Undo"); menuEditUndo.addActionListener(this::menuEditUndoActionPerformed); menuEdit.add(menuEditUndo); menuEditRedo.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK)); menuEditRedo.setText("Redo"); menuEditRedo.addActionListener(this::menuEditRedoActionPerformed); menuEdit.add(menuEditRedo); menuEdit.add(jSeparator2); menuEditSelectArea.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.event.InputEvent.CTRL_MASK)); menuEditSelectArea.setText("Select area"); menuEditSelectArea.addActionListener(this::menuEditSelectAreaActionPerformed); menuEdit.add(menuEditSelectArea); menuEditCopySelectedZxPolyAsImage.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.ALT_MASK | java.awt.event.InputEvent.CTRL_MASK)); menuEditCopySelectedZxPolyAsImage.setText("Copy selection (zxpoly)"); menuEditCopySelectedZxPolyAsImage .addActionListener(this::menuEditCopySelectedZxPolyAsImageActionPerformed); menuEdit.add(menuEditCopySelectedZxPolyAsImage); menuEditCopySelectedBaseAsImage.setText("Copy selection (base)"); menuEditCopySelectedBaseAsImage .addActionListener(this::menuEditCopySelectedBaseAsImageActionPerformed); menuEdit.add(menuEditCopySelectedBaseAsImage); menuEditPasteImage.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_V, java.awt.event.InputEvent.ALT_MASK | java.awt.event.InputEvent.CTRL_MASK)); menuEditPasteImage.setText("Paste image"); menuEditPasteImage.addActionListener(this::menuEditPasteImageActionPerformed); menuEdit.add(menuEditPasteImage); menuEdit.add(jSeparator7); menuEditCopyBaseToPlans.setText("Copy base to all plans"); menuEditCopyBaseToPlans.addActionListener(this::menuEditCopyBaseToPlansActionPerformed); menuEdit.add(menuEditCopyBaseToPlans); menuEditClear.setText("Clear"); menuEditClear.addActionListener(this::menuEditClearActionPerformed); menuEdit.add(menuEditClear); menuBar.add(menuEdit); menuViewZoom.add(menuViewZoomIn); menuViewZoom.add(menuViewZoomOut); menuView.add(menuViewZoom); menuBar.add(menuView); menuOptions.setText("Options"); menuOptionsGrid.setSelected(true); menuOptionsGrid.setText("Grid"); menuOptionsGrid.addActionListener(this::menuOptionsGridActionPerformed); menuOptions.add(menuOptionsGrid); menuOptionsColumns.setSelected(true); menuOptionsColumns.setText("Columns"); menuOptionsColumns.addActionListener(this::menuOptionsColumnsActionPerformed); menuOptions.add(menuOptionsColumns); menuOptionsInvertBase.setSelected(true); menuOptionsInvertBase.setText("Invert base"); menuOptionsInvertBase.addActionListener(this::menuOptionsInvertBaseActionPerformed); menuOptions.add(menuOptionsInvertBase); menuOptions.add(jSeparator5); menuOptionsZXScreen .setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, 0)); menuOptionsZXScreen.setSelected(true); menuOptionsZXScreen.setText("ZX-Screen addressing"); menuOptionsZXScreen.addChangeListener(this::menuOptionsZXScreenStateChanged); menuOptions.add(menuOptionsZXScreen); menuOptionsMode512.setSelected(true); menuOptionsMode512.setText("512 video mode"); menuOptionsMode512.addChangeListener(this::menuOptionsMode512StateChanged); menuOptions.add(menuOptionsMode512); menuOptions.add(jSeparator6); menuOptionDontShowAttributes.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK)); attributesButtonGroup.add(menuOptionDontShowAttributes); menuOptionDontShowAttributes.setSelected(true); menuOptionDontShowAttributes.setText("Don't show attribute colors"); menuOptionDontShowAttributes .addActionListener(this::menuOptionDontShowAttributesActionPerformed); menuOptions.add(menuOptionDontShowAttributes); menuOptionsShowBaseAttributes.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.CTRL_MASK)); attributesButtonGroup.add(menuOptionsShowBaseAttributes); menuOptionsShowBaseAttributes.setText("Show attribute colors"); menuOptionsShowBaseAttributes .addActionListener(this::menuOptionsShowBaseAttributesActionPerformed); menuOptions.add(menuOptionsShowBaseAttributes); menuOptionsShow512x384Attributes.setAccelerator(javax.swing.KeyStroke .getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.ALT_MASK | java.awt.event.InputEvent.CTRL_MASK)); attributesButtonGroup.add(menuOptionsShow512x384Attributes); menuOptionsShow512x384Attributes.setText("Show 512x384 plane attributes"); menuOptionsShow512x384Attributes .addActionListener(this::menuOptionsShow512x384AttributesActionPerformed); menuOptions.add(menuOptionsShow512x384Attributes); menuOptions.add(jSeparator3); columnModeGroup.add(menuOptionsColumnsAll); menuOptionsColumnsAll.setSelected(true); menuOptionsColumnsAll.setText("All columns"); menuOptionsColumnsAll.addActionListener(this::menuOptionsColumnsAllActionPerformed); menuOptions.add(menuOptionsColumnsAll); columnModeGroup.add(menuOptionsColumnsOdd); menuOptionsColumnsOdd.setText("Odd columns"); menuOptionsColumnsOdd.addActionListener(this::menuOptionsColumnsOddActionPerformed); menuOptions.add(menuOptionsColumnsOdd); columnModeGroup.add(menuOptionsColumnsEven); menuOptionsColumnsEven.setText("Even columns"); menuOptionsColumnsEven.addActionListener(this::menuOptionsColumnsEvenActionPerformed); menuOptions.add(menuOptionsColumnsEven); menuBar.add(menuOptions); menuHelp.setText("Help"); menuHelpAbout.setText("About"); menuHelpAbout.addActionListener(this::menuHelpAboutActionPerformed); menuHelp.add(menuHelpAbout); menuBar.add(menuHelp); setJMenuBar(menuBar); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jScrollPane1) .addPreferredGap( javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(scrollBarAddress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(colorSelector, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(panelTools, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(sliderPenWidth, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addGroup(layout.createSequentialGroup() .addComponent(sliderColumns, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(29, 29, 29) .addGroup(layout .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(buttonLock, javax.swing.GroupLayout.DEFAULT_SIZE, 107, Short.MAX_VALUE) .addComponent(spinnerCurrentAddress)))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(scrollBarAddress, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 399, Short.MAX_VALUE) .addComponent(panelTools, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(sliderColumns, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(spinnerCurrentAddress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() .addComponent(buttonLock) .addGap(27, 27, 27))) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(colorSelector, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(sliderPenWidth, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); layout.linkSize(javax.swing.SwingConstants.VERTICAL, colorSelector, sliderPenWidth); getAccessibleContext().setAccessibleName("ZX-Poly Sprite corrector"); pack(); } private void openSzeFileForPath(final String path) { final File file = new File(path); if (file.isFile()) { if (this.mainEditor.isChanged()) { if (showConfirmDialog(this, "Open file '" + file.getName() + "'?", "Confirmation", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { return; } } final SZEPlugin szePlugin = container.getComponent(SZEPlugin.class); try { loadFileWithPlugin(szePlugin, file, -1); } catch (IOException ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } else { JOptionPane.showMessageDialog(this, "Can't find file '" + path + '\'', "Error", JOptionPane.ERROR_MESSAGE); } } private List<String> getRecentProjects() { final Preferences preferences = Preferences.userNodeForPackage(MainFrame.class); final String list = preferences.get("recent-projects", ""); return Arrays.stream(list.split("\\n")) .map(String::trim) .filter(x -> !x.isEmpty()) .collect(toList()); } private synchronized void addSzeProjectToRecentProjects(final File file) { try { List<String> recentProjects = new ArrayList<>(getRecentProjects()); recentProjects.remove(file.getAbsolutePath()); recentProjects.add(0, file.getAbsolutePath()); final String newValue = recentProjects.stream().limit(10).collect(joining("\n")); final Preferences preferences = Preferences.userNodeForPackage(MainFrame.class); preferences.put("recent-projects", newValue); preferences.flush(); } catch (Exception ex) { System.err.println("Can't save recent project info: " + ex.getMessage()); } } private void menuFileExitActionPerformed(java.awt.event.ActionEvent evt) { dispose(); } private void applicationClosing(java.awt.event.WindowEvent evt) { if (this.mainEditor.hasData()) { if (showConfirmDialog(this, "Close application?", "Confirmation", JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) { return; } } dispose(); } private void buttonLockActionPerformed(java.awt.event.ActionEvent evt) { if (this.buttonLock.isSelected()) { this.scrollBarAddress.setEnabled(false); this.sliderColumns.setEnabled(false); } else { if (this.mainEditor.getProcessingData() != null) { this.scrollBarAddress.setEnabled(true); } this.sliderColumns.setEnabled(true); } } private void menuHelpAboutActionPerformed(java.awt.event.ActionEvent evt) { new AboutDialog(this).setVisible(true); } private void mainEditorPanelMouseWheelMoved(java.awt.event.MouseWheelEvent evt) { if (this.selectAreaMode) { this.selectAreaMode = false; this.mainEditor.resetSelectArea(); } else if (evt.getModifiersEx() == MouseWheelEvent.CTRL_DOWN_MASK) { if (evt.getWheelRotation() < 0) { this.mainEditor.zoomIn(); } else { this.mainEditor.zoomOut(); } } updateBottomBar(); } private void sliderColumnsStateChanged(javax.swing.event.ChangeEvent evt) { final int columnIndex = this.sliderColumns.getValue(); final int columns; switch (this.mainEditor.getColumnMode()) { case EVEN: case ODD: columns = columnIndex * 2; break; case ALL: default: columns = columnIndex; break; } this.mainEditor.setColumns(columns); updateAddressScrollBar(); } private void setCurrentSZEFile(final File file) { this.szeFile = file; this.menuSave.setEnabled(file != null); } private void loadFileWithPlugin(final AbstractFilePlugin plugin, final File selectedFile, final int selected) throws IOException { final AbstractFilePlugin.ReadResult result = plugin.readFrom(selectedFile, selected); this.setTitle(selectedFile.getAbsolutePath()); this.mainEditor.setProcessingData(result.getData()); if (result.getSessionData() != null) { loadStateFromSession(result.getSessionData()); } else { resetOptions(); } this.mainEditor.setChanged(false); setCurrentSZEFile(plugin instanceof SZEPlugin ? selectedFile : null); if ((plugin instanceof SCRPlugin) && !this.menuOptionsZXScreen.isSelected()) { this.menuOptionsZXScreen.setSelected(true); } } private void menuFileOpenActionPerformed(java.awt.event.ActionEvent evt) { try { this.toolsButtonGroup.clearSelection(); final JFileChooser chooser = new JFileChooser(this.lastOpenedFile); chooser.setAcceptAllFileFilterUsed(false); container.getComponents(AbstractFilePlugin.class) .stream() .filter(AbstractFilePlugin::isImportable) .forEach((plugin) -> chooser.addChoosableFileFilter(plugin.getImportFileFilter())); final InsideFileView insideFileView = new InsideFileView(chooser); chooser.setAccessory(insideFileView); if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { final AbstractFilePlugin plugin = (AbstractFilePlugin) chooser.getFileFilter(); final File selectedFile = chooser.getSelectedFile(); this.lastOpenedFile = selectedFile; try { int selected = -1; if (plugin.doesContainInternalFileItems()) { final SelectInsideDataDialog itemSelector = new SelectInsideDataDialog(this, selectedFile, plugin); itemSelector.setVisible(true); selected = itemSelector.getSelectedIndex(); if (selected < 0) { return; } } loadFileWithPlugin(plugin, selectedFile, selected); if (plugin instanceof SZEPlugin) { this.addSzeProjectToRecentProjects(selectedFile); } } catch (IllegalArgumentException ex) { ex.printStackTrace(); JOptionPane .showMessageDialog(this, ex.getMessage(), "Can't read", JOptionPane.WARNING_MESSAGE); } catch (IOException ex) { ex.printStackTrace(); JOptionPane .showMessageDialog(this, "Can't read file or its part [" + ex.getMessage() + ']', "Error", JOptionPane.ERROR_MESSAGE); } catch (Exception ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, "Unexpected exception! See log!", "Unexpected error", JOptionPane.ERROR_MESSAGE); } finally { updateAddressScrollBar(); updateRedoUndo(); } } } finally { menuEditMenuSelected(null); } } private void scrollBarAddressAdjustmentValueChanged(java.awt.event.AdjustmentEvent evt) { final int address = evt.getValue(); this.mainEditor.setAddress(address); } private void processCurrentToolForPoint(final int buttons) { final Rectangle toolRect = this.mainEditor.getToolArea(); if (toolRect != null) { final ToolButtonModel tool = (ToolButtonModel) this.toolsButtonGroup.getSelection(); if (tool != null) { tool.getTool().process(this.mainEditor, toolRect, buttons); } } } private Point mouseCoord2EditorCoord(final MouseEvent evt) { return this.mainEditor.mousePoint2ScreenPoint( SwingUtilities.convertPoint(this.mainEditorPanel, evt.getPoint(), this.mainEditor)); } private Rectangle updateToolRectangle(final Point editorPoint) { final int width = this.sliderPenWidth.getValue(); final Rectangle rect; if (width <= 1) { rect = new Rectangle(editorPoint.x, editorPoint.y, 1, 1); } else { rect = new Rectangle(editorPoint.x - (width >> 1), editorPoint.y - (width >> 1), width, width); } if (this.currentAbstractTool.get() == null) { this.mainEditor.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); this.mainEditor.setCursorPoint(rect.getLocation()); this.mainEditor.setToolArea(null); } else { this.mainEditor.setCursor(CURSOR_BLANK); this.mainEditor.setCursorPoint(rect.getLocation()); this.mainEditor.setToolArea(rect); } setLabelAddress(this.mainEditor.getZXGraphics().coordToAddress(rect.x, rect.y)); return rect; } private void setLabelAddress(final int address) { if (address < 0) { this.labelAddress.setText("Addr: ----"); } else { this.labelAddress.setText("Addr: " + address + " (" + toHex(address) + ')'); } } private void updateRedoUndo() { this.menuEditRedo.setEnabled(this.mainEditor.hasRedo()); this.menuEditUndo.setEnabled(this.mainEditor.hasUndo()); } private int extractButtons(final MouseEvent event) { int result = AbstractTool.BUTTON_NONE; if ((event.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) { result |= AbstractTool.BUTTON_CTRL; } if ((event.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0) { result |= AbstractTool.BUTTON_ALT; } if ((event.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) { result |= AbstractTool.BUTTON_SHIFT; } if ((event.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0) { result |= AbstractTool.BUTTON_MOUSE_LEFT; } if ((event.getModifiersEx() & MouseEvent.BUTTON2_DOWN_MASK) != 0) { result |= AbstractTool.BUTTON_MOUSE_MIDDLE; } if ((event.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0) { result |= AbstractTool.BUTTON_MOUSE_RIGHT; } return result; } private void mainEditorPanelMousePressed(java.awt.event.MouseEvent evt) { this.mainEditor.addUndo(); updateRedoUndo(); if (this.selectAreaMode) { this.mainEditor.startSelectArea(mouseCoord2EditorCoord(evt)); } else { updateToolRectangle(mouseCoord2EditorCoord(evt)); processCurrentToolForPoint(extractButtons(evt)); } } private void mainEditorPanelMouseMoved(java.awt.event.MouseEvent evt) { updateToolRectangle(mouseCoord2EditorCoord(evt)); } private void mainEditorPanelMouseExited(java.awt.event.MouseEvent evt) { this.mainEditor.setToolArea(null); } private void mainEditorPanelMouseEntered(java.awt.event.MouseEvent evt) { updateToolRectangle(mouseCoord2EditorCoord(evt)); } private void mainEditorPanelMouseDragged(java.awt.event.MouseEvent evt) { if (this.selectAreaMode) { this.mainEditor.updateSelectArea(mouseCoord2EditorCoord(evt)); } else { updateToolRectangle(mouseCoord2EditorCoord(evt)); processCurrentToolForPoint(extractButtons(evt)); } } private void menuOptionsMode512StateChanged(javax.swing.event.ChangeEvent evt) { this.mainEditor.resetSelectArea(); this.mainEditor.setMode512(this.menuOptionsMode512.isSelected()); } private void menuOptionsZXScreenStateChanged(javax.swing.event.ChangeEvent evt) { this.mainEditor.setZXScreenMode(this.menuOptionsZXScreen.isSelected()); } private void menuOptionsInvertBaseActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setInvertShowBaseData(this.menuOptionsInvertBase.isSelected()); } private void menuOptionsColumnsActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setShowColumnBorders(this.menuOptionsColumns.isSelected()); } private void menuOptionsGridActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setShowGrid(this.menuOptionsGrid.isSelected()); } private void menuEditUndoActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.undo(); updateRedoUndo(); } private void menuEditRedoActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.redo(); updateRedoUndo(); } private void menuEditClearActionPerformed(java.awt.event.ActionEvent evt) { if (showConfirmDialog(this, "Clear ZX-Poly data?", "Confirmation", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { this.mainEditor.clear(); } } private void menuOptionDontShowAttributesActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setShowAttributes(EditorComponent.AttributeMode.DONT_SHOW); } private void menuOptionsShowBaseAttributesActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setShowAttributes(EditorComponent.AttributeMode.SHOW_BASE); } private void menuFileSaveAsActionPerformed(java.awt.event.ActionEvent evt) { if (this.mainEditor.hasData()) { final ZXPolyData zxpolydata = this.mainEditor.getProcessingData(); final JFileChooser fileChoolser = new JFileChooser(this.lastOpenedFile); fileChoolser.setAcceptAllFileFilterUsed(false); fileChoolser.addChoosableFileFilter(container.getComponent(SZEPlugin.class)); if (fileChoolser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { try { final File thefile = ensureExtension(fileChoolser.getSelectedFile(), container.getComponent(SZEPlugin.class)); container.getComponent(SZEPlugin.class) .writeTo(thefile, zxpolydata, makeCurrentSessionData()); this.mainEditor.setChanged(false); this.setTitle(thefile.getAbsolutePath()); setCurrentSZEFile(thefile); } catch (Exception ex) { ex.printStackTrace(); JOptionPane .showMessageDialog(this, "Error during operation [" + ex.getMessage() + ']', "Error", JOptionPane.ERROR_MESSAGE); } } } } private void menuOptionsShow512x384AttributesActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setShowAttributes(EditorComponent.AttributeMode.SHOW_512x384_ZXPOLY_PLANES); } private void menuEditCopyBaseToPlansActionPerformed(java.awt.event.ActionEvent evt) { if (showConfirmDialog(this, "Do you really want to copy base data to all ZX-Poly planes?", "Confirmation", JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION) { this.mainEditor.copyPlansFromBase(); } } private SessionData makeCurrentSessionData() { final SessionData result = new SessionData(this.mainEditor); result.setExtraProperty(EXTRA_PROPERTY_DATA_ID, serializeProperties(lastSpec256Properties)); return result; } private void menuSaveActionPerformed(java.awt.event.ActionEvent evt) { try { container.getComponent(SZEPlugin.class) .writeTo(this.szeFile, this.mainEditor.getProcessingData(), makeCurrentSessionData()); this.mainEditor.setChanged(false); } catch (Exception ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, "Can't save file for exception [" + ex.getMessage() + ']', "Error", JOptionPane.ERROR_MESSAGE); } } private void menuFileNewActionPerformed(java.awt.event.ActionEvent evt) { try { if (this.mainEditor.hasData()) { if (showConfirmDialog(this, "Do you really want to create new data?", "Confirmation", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { return; } } final NewDataDialog dialog = new NewDataDialog(this); dialog.setVisible(true); final AbstractFilePlugin.ReadResult result = dialog.getResult(); if (result != null) { this.mainEditor.setProcessingData(result.getData()); setCurrentSZEFile(null); } repaint(); } finally { menuEditMenuSelected(null); } } private void menuOptionsColumnsAllActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setColumnMode(EditorComponent.ColumnMode.ALL); this.sliderColumns.setModel(SLIDER_ALL_MODEL); this.sliderColumns.setLabelTable(SLIDER_ALL_LABELS); this.sliderColumns.setValue(this.mainEditor.getColumns()); } private void menuOptionsColumnsOddActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setColumnMode(EditorComponent.ColumnMode.ODD); this.sliderColumns.setModel(SLIDER_ODD_OR_EVEN_MODEL); this.sliderColumns.setLabelTable(SLIDER_ODD_LABELS); this.sliderColumns.setValue(this.mainEditor.getColumns() / 2); } private void menuOptionsColumnsEvenActionPerformed(java.awt.event.ActionEvent evt) { this.mainEditor.setColumnMode(EditorComponent.ColumnMode.EVEN); this.sliderColumns.setModel(SLIDER_ODD_OR_EVEN_MODEL); this.sliderColumns.setLabelTable(SLIDER_EVEN_LABELS); this.sliderColumns.setValue(this.mainEditor.getColumns() / 2); } private void mainEditorPanelMouseReleased(java.awt.event.MouseEvent evt) { if (this.selectAreaMode) { this.selectAreaMode = false; this.mainEditor.endSelectArea(mouseCoord2EditorCoord(evt)); this.menuEditMenuSelected(null); } } private void deactivateCurrentTool() { this.toolsButtonGroup.clearSelection(); } private void menuEditSelectAreaActionPerformed(java.awt.event.ActionEvent evt) { deactivateCurrentTool(); this.mainEditor.setDraggedImage(null); this.selectAreaMode = true; this.mainEditor.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); this.mainEditor.addUndo(); this.mainEditor.resetSelectArea(); } private void menuEditCopySelectedBaseAsImageActionPerformed(java.awt.event.ActionEvent evt) { final Image selectedAreaImage = this.mainEditor.getSelectedAreaAsImage(true); if (selectedAreaImage != null) { new TransferableImage(selectedAreaImage).toClipboard(); } } private void menuEditCopySelectedZxPolyAsImageActionPerformed(java.awt.event.ActionEvent evt) { final Image selectedAreaImage = this.mainEditor.getSelectedAreaAsImage(false); if (selectedAreaImage != null) { new TransferableImage(selectedAreaImage).toClipboard(); } } private void menuEditMenuSelected(javax.swing.event.MenuEvent evt) { this.toolsButtonGroup.clearSelection(); this.menuEditSelectArea.setEnabled(this.mainEditor.hasData()); this.menuEditPasteImage.setEnabled(GfxUtils.doesClipboardHasImage()); this.menuEditCopySelectedBaseAsImage.setEnabled(this.mainEditor.hasSelectedArea()); this.menuEditCopySelectedZxPolyAsImage.setEnabled(this.mainEditor.hasSelectedArea()); } private void mainEditorPanelMouseClicked(java.awt.event.MouseEvent evt) { if (this.mainEditor.hasDraggedImage()) { if (evt.getClickCount() > 1 && evt.getButton() == MouseEvent.BUTTON1) { this.mainEditor.doStampDraggedImage(); evt.consume(); } else if (evt.getButton() == MouseEvent.BUTTON3) { this.mainEditor.setDraggedImage(null); evt.consume(); } } } private void menuEditPasteImageActionPerformed(java.awt.event.ActionEvent evt) { final Image image = GfxUtils.getImageFromClipboard(); if (image != null) { this.toolsButtonGroup.clearSelection(); this.mainEditor.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); this.mainEditor.setToolArea(null); this.mainEditor.setDraggedImage(image); } } private void menuEditMenuDeselected(javax.swing.event.MenuEvent evt) { this.menuEditSelectArea.setEnabled(this.mainEditor.hasData()); this.menuEditPasteImage.setEnabled(this.mainEditor.hasData()); this.menuEditCopySelectedBaseAsImage.setEnabled(this.mainEditor.hasData()); this.menuEditCopySelectedZxPolyAsImage.setEnabled(this.mainEditor.hasData()); } private void updateAddressScrollBar() { this.sliderColumns.setEnabled(true); this.scrollBarAddress.setMinimum(0); if (this.mainEditor.getProcessingData() == null) { this.scrollBarAddress.setEnabled(false); } else { this.scrollBarAddress .setMaximum(Math.max(0, this.mainEditor.getProcessingData().length() - 32)); this.scrollBarAddress.setEnabled(true); this.scrollBarAddress.setValue(this.mainEditor.getAddress()); this.scrollBarAddress.setUnitIncrement(this.mainEditor.getColumns()); this.scrollBarAddress.setBlockIncrement(this.mainEditor.getColumns() * 96); this.scrollBarAddress.setVisibleAmount(this.mainEditor.getColumns() * 192); } this.scrollBarAddress.repaint(); } }