Java tutorial
package com.rinke.solutions.pinball; import java.awt.SplashScreen; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Observable; import java.util.Observer; import java.util.Optional; import java.util.UUID; import java.util.regex.Pattern; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.beanutils.BeanUtils; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Scale; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Spinner; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Option; import com.rinke.solutions.pinball.animation.AniEvent; import com.rinke.solutions.pinball.animation.Animation; import com.rinke.solutions.pinball.animation.AnimationCompiler; import com.rinke.solutions.pinball.animation.AnimationFactory; import com.rinke.solutions.pinball.animation.AnimationModel; import com.rinke.solutions.pinball.animation.AnimationType; import com.rinke.solutions.pinball.animation.CompiledAnimation; import com.rinke.solutions.pinball.animation.EventHandler; import com.rinke.solutions.pinball.api.BinaryExporter; import com.rinke.solutions.pinball.api.BinaryExporterFactory; import com.rinke.solutions.pinball.api.LicenseManager; import com.rinke.solutions.pinball.api.LicenseManager.Capability; import com.rinke.solutions.pinball.api.LicenseManagerFactory; import com.rinke.solutions.pinball.io.ConnectorFactory; import com.rinke.solutions.pinball.io.DMCImporter; import com.rinke.solutions.pinball.io.FileHelper; import com.rinke.solutions.pinball.io.PaletteImporter; import com.rinke.solutions.pinball.io.Pin2DmdConnector; import com.rinke.solutions.pinball.io.Pin2DmdConnector.ConnectionHandle; import com.rinke.solutions.pinball.io.Pin2DmdConnector.UsbCmd; import com.rinke.solutions.pinball.io.SmartDMDImporter; import com.rinke.solutions.pinball.model.Frame; import com.rinke.solutions.pinball.model.FrameSeq; import com.rinke.solutions.pinball.model.Mask; import com.rinke.solutions.pinball.model.PalMapping; import com.rinke.solutions.pinball.model.PalMapping.SwitchMode; import com.rinke.solutions.pinball.model.Palette; import com.rinke.solutions.pinball.model.PaletteType; import com.rinke.solutions.pinball.model.Plane; import com.rinke.solutions.pinball.model.PlaneNumber; import com.rinke.solutions.pinball.model.Project; import com.rinke.solutions.pinball.model.Scene; import com.rinke.solutions.pinball.ui.About; import com.rinke.solutions.pinball.ui.DeviceConfig; import com.rinke.solutions.pinball.ui.FileChooser; import com.rinke.solutions.pinball.ui.FileDialogDelegate; import com.rinke.solutions.pinball.ui.GifExporter; import com.rinke.solutions.pinball.ui.RegisterLicense; import com.rinke.solutions.pinball.ui.UsbConfig; import com.rinke.solutions.pinball.util.ApplicationProperties; import com.rinke.solutions.pinball.util.FileChooserUtil; import com.rinke.solutions.pinball.util.ObservableList; import com.rinke.solutions.pinball.util.ObservableMap; import com.rinke.solutions.pinball.util.RecentMenuManager; import com.rinke.solutions.pinball.widget.CircleTool; import com.rinke.solutions.pinball.widget.ColorizeTool; import com.rinke.solutions.pinball.widget.DMDWidget; import com.rinke.solutions.pinball.widget.DrawTool; import com.rinke.solutions.pinball.widget.FloodFillTool; import com.rinke.solutions.pinball.widget.LineTool; import com.rinke.solutions.pinball.widget.PaletteTool; import com.rinke.solutions.pinball.widget.RectTool; import com.rinke.solutions.pinball.widget.SetPixelTool; @Slf4j public class PinDmdEditor implements EventHandler { private static final int FRAME_RATE = 40; private static final String HELP_URL = "http://pin2dmd.com/editor/"; DMD dmd = new DMD(128, 32); // for sake of window builder MaskDmdObserver maskDmdObserver; AnimationHandler animationHandler = null; CyclicRedraw cyclicRedraw = new CyclicRedraw(); ObservableMap<String, Animation> animations = new ObservableMap<String, Animation>(new LinkedHashMap<>()); Map<String, DrawTool> drawTools = new HashMap<>(); Display display; protected Shell shell; protected int lastTimeCode; @Option(name = "-ani", usage = "animation file to load", required = false) private String aniToLoad; @Option(name = "-cut", usage = "<src name>,<new name>,<start>,<end>", required = false) private String cutCmd; @Option(name = "-nodirty", usage = "dont check dirty flag on close", required = false) private boolean nodirty = false; @Option(name = "-save", usage = "if set, project is saved right away", required = false) private String saveFile; @Option(name = "-load", usage = "if set, project is loaded right away", required = false) private String loadFile; @Argument private java.util.List<String> arguments = new ArrayList<String>(); private Label lblTcval; private Label lblFrameNo; private FileChooserUtil fileChooserUtil; private String frameTextPrefix = "Pin2dmd Editor "; private Animation defaultAnimation = new Animation(null, "", 0, 0, 1, 1, 1); Optional<Animation> selectedAnimation = Optional.of(defaultAnimation); java.util.List<Animation> playingAnis = new ArrayList<Animation>(); Palette activePalette; // colaboration classes DMDClock clock = new DMDClock(false); FileHelper fileHelper = new FileHelper(); SmartDMDImporter smartDMDImporter = new SmartDMDImporter(); Project project = new Project(); byte[] emptyMask = new byte[512]; int numberOfHashes = 4; java.util.List<byte[]> hashes = new ArrayList<byte[]>(); /** instance level SWT widgets */ Button btnHash[] = new Button[numberOfHashes]; Text txtDuration; Scale scale; ComboViewer paletteComboViewer; TableViewer aniListViewer; TableViewer keyframeTableViewer; Button btnRemoveAni; Button btnDeleteKeyframe; Button btnAddKeyframe; Button btnFetchDuration; Button btnPrev; Button btnNext; ComboViewer paletteTypeComboViewer; DMDWidget dmdWidget; ResourceManager resManager; Button btnNewPalette; Button btnRenamePalette; ToolBar drawToolBar; ComboViewer frameSeqViewer; Button btnMarkStart; Button btnMarkEnd; Button btnCut; Button btnStartStop; Button btnAddFrameSeq; DMDWidget previewDmd; ObservableList<Animation> frameSeqList = new ObservableList<>(new ArrayList<>()); //ComboViewer planesComboViewer; PaletteTool paletteTool; int selectedHashIndex; PalMapping selectedPalMapping; int saveTimeCode; CutInfo cutInfo = new CutInfo(); java.util.List<Palette> previewPalettes = new ArrayList<>(); //PlaneNumber planeNumber; Label lblPlanesVal; Label lblDelayVal; private Button btnSortAni; LicenseManager licManager; private Button btnMask; boolean useMask; private Observer editAniObserver; private Button btnLivePreview; private boolean livePreviewActive; private ConnectionHandle handle; private String pin2dmdAdress = null; Pin2DmdConnector connector; private Menu menuPopRecentProjects; private Menu mntmRecentAnimations; private Menu mntmRecentPalettes; RecentMenuManager recentProjectsMenuManager; RecentMenuManager recentPalettesMenuManager; RecentMenuManager recentAnimationsMenuManager; private Spinner maskSpinner; private int actMaskNumber; private Button btnColorMask; private Button btnAddColormaskKeyFrame; private MenuItem mntmGodmd; PaletteHandler paletteHandler; AnimationActionHandler aniAction; private GoDmdGroup goDmdGroup; private MenuItem mntmUploadProject; private MenuItem mntmUploadPalettes; private Button btnCopyToNext; private Button btnUndo; private Button btnRedo; private Button btnCopyToPrev; public PinDmdEditor() { super(); dmd = new DMD(128, 32); maskDmdObserver = new MaskDmdObserver(); maskDmdObserver.setDmd(dmd); activePalette = project.palettes.get(0); previewPalettes = Palette.previewPalettes(); licManager = LicenseManagerFactory.getInstance(); Arrays.fill(emptyMask, (byte) 0xFF); pin2dmdAdress = ApplicationProperties.get(ApplicationProperties.PIN2DMD_ADRESS_PROP_KEY); connector = ConnectorFactory.create(pin2dmdAdress); } public void refreshPin2DmdHost(String address) { if (address != null && !address.equals(pin2dmdAdress)) { if (handle != null) { connector.release(handle); } this.pin2dmdAdress = address; ApplicationProperties.put(ApplicationProperties.PIN2DMD_ADRESS_PROP_KEY, pin2dmdAdress); connector = ConnectorFactory.create(address); } } /** * handles redraw of animations * * @author steve */ private class CyclicRedraw implements Runnable { @Override public void run() { // if( !previewCanvas.isDisposed()) previewCanvas.redraw(); if (dmdWidget != null && !dmdWidget.isDisposed()) dmdWidget.redraw(); if (previewDmd != null && !previewDmd.isDisposed()) previewDmd.redraw(); if (animationHandler != null && !animationHandler.isStopped()) { animationHandler.run(); display.timerExec(animationHandler.getRefreshDelay(), cyclicRedraw); } } } /** * Launch the application. * * @param args */ public static void main(String[] args) { Display display = Display.getDefault(); Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() { public void run() { try { PinDmdEditor window = new PinDmdEditor(); window.open(args); } catch (Exception e) { e.printStackTrace(); } } }); } private void saveHashes(java.util.List<byte[]> hashes) { if (hashes != null) { this.hashes.clear(); for (byte[] h : hashes) { this.hashes.add(Arrays.copyOf(h, h.length)); } } } public void createBindings() { // do some bindings editAniObserver = ObserverManager.bind(animationHandler, e -> this.enableDrawing(e), () -> animationIsEditable()); ObserverManager.bind(animationHandler, e -> dmdWidget.setDrawingEnabled(e), () -> animationHandler.isStopped()); ObserverManager.bind(animationHandler, e -> btnPrev.setEnabled(e), () -> animationHandler.isStopped()); ObserverManager.bind(animationHandler, e -> btnNext.setEnabled(e), () -> animationHandler.isStopped()); ObserverManager.bind(cutInfo, e -> btnCut.setEnabled(e), () -> (cutInfo.getStart() > 0 && cutInfo.getEnd() > 0)); ObserverManager.bind(cutInfo, e -> btnMarkEnd.setEnabled(e), () -> (cutInfo.getStart() > 0)); ObserverManager.bind(animations, e -> btnStartStop.setEnabled(e), () -> !this.animations.isEmpty() && animationHandler.isStopped()); ObserverManager.bind(animations, e -> btnPrev.setEnabled(e), () -> !this.animations.isEmpty()); ObserverManager.bind(animations, e -> btnNext.setEnabled(e), () -> !this.animations.isEmpty()); ObserverManager.bind(animations, e -> btnMarkStart.setEnabled(e), () -> !this.animations.isEmpty()); ObserverManager.bind(animations, e -> aniListViewer.refresh(), () -> true); ObserverManager.bind(animations, e -> buildFrameSeqList(), () -> true); // ObserverManager.bind(animations, e->btnAddFrameSeq.setEnabled(e), // ()->!frameSeqList.isEmpty()); } private void enableDrawing(boolean e) { drawToolBar.setEnabled(e); btnColorMask.setEnabled(e); btnCopyToNext.setEnabled(e); btnCopyToPrev.setEnabled(e); } private boolean animationIsEditable() { return (this.useMask && !project.masks.get(actMaskNumber).locked) || (animationHandler.isStopped() && isEditable(animationHandler.getAnimations())); } private boolean isEditable(java.util.List<Animation> a) { if (a != null) { return a.size() == 1 && a.get(0).isMutable(); } return false; } protected void buildFrameSeqList() { frameSeqList.clear(); frameSeqList.addAll(animations.values().stream().filter(a -> a.isMutable()).collect(Collectors.toList())); frameSeqViewer.refresh(); } /** * Open the window. * * @param args */ public void open(String[] args) { CmdLineParser parser = new CmdLineParser(this); try { parser.parseArgument(args); } catch (CmdLineException e) { System.err.println(e.getMessage()); // print the list of available options parser.printUsage(System.err); System.err.println(); System.exit(1); } display = Display.getDefault(); shell = new Shell(); fileChooserUtil = new FileChooserUtil(shell); paletteHandler = new PaletteHandler(this, shell); aniAction = new AnimationActionHandler(this, shell); createContents(shell); createNewProject(); paletteComboViewer.getCombo().select(0); paletteTool.setPalette(activePalette); animationHandler = new AnimationHandler(playingAnis, clock, dmd); animationHandler.setScale(scale); animationHandler.setEventHandler(this); animationHandler.setMask(project.mask); createBindings(); SplashScreen splashScreen = SplashScreen.getSplashScreen(); if (splashScreen != null) { splashScreen.close(); } shell.open(); shell.layout(); shell.addListener(SWT.Close, e -> { e.doit = dirtyCheck(); }); GlobalExceptionHandler.getInstance().setDisplay(display); GlobalExceptionHandler.getInstance().setShell(shell); new Label(shell, SWT.NONE); new Label(shell, SWT.NONE); new Label(shell, SWT.NONE); new Label(shell, SWT.NONE); display.timerExec(animationHandler.getRefreshDelay(), cyclicRedraw); processCmdLine(); int retry = 0; while (true) { try { log.info("entering event loop"); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } System.exit(0); } catch (Exception e) { GlobalExceptionHandler.getInstance().showError(e); log.error("unexpected error: {}", e); if (retry++ > 10) System.exit(1); } } } private void processCmdLine() { // cmd line processing if (loadFile != null) { loadProject(loadFile); } if (aniToLoad != null) { aniAction.loadAni(aniToLoad, false, true); } if (cutCmd != null && !animations.isEmpty()) { String[] cuts = cutCmd.split(","); if (cuts.length >= 3) { cutScene(animations.get(cuts[0]), Integer.parseInt(cuts[2]), Integer.parseInt(cuts[3]), cuts[1]); } } if (saveFile != null) { saveProject(saveFile); } } private Animation cutScene(Animation animation, int start, int end, String name) { Animation cutScene = animation.cutScene(start, end, 4); // TODO improve to make it selectable how many planes paletteHandler.copyPalettePlaneUpgrade(); cutScene.setDesc(name); cutScene.setPalIndex(activePalette.index); animations.put(name, cutScene); aniListViewer.setSelection(new StructuredSelection(cutScene)); return cutScene; } void createNewProject() { project.clear(); activePalette = project.palettes.get(0); paletteComboViewer.refresh(); keyframeTableViewer.refresh(); animations.clear(); playingAnis.clear(); selectedAnimation = Optional.of(defaultAnimation); } private void loadProject() { String filename = fileChooserUtil.choose(SWT.OPEN, null, new String[] { "*.xml;*.json;" }, new String[] { "Project XML", "Project JSON" }); if (filename != null) loadProject(filename); } /** * imports a secondary project to implement a merge functionality */ void importProject() { String filename = fileChooserUtil.choose(SWT.OPEN, null, new String[] { "*.xml;*.json;" }, new String[] { "Project XML", "Project JSON" }); if (filename != null) importProject(filename); } void importProject(String filename) { log.info("importing project from {}", filename); Project projectToImport = (Project) fileHelper.loadObject(filename); // merge into existing Project HashSet<String> collisions = new HashSet<>(); for (String key : projectToImport.frameSeqMap.keySet()) { if (project.frameSeqMap.containsKey(key)) { collisions.add(key); } else { project.frameSeqMap.put(key, projectToImport.frameSeqMap.get(key)); } } if (!collisions.isEmpty()) { MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK | SWT.IGNORE | SWT.ABORT); messageBox.setText("Override warning"); messageBox.setMessage("the following frame seq have NOT been \nimported due to name collisions: " + collisions + "\n"); messageBox.open(); } for (String inputFile : projectToImport.inputFiles) { aniAction.loadAni(buildRelFilename(filename, inputFile), true, true); } for (PalMapping palMapping : projectToImport.palMappings) { project.palMappings.add(palMapping); } } void loadProject(String filename) { log.info("load project from {}", filename); Project projectToLoad = (Project) fileHelper.loadObject(filename); if (projectToLoad != null) { shell.setText(frameTextPrefix + " - " + new File(filename).getName()); project = projectToLoad; animations.clear(); for (String file : project.inputFiles) aniAction.loadAni(buildRelFilename(filename, file), true, false); for (int i = 1; i < project.scenes.size(); i++) { // cutOutNewAnimation(project.scenes.get(i).start, // project.scenes.get(i).end, animations.get(0)); log.info("cutting out " + project.scenes.get(i)); } paletteComboViewer.setInput(project.palettes); paletteComboViewer.setSelection(new StructuredSelection(project.palettes.get(0))); keyframeTableViewer.setInput(project.palMappings); for (Animation ani : animations.values()) { selectedAnimation = Optional.of(animations.isEmpty() ? defaultAnimation : ani); break; } ensureDefault(); recentProjectsMenuManager.populateRecent(filename); } } private void ensureDefault() { boolean foundDefault = false; for (Palette p : project.palettes) { if (PaletteType.DEFAULT.equals(p.type)) { foundDefault = true; break; } } if (!foundDefault) { project.palettes.get(0).type = PaletteType.DEFAULT; } } private String buildRelFilename(String parent, String file) { return new File(parent).getParent() + File.separator + new File(file).getName(); } private void exportProject() { licManager.requireOneOf(Capability.VPIN, Capability.REALPIN); String filename = fileChooserUtil.choose(SWT.SAVE, project.name, new String[] { "*.dat" }, new String[] { "Export dat" }); if (filename != null) { exportProject(filename, f -> new FileOutputStream(f)); warn("Hint", "Remember to rename your export file to pin2dmd.pal if you want to use it" + " in a real pinballs sdcard of pin2dmd."); } } private void saveProject() { String filename = fileChooserUtil.choose(SWT.SAVE, project.name, new String[] { "*.xml", "*.json" }, new String[] { "Project XML", "Project JSON" }); if (filename != null) saveProject(filename); } @FunctionalInterface public interface OutputStreamProvider { OutputStream buildStream(String name) throws IOException; } void uploadProject() { Map<String, ByteArrayOutputStream> captureOutput = new HashMap<>(); exportProject("a.dat", f -> { ByteArrayOutputStream stream = new ByteArrayOutputStream(); captureOutput.put(f, stream); return stream; }); connector.transferFile("pin2dmd.pal", new ByteArrayInputStream(captureOutput.get("a.dat").toByteArray())); if (captureOutput.containsKey("a.fsq")) { connector.transferFile("pin2dmd.fsq", new ByteArrayInputStream(captureOutput.get("a.fsq").toByteArray())); } sleep(200); connector.sendCmd(UsbCmd.RESET); } private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { } } void exportProject(String filename, OutputStreamProvider streamProvider) { licManager.requireOneOf(Capability.VPIN, Capability.REALPIN); for (PalMapping p : project.palMappings) { if (p.frameSeqName != null) { FrameSeq frameSeq = new FrameSeq(p.frameSeqName); if (p.switchMode.equals(SwitchMode.ADD)) { frameSeq.mask = 0b11111100; } project.frameSeqMap.put(p.frameSeqName, frameSeq); } } // for all referenced frame mapping we must also copy the frame data as // there are two models for (FrameSeq p : project.frameSeqMap.values()) { Animation ani = animations.get(p.name); ani.actFrame = 0; DMD tmp = new DMD(128, 32); for (int i = 0; i <= ani.end; i++) { Frame frame = new Frame(ani.render(tmp, false)); // copy frames to not remove in org // remove planes not in mask int pl = 0; for (Iterator<Plane> iter = frame.planes.iterator(); iter.hasNext();) { iter.next(); if (((1 << pl) & p.mask) == 0) { iter.remove(); } pl++; } p.frames.add(frame); } } // create addtional files for frame sequences try { Map<String, Integer> map = new HashMap<String, Integer>(); BinaryExporter exporter = BinaryExporterFactory.getInstance(); if (!project.frameSeqMap.isEmpty()) { DataOutputStream dos = new DataOutputStream( streamProvider.buildStream(replaceExtensionTo("fsq", filename))); map = exporter.writeFrameSeqTo(dos, project); dos.close(); } DataOutputStream dos2 = new DataOutputStream(streamProvider.buildStream(filename)); exporter.writeTo(dos2, map, project); dos2.close(); // fileHelper.storeObject(project, filename); } catch (IOException e) { throw new RuntimeException("error writing " + filename, e); } } private void saveProject(String filename) { log.info("write project to {}", filename); String aniFilename = replaceExtensionTo("ani", filename); String baseName = new File(aniFilename).getName(); String baseNameWithoutExtension = baseName.substring(0, baseName.indexOf('.')); if (project.name == null) { project.name = baseNameWithoutExtension; } else if (!project.name.equals(baseNameWithoutExtension)) { // save as project.inputFiles.remove(project.name + ".ani"); } int numberOfStoredAnis = aniAction.storeAnimations(animations.values(), aniFilename, 1); if (numberOfStoredAnis > 0 && !project.inputFiles.contains(baseName)) { project.inputFiles.add(baseName); } fileHelper.storeObject(project, filename); project.dirty = false; } String replaceExtensionTo(String newExt, String filename) { int p = filename.lastIndexOf("."); if (p != -1) return filename.substring(0, p) + "." + newExt; return filename; } public void createHashButtons(Composite parent, int x, int y) { for (int i = 0; i < numberOfHashes; i++) { btnHash[i] = new Button(parent, SWT.CHECK); if (i == 0) btnHash[i].setSelection(true); btnHash[i].setData(Integer.valueOf(i)); btnHash[i].setText("Hash" + i); // btnHash[i].setFont(new Font(shell.getDisplay(), "sans", 10, 0)); btnHash[i].setBounds(x, y + i * 16, 331, 18); btnHash[i].addListener(SWT.Selection, e -> { selectedHashIndex = (Integer) e.widget.getData(); if (selectedPalMapping != null) { selectedPalMapping.hashIndex = selectedHashIndex; } for (int j = 0; j < numberOfHashes; j++) { if (j != selectedHashIndex) btnHash[j].setSelection(false); } // switch palettes in preview previewDmd.setPalette(previewPalettes.get(selectedHashIndex)); }); } } public void addKeyFrame(SwitchMode switchMode) { PalMapping palMapping = new PalMapping(activePalette.index, "KeyFrame " + (project.palMappings.size() + 1)); if (selectedHashIndex != -1) { palMapping.setDigest(hashes.get(selectedHashIndex)); } palMapping.animationName = selectedAnimation.get().getDesc(); palMapping.frameIndex = selectedAnimation.get().actFrame; palMapping.switchMode = switchMode; if (useMask) { palMapping.withMask = useMask; palMapping.maskNumber = actMaskNumber; project.masks.get(actMaskNumber).locked = true; switchMask(true); } if (!checkForDuplicateKeyFrames(palMapping)) { project.palMappings.add(palMapping); saveTimeCode = lastTimeCode; keyframeTableViewer.refresh(); } else { warn("Hash is already used", "The selected hash is already used by another key frame"); } } boolean checkForDuplicateKeyFrames(PalMapping palMapping) { for (PalMapping p : project.palMappings) { if (Arrays.equals(p.crc32, palMapping.crc32)) return true; } return false; } /** * encapsulates delegating logic for mask and dmd regarding undo / redo and abservable * do it delegates (can)Undo and (can)Redo calls, depending on mask set * and forwards observer notifies. */ private static class MaskDmdObserver extends Observable implements Observer { private DMD dmd; private DMD mask; public void setDmd(DMD dmd) { this.dmd = dmd; dmd.addObserver(this); setChanged(); notifyObservers(); } public void setMask(DMD mask) { this.mask = mask; if (mask != null) { mask.addObserver(this); } setChanged(); notifyObservers(); } public boolean canUndo() { return mask != null ? mask.canUndo() : dmd.canUndo(); } public boolean canRedo() { return mask != null ? mask.canRedo() : dmd.canRedo(); } public void redo() { if (mask != null) mask.redo(); else dmd.redo(); } public void undo() { if (mask != null) mask.undo(); else dmd.undo(); } @Override public void update(Observable o, Object arg) { setChanged(); notifyObservers(); } } /** * Create contents of the window. */ void createContents(Shell shell) { shell.setSize(1238, 657); shell.setText("Pin2dmd - Editor"); shell.setLayout(new GridLayout(4, false)); createMenu(shell); recentProjectsMenuManager = new RecentMenuManager("recentProject", 4, menuPopRecentProjects, e -> loadProject((String) e.widget.getData())); recentProjectsMenuManager.loadRecent(); recentPalettesMenuManager = new RecentMenuManager("recentPalettes", 4, mntmRecentPalettes, e -> paletteHandler.loadPalette((String) e.widget.getData())); recentPalettesMenuManager.loadRecent(); recentAnimationsMenuManager = new RecentMenuManager("recentAnimations", 4, mntmRecentAnimations, e -> aniAction.loadAni(((String) e.widget.getData()), true, false)); recentAnimationsMenuManager.loadRecent(); resManager = new LocalResourceManager(JFaceResources.getResources(), shell); Label lblAnimations = new Label(shell, SWT.NONE); lblAnimations.setText("Animations"); Label lblKeyframes = new Label(shell, SWT.NONE); lblKeyframes.setText("KeyFrames"); Label lblPreview = new Label(shell, SWT.NONE); lblPreview.setText("Preview"); new Label(shell, SWT.NONE); aniListViewer = new TableViewer(shell, SWT.BORDER | SWT.V_SCROLL); Table aniList = aniListViewer.getTable(); GridData gd_aniList = new GridData(SWT.LEFT, SWT.FILL, false, false, 1, 1); gd_aniList.widthHint = 189; aniList.setLayoutData(gd_aniList); aniList.setLinesVisible(true); aniList.addKeyListener(new EscUnselect(aniListViewer)); aniListViewer.setContentProvider(ArrayContentProvider.getInstance()); aniListViewer.setLabelProvider(new LabelProviderAdapter(o -> ((Animation) o).getDesc())); aniListViewer.setInput(animations.values()); aniListViewer.addSelectionChangedListener(event -> { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); onAnimationSelectionChanged(selection.size() > 0 ? (Animation) selection.getFirstElement() : null); }); TableViewerColumn viewerCol1 = new TableViewerColumn(aniListViewer, SWT.LEFT); viewerCol1.setEditingSupport( new GenericTextCellEditor(aniListViewer, e -> ((Animation) e).getDesc(), (e, v) -> { Animation ani = (Animation) e; updateAnimationMapKey(ani.getDesc(), v); ani.setDesc(v); frameSeqViewer.refresh(); })); viewerCol1.getColumn().setWidth(220); viewerCol1.setLabelProvider(new ColumnLabelProviderAdapter(o -> ((Animation) o).getDesc())); keyframeTableViewer = new TableViewer(shell, SWT.SINGLE | SWT.V_SCROLL); Table keyframeList = keyframeTableViewer.getTable(); GridData gd_keyframeList = new GridData(SWT.LEFT, SWT.FILL, false, false, 1, 1); gd_keyframeList.widthHint = 137; keyframeList.setLinesVisible(true); keyframeList.setLayoutData(gd_keyframeList); keyframeList.addKeyListener(new EscUnselect(keyframeTableViewer)); //keyframeTableViewer.setLabelProvider(new KeyframeLabelProvider(shell)); keyframeTableViewer.setContentProvider(ArrayContentProvider.getInstance()); keyframeTableViewer.setInput(project.palMappings); keyframeTableViewer.addSelectionChangedListener(event -> keyFrameChanged(event)); TableViewerColumn viewerColumn = new TableViewerColumn(keyframeTableViewer, SWT.LEFT); viewerColumn.setEditingSupport( new GenericTextCellEditor(keyframeTableViewer, e -> ((PalMapping) e).name, (e, v) -> { ((PalMapping) e).name = v; })); viewerColumn.getColumn().setWidth(200); viewerColumn.setLabelProvider(new KeyframeLabelProvider(shell)); dmdWidget = new DMDWidget(shell, SWT.DOUBLE_BUFFERED, this.dmd, true); // dmdWidget.setBounds(0, 0, 700, 240); GridData gd_dmdWidget = new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1); gd_dmdWidget.heightHint = 231; gd_dmdWidget.widthHint = 826; dmdWidget.setLayoutData(gd_dmdWidget); dmdWidget.setPalette(activePalette); dmdWidget.addListeners(l -> frameChanged(l)); Composite composite_1 = new Composite(shell, SWT.NONE); composite_1.setLayout(new GridLayout(2, false)); GridData gd_composite_1 = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gd_composite_1.heightHint = 35; gd_composite_1.widthHint = 206; composite_1.setLayoutData(gd_composite_1); btnRemoveAni = new Button(composite_1, SWT.NONE); btnRemoveAni.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1)); btnRemoveAni.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { } }); btnRemoveAni.setText("Remove"); btnRemoveAni.setEnabled(false); btnRemoveAni.addListener(SWT.Selection, e -> { if (selectedAnimation.isPresent()) { String key = selectedAnimation.get().getDesc(); animations.remove(key); playingAnis.clear(); animationHandler.setAnimations(playingAnis); animationHandler.setClockActive(true); } }); btnSortAni = new Button(composite_1, SWT.NONE); btnSortAni.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1)); btnSortAni.setText("Sort"); btnSortAni.addListener(SWT.Selection, e -> sortAnimations()); Composite composite_2 = new Composite(shell, SWT.NONE); composite_2.setLayout(new GridLayout(3, false)); GridData gd_composite_2 = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gd_composite_2.heightHint = 35; gd_composite_2.widthHint = 157; composite_2.setLayoutData(gd_composite_2); btnDeleteKeyframe = new Button(composite_2, SWT.NONE); GridData gd_btnDeleteKeyframe = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gd_btnDeleteKeyframe.widthHint = 88; btnDeleteKeyframe.setLayoutData(gd_btnDeleteKeyframe); btnDeleteKeyframe.setText("Remove"); btnDeleteKeyframe.setEnabled(false); btnDeleteKeyframe.addListener(SWT.Selection, e -> { if (selectedPalMapping != null) { project.palMappings.remove(selectedPalMapping); keyframeTableViewer.refresh(); checkReleaseMask(); } }); Button btnSortKeyFrames = new Button(composite_2, SWT.NONE); btnSortKeyFrames.setText("Sort"); btnSortKeyFrames.addListener(SWT.Selection, e -> sortKeyFrames()); new Label(composite_2, SWT.NONE); scale = new Scale(shell, SWT.NONE); scale.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); scale.addListener(SWT.Selection, e -> animationHandler.setPos(scale.getSelection())); Group grpKeyframe = new Group(shell, SWT.NONE); grpKeyframe.setLayout(new GridLayout(3, false)); GridData gd_grpKeyframe = new GridData(SWT.FILL, SWT.TOP, false, false, 2, 4); gd_grpKeyframe.heightHint = 191; gd_grpKeyframe.widthHint = 350; grpKeyframe.setLayoutData(gd_grpKeyframe); grpKeyframe.setText("KeyFrames"); Composite composite_hash = new Composite(grpKeyframe, SWT.NONE); //gd_composite_hash.widthHint = 105; GridData gd_composite_hash = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); gd_composite_hash.widthHint = 148; composite_hash.setLayoutData(gd_composite_hash); createHashButtons(composite_hash, 10, 0); previewDmd = new DMDWidget(grpKeyframe, SWT.DOUBLE_BUFFERED, dmd, false); GridData gd_dmdPreWidget = new GridData(SWT.CENTER, SWT.TOP, false, false, 1, 1); gd_dmdPreWidget.heightHint = 40; gd_dmdPreWidget.widthHint = 132; previewDmd.setLayoutData(gd_dmdPreWidget); previewDmd.setDrawingEnabled(false); previewDmd.setPalette(previewPalettes.get(0)); new Label(grpKeyframe, SWT.NONE); btnAddColormaskKeyFrame = new Button(grpKeyframe, SWT.NONE); btnAddColormaskKeyFrame .setToolTipText("Adds a key frame that trigger a color masking scene to be overlayed"); btnAddColormaskKeyFrame.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); btnAddColormaskKeyFrame.setText("Add ColorMask"); btnAddColormaskKeyFrame.setEnabled(false); btnAddColormaskKeyFrame.addListener(SWT.Selection, e -> addFrameSeq(SwitchMode.ADD)); btnAddKeyframe = new Button(grpKeyframe, SWT.NONE); btnAddKeyframe.setToolTipText("Adds a key frame that switches palette"); btnAddKeyframe.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1)); btnAddKeyframe.setText("Add PalSwitch"); btnAddKeyframe.setEnabled(false); btnAddKeyframe.addListener(SWT.Selection, e -> addKeyFrame(SwitchMode.PALETTE)); Label lblDuration = new Label(grpKeyframe, SWT.NONE); lblDuration.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); lblDuration.setText("Duration:"); txtDuration = new Text(grpKeyframe, SWT.BORDER); GridData gd_txtDuration = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); gd_txtDuration.widthHint = 93; txtDuration.setLayoutData(gd_txtDuration); txtDuration.setText("0"); txtDuration.addListener(SWT.Verify, e -> e.doit = Pattern.matches("^[0-9]*$", e.text)); txtDuration.addListener(SWT.Modify, e -> { if (selectedPalMapping != null) { selectedPalMapping.durationInMillis = Integer.parseInt(txtDuration.getText()); selectedPalMapping.durationInFrames = (int) selectedPalMapping.durationInMillis / 40; } }); btnFetchDuration = new Button(grpKeyframe, SWT.NONE); btnFetchDuration.setToolTipText( "Fetches duration for palette switches by calculating the difference between actual timestamp and keyframe timestamp"); btnFetchDuration.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); btnFetchDuration.setText("Fetch Duration"); btnFetchDuration.setEnabled(false); btnFetchDuration.addListener(SWT.Selection, e -> { if (selectedPalMapping != null) { selectedPalMapping.durationInMillis = lastTimeCode - saveTimeCode; selectedPalMapping.durationInFrames = (int) selectedPalMapping.durationInMillis / FRAME_RATE; txtDuration.setText(selectedPalMapping.durationInMillis + ""); } }); Label lblNewLabel = new Label(grpKeyframe, SWT.NONE); lblNewLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); lblNewLabel.setText("FrameSeq:"); frameSeqViewer = new ComboViewer(grpKeyframe, SWT.NONE); Combo frameSeqCombo = frameSeqViewer.getCombo(); frameSeqCombo.setToolTipText("Choose frame sequence to use with key frame"); GridData gd_frameSeqCombo = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); gd_frameSeqCombo.widthHint = 100; frameSeqCombo.setLayoutData(gd_frameSeqCombo); frameSeqViewer.setLabelProvider(new LabelProviderAdapter(o -> ((Animation) o).getDesc())); frameSeqViewer.setContentProvider(ArrayContentProvider.getInstance()); frameSeqViewer.setInput(frameSeqList); frameSeqViewer.addSelectionChangedListener(event -> frameSeqChanged(event)); btnAddFrameSeq = new Button(grpKeyframe, SWT.NONE); btnAddFrameSeq.setToolTipText("Adds a keyframe that triggers playback of a replacement scene"); btnAddFrameSeq.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); btnAddFrameSeq.setText("Add FrameSeq"); btnAddFrameSeq.addListener(SWT.Selection, e -> addFrameSeq(SwitchMode.REPLACE)); btnAddFrameSeq.setEnabled(false); Group grpDetails = new Group(shell, SWT.NONE); grpDetails.setLayout(new GridLayout(10, false)); GridData gd_grpDetails = new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1); gd_grpDetails.heightHint = 21; gd_grpDetails.widthHint = 776; grpDetails.setLayoutData(gd_grpDetails); grpDetails.setText("Details"); Label lblFrame = new Label(grpDetails, SWT.NONE); lblFrame.setText("Frame:"); lblFrameNo = new Label(grpDetails, SWT.NONE); GridData gd_lblFrameNo = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gd_lblFrameNo.widthHint = 66; gd_lblFrameNo.minimumWidth = 60; lblFrameNo.setLayoutData(gd_lblFrameNo); lblFrameNo.setText("---"); Label lblTimecode = new Label(grpDetails, SWT.NONE); lblTimecode.setText("Timecode:"); lblTcval = new Label(grpDetails, SWT.NONE); GridData gd_lblTcval = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gd_lblTcval.widthHint = 62; gd_lblTcval.minimumWidth = 80; lblTcval.setLayoutData(gd_lblTcval); lblTcval.setText("---"); Label lblDelay = new Label(grpDetails, SWT.NONE); lblDelay.setText("Delay:"); lblDelayVal = new Label(grpDetails, SWT.NONE); GridData gd_lblDelayVal = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gd_lblDelayVal.widthHint = 53; lblDelayVal.setLayoutData(gd_lblDelayVal); lblDelayVal.setText("---"); Label lblPlanes = new Label(grpDetails, SWT.NONE); lblPlanes.setText("Planes:"); lblPlanesVal = new Label(grpDetails, SWT.NONE); lblPlanesVal.setText("---"); new Label(grpDetails, SWT.NONE); btnLivePreview = new Button(grpDetails, SWT.CHECK); btnLivePreview.setToolTipText("controls live preview to real display device"); btnLivePreview.setText("Live Preview"); btnLivePreview.addListener(SWT.Selection, e -> switchLivePreview(e)); Composite composite = new Composite(shell, SWT.NONE); composite.setLayout(new GridLayout(9, false)); composite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); btnStartStop = new Button(composite, SWT.NONE); btnStartStop.setText("Start"); btnStartStop.addListener(SWT.Selection, e -> startStop(animationHandler.isStopped())); btnPrev = new Button(composite, SWT.NONE); btnPrev.setText("<"); btnPrev.addListener(SWT.Selection, e -> prevFrame()); btnNext = new Button(composite, SWT.NONE); btnNext.setText(">"); btnNext.addListener(SWT.Selection, e -> nextFrame()); btnMarkStart = new Button(composite, SWT.NONE); btnMarkStart.setToolTipText("Marks start of scene for cutting"); btnMarkEnd = new Button(composite, SWT.NONE); btnCut = new Button(composite, SWT.NONE); btnCut.setToolTipText("Cuts out a new scene for editing and use a replacement or color mask"); btnMarkStart.setText("Mark Start"); btnMarkStart.addListener(SWT.Selection, e -> { cutInfo.setStart(selectedAnimation.get().actFrame); }); btnMarkEnd.setText("Mark End"); btnMarkEnd.addListener(SWT.Selection, e -> { cutInfo.setEnd(selectedAnimation.get().actFrame); }); btnCut.setText("Cut"); btnCut.addListener(SWT.Selection, e -> { // respect number of planes while cutting / copying Animation ani = cutScene(selectedAnimation.get(), cutInfo.getStart(), cutInfo.getEnd(), "Scene " + animations.size()); log.info("cutting out scene from {} to {}", cutInfo); cutInfo.reset(); // TODO mark such a scene somehow, to copy it to the // projects frames sequence for later export // alternatively introduce a dedicated flag for scenes that // should be exported // also define a way that a keyframe triggers a replacement // sequence instead of switching // the palette only // TODO NEED TO ADD a reference to the animation in the list // / map project.scenes.add(new Scene(ani.getDesc(), ani.start, ani.end, activePalette.index)); }); new Label(composite, SWT.NONE); Button btnIncPitch = new Button(composite, SWT.NONE); btnIncPitch.setText("+"); btnIncPitch.addListener(SWT.Selection, e -> dmdWidget.incPitch()); Button btnDecPitch = new Button(composite, SWT.NONE); btnDecPitch.setText("-"); btnDecPitch.addListener(SWT.Selection, e -> dmdWidget.decPitch()); Group grpPalettes = new Group(shell, SWT.NONE); grpPalettes.setLayout(new GridLayout(4, false)); GridData gd_grpPalettes = new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1); gd_grpPalettes.widthHint = 479; gd_grpPalettes.heightHint = 71; grpPalettes.setLayoutData(gd_grpPalettes); grpPalettes.setText("Palettes"); paletteComboViewer = new ComboViewer(grpPalettes, SWT.NONE); Combo combo = paletteComboViewer.getCombo(); GridData gd_combo = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); gd_combo.widthHint = 166; combo.setLayoutData(gd_combo); paletteComboViewer.setContentProvider(ArrayContentProvider.getInstance()); paletteComboViewer .setLabelProvider(new LabelProviderAdapter(o -> ((Palette) o).index + " - " + ((Palette) o).name)); paletteComboViewer.setInput(project.palettes); paletteComboViewer.addSelectionChangedListener(event -> { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); if (selection.size() > 0) { paletteChanged((Palette) selection.getFirstElement()); } }); paletteTypeComboViewer = new ComboViewer(grpPalettes, SWT.READ_ONLY); Combo combo_1 = paletteTypeComboViewer.getCombo(); combo_1.setToolTipText( "Type of palette. Default palette is choosen at start and after timed switch is expired"); GridData gd_combo_1 = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); gd_combo_1.widthHint = 85; combo_1.setLayoutData(gd_combo_1); paletteTypeComboViewer.setContentProvider(ArrayContentProvider.getInstance()); paletteTypeComboViewer.setInput(PaletteType.values()); paletteTypeComboViewer.setSelection(new StructuredSelection(activePalette.type)); paletteTypeComboViewer.addSelectionChangedListener(e -> paletteTypeChanged(e)); btnNewPalette = new Button(grpPalettes, SWT.NONE); btnNewPalette.setToolTipText("Creates a new palette by copying the actual colors"); btnNewPalette.setText("New"); btnNewPalette.addListener(SWT.Selection, e -> paletteHandler.newPalette()); btnRenamePalette = new Button(grpPalettes, SWT.NONE); btnRenamePalette.setToolTipText("Confirms the new palette name"); btnRenamePalette.setText("Rename"); btnRenamePalette.addListener(SWT.Selection, e -> { String newName = paletteComboViewer.getCombo().getText(); if (newName.contains(" - ")) { activePalette.name = newName.split(" - ")[1]; paletteComboViewer.setSelection(new StructuredSelection(activePalette)); paletteComboViewer.refresh(); } else { warn("Illegal palette name", "Palette names must consist of palette index and name.\nName format therefore must be '<idx> - <name>'"); paletteComboViewer.getCombo().setText(activePalette.index + " - " + activePalette.name); } }); Composite grpPal = new Composite(grpPalettes, SWT.NONE); grpPal.setLayout(new GridLayout(1, false)); GridData gd_grpPal = new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1); gd_grpPal.widthHint = 313; gd_grpPal.heightHint = 22; grpPal.setLayoutData(gd_grpPal); // GridData gd_grpPal = new GridData(SWT.LEFT, SWT.CENTER, false, false, // 1, 1); // gd_grpPal.widthHint = 223; // gd_grpPal.heightHint = 61; // grpPal.setLayoutData(gd_grpPal); // paletteTool = new PaletteTool(shell, grpPal, SWT.FLAT | SWT.RIGHT, activePalette); paletteTool.addListener(dmdWidget); Label lblCtrlclickToEdit = new Label(grpPalettes, SWT.NONE); GridData gd_lblCtrlclickToEdit = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1); gd_lblCtrlclickToEdit.widthHint = 139; lblCtrlclickToEdit.setLayoutData(gd_lblCtrlclickToEdit); lblCtrlclickToEdit.setText("Ctrl-Click to edit color"); Composite composite_3 = new Composite(shell, SWT.NONE); GridLayout gl_composite_3 = new GridLayout(1, false); gl_composite_3.marginWidth = 0; gl_composite_3.marginHeight = 0; composite_3.setLayout(gl_composite_3); GridData gd_composite_3 = new GridData(SWT.LEFT, SWT.FILL, false, false, 1, 2); gd_composite_3.heightHint = 190; gd_composite_3.widthHint = 338; composite_3.setLayoutData(gd_composite_3); goDmdGroup = new GoDmdGroup(composite_3); Group grpDrawing = new Group(shell, SWT.NONE); grpDrawing.setLayout(new GridLayout(6, false)); GridData gd_grpDrawing = new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1); gd_grpDrawing.heightHint = 63; gd_grpDrawing.widthHint = 479; grpDrawing.setLayoutData(gd_grpDrawing); grpDrawing.setText("Drawing"); drawToolBar = new ToolBar(grpDrawing, SWT.FLAT | SWT.RIGHT); drawToolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1)); ToolItem tltmPen = new ToolItem(drawToolBar, SWT.RADIO); tltmPen.setImage( resManager.createImage(ImageDescriptor.createFromFile(PinDmdEditor.class, "/icons/pencil.png"))); tltmPen.addListener(SWT.Selection, e -> dmdWidget.setDrawTool(drawTools.get("pencil"))); ToolItem tltmFill = new ToolItem(drawToolBar, SWT.RADIO); tltmFill.setImage(resManager .createImage(ImageDescriptor.createFromFile(PinDmdEditor.class, "/icons/color-fill.png"))); tltmFill.addListener(SWT.Selection, e -> dmdWidget.setDrawTool(drawTools.get("fill"))); ToolItem tltmRect = new ToolItem(drawToolBar, SWT.RADIO); tltmRect.setImage( resManager.createImage(ImageDescriptor.createFromFile(PinDmdEditor.class, "/icons/rect.png"))); tltmRect.addListener(SWT.Selection, e -> dmdWidget.setDrawTool(drawTools.get("rect"))); ToolItem tltmLine = new ToolItem(drawToolBar, SWT.RADIO); tltmLine.setImage( resManager.createImage(ImageDescriptor.createFromFile(PinDmdEditor.class, "/icons/line.png"))); tltmLine.addListener(SWT.Selection, e -> dmdWidget.setDrawTool(drawTools.get("line"))); ToolItem tltmCircle = new ToolItem(drawToolBar, SWT.RADIO); tltmCircle.setImage( resManager.createImage(ImageDescriptor.createFromFile(PinDmdEditor.class, "/icons/oval.png"))); tltmCircle.addListener(SWT.Selection, e -> dmdWidget.setDrawTool(drawTools.get("circle"))); ToolItem tltmColorize = new ToolItem(drawToolBar, SWT.RADIO); tltmColorize.setImage( resManager.createImage(ImageDescriptor.createFromFile(PinDmdEditor.class, "/icons/colorize.png"))); tltmColorize.addListener(SWT.Selection, e -> dmdWidget.setDrawTool(drawTools.get("colorize"))); drawTools.put("pencil", new SetPixelTool(paletteTool.getSelectedColor())); drawTools.put("fill", new FloodFillTool(paletteTool.getSelectedColor())); drawTools.put("rect", new RectTool(paletteTool.getSelectedColor())); drawTools.put("line", new LineTool(paletteTool.getSelectedColor())); drawTools.put("circle", new CircleTool(paletteTool.getSelectedColor())); drawTools.put("colorize", new ColorizeTool(paletteTool.getSelectedColor())); drawTools.values().forEach(d -> paletteTool.addIndexListener(d)); paletteTool.addListener(palette -> { if (livePreviewActive) { connector.upload(activePalette, handle); } }); new Label(grpDrawing, SWT.NONE); btnColorMask = new Button(grpDrawing, SWT.CHECK); btnColorMask.setToolTipText("limits drawing to upper planes, so that this will just add coloring layers"); btnColorMask.setText("ColMask"); btnColorMask.addListener(SWT.Selection, e -> switchColorMask(btnColorMask.getSelection())); Label lblMaskNo = new Label(grpDrawing, SWT.NONE); lblMaskNo.setText("Mask No:"); maskSpinner = new Spinner(grpDrawing, SWT.BORDER); maskSpinner.setToolTipText("select the mask to use"); maskSpinner.setMinimum(0); maskSpinner.setMaximum(9); maskSpinner.addListener(SWT.Selection, e -> maskNumberChanged(e)); btnMask = new Button(grpDrawing, SWT.CHECK); btnMask.setText("Show Mask"); btnMask.addListener(SWT.Selection, e -> switchMask(btnMask.getSelection())); btnCopyToPrev = new Button(grpDrawing, SWT.NONE); btnCopyToPrev.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); btnCopyToPrev.setText("CopyToPrev"); btnCopyToPrev.addListener(SWT.Selection, e -> copyAndMoveToPrevFrame()); new Label(grpDrawing, SWT.NONE); btnCopyToNext = new Button(grpDrawing, SWT.NONE); btnCopyToNext.setToolTipText("copy the actual scene / color mask to next frame and move forward"); btnCopyToNext.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); btnCopyToNext.setText("CopyToNext"); btnCopyToNext.addListener(SWT.Selection, e -> copyAndMoveToNextFrame()); btnUndo = new Button(grpDrawing, SWT.NONE); btnUndo.setText("&Undo"); btnUndo.addListener(SWT.Selection, e -> undo()); btnRedo = new Button(grpDrawing, SWT.NONE); btnRedo.setText("&Redo"); btnRedo.addListener(SWT.Selection, e -> redo()); ObserverManager.bind(maskDmdObserver, e -> btnUndo.setEnabled(e), () -> maskDmdObserver.canUndo()); ObserverManager.bind(maskDmdObserver, e -> btnRedo.setEnabled(e), () -> maskDmdObserver.canRedo()); } private void startStop(boolean stopped) { if (stopped) { selectedAnimation.orElse(defaultAnimation).commitDMDchanges(dmd); animationHandler.start(); display.timerExec(animationHandler.getRefreshDelay(), cyclicRedraw); btnStartStop.setText("Stop"); } else { animationHandler.stop(); btnStartStop.setText("Start"); } } private void prevFrame() { selectedAnimation.orElse(defaultAnimation).commitDMDchanges(dmd); animationHandler.prev(); } private void nextFrame() { selectedAnimation.orElse(defaultAnimation).commitDMDchanges(dmd); animationHandler.next(); } private void copyAndMoveToNextFrame() { nextFrame(); CompiledAnimation ani = (CompiledAnimation) selectedAnimation.get(); if (!ani.hasEnded()) { copyFrame(ani, ani.getActFrame(), -1); } } private void copyAndMoveToPrevFrame() { prevFrame(); CompiledAnimation ani = (CompiledAnimation) selectedAnimation.get(); if (ani.getActFrame() > ani.getStart()) { copyFrame(ani, ani.getActFrame(), 1); } } private void copyFrame(CompiledAnimation cani, int n, int offset) { Frame srcFrame = cani.frames.get(n + offset); Frame actFrame = cani.frames.get(n); int drawMask = dmd.getDrawMask(); dmd.addUndoBuffer(); for (int i = 0; i < actFrame.planes.size(); i++) { if ((drawMask & 1) != 0) { int size = srcFrame.planes.get(i).plane.length; System.arraycopy(srcFrame.planes.get(i).plane, 0, dmd.getFrame().planes.get(i).plane, 0, size); dmdRedraw(); } drawMask >>= 1; } } private void sortKeyFrames() { Collections.sort(project.palMappings, new Comparator<PalMapping>() { @Override public int compare(PalMapping o1, PalMapping o2) { return o1.name.compareTo(o2.name); } }); keyframeTableViewer.refresh(); } private void switchColorMask(boolean on) { dmd.setDrawMask(on ? 0b11111100 : 0xFFFF); } /** * checks all pal mappings and releases masks if not used anymore */ private void checkReleaseMask() { HashSet<Integer> useMasks = new HashSet<>(); for (PalMapping p : project.palMappings) { if (p.withMask) { useMasks.add(p.maskNumber); } } for (int i = 0; i < project.masks.size(); i++) { project.masks.get(i).locked = useMasks.contains(i); } switchMask(useMask); } private void onAnimationSelectionChanged(Animation a) { Animation current = selectedAnimation.get(); if (current.isMutable()) { goDmdGroup.updateAnimation(current); } if (a != null) { selectedAnimation = Optional.of(a); int numberOfPlanes = selectedAnimation.get().getRenderer().getNumberOfPlanes(); if (numberOfPlanes == 3) { numberOfPlanes = 2; goDmdGroup.transitionCombo.select(1); } else { goDmdGroup.transitionCombo.select(0); } dmd.setNumberOfSubframes(numberOfPlanes); paletteTool.setNumberOfPlanes(useMask ? 1 : numberOfPlanes); //planesComboViewer.setSelection(new StructuredSelection(PlaneNumber.valueOf(numberOfPlanes))); playingAnis.clear(); playingAnis.add(selectedAnimation.get()); animationHandler.setAnimations(playingAnis); dmdRedraw(); } else { selectedAnimation = Optional.of(defaultAnimation); } goDmdGroup.updateAniModel(selectedAnimation.get()); btnRemoveAni.setEnabled(a != null); btnAddKeyframe.setEnabled(a != null); } private void paletteChanged(Palette newPalette) { activePalette = newPalette; if (selectedPalMapping != null) selectedPalMapping.palIndex = activePalette.index; // change palette in ANI file if (selectedAnimation.get().isMutable()) { selectedAnimation.get().setPalIndex(activePalette.index); } dmdWidget.setPalette(activePalette); paletteTool.setPalette(activePalette); log.info("new palette is {}", activePalette); paletteTypeComboViewer.setSelection(new StructuredSelection(activePalette.type)); if (livePreviewActive) connector.switchToPal(activePalette.index, handle); } void updateAnimationMapKey(String oldKey, String newKey) { ArrayList<Animation> tmp = new ArrayList<Animation>(); if (!oldKey.equals(newKey)) { animations.values().forEach(ani -> tmp.add(ani)); animations.clear(); tmp.forEach(ani -> animations.put(ani.getDesc(), ani)); } } private void frameChanged(Frame frame) { if (livePreviewActive) { connector.sendFrame(frame, handle); } } private void switchLivePreview(Event e) { boolean selection = btnLivePreview.getSelection(); if (selection) { try { connector.switchToMode(DeviceMode.PinMame_RGB.ordinal(), null); handle = connector.connect(pin2dmdAdress); livePreviewActive = selection; for (Palette pal : project.palettes) { connector.upload(pal, handle); } setEnableUsbTooling(!selection); } catch (RuntimeException ex) { warn("usb problem", "Message was: " + ex.getMessage()); btnLivePreview.setSelection(false); } } else { if (handle != null) { try { connector.release(handle); livePreviewActive = selection; setEnableUsbTooling(!selection); } catch (RuntimeException ex) { warn("usb problem", "Message was: " + ex.getMessage()); } handle = null; } } } private void setEnableUsbTooling(boolean enabled) { mntmUploadPalettes.setEnabled(enabled); mntmUploadProject.setEnabled(enabled); } private void addFrameSeq(SwitchMode switchMode) { if (!frameSeqViewer.getSelection().isEmpty()) { if (selectedHashIndex != -1) { Animation ani = (Animation) ((IStructuredSelection) frameSeqViewer.getSelection()) .getFirstElement(); // TODO add index, add ref to framesSeq PalMapping palMapping = new PalMapping(0, "KeyFrame " + ani.getDesc()); palMapping.setDigest(hashes.get(selectedHashIndex)); palMapping.palIndex = activePalette.index; palMapping.frameSeqName = ani.getDesc(); palMapping.animationName = selectedAnimation.get().getDesc(); palMapping.switchMode = switchMode; palMapping.frameIndex = selectedAnimation.get().actFrame; if (useMask) { palMapping.withMask = useMask; palMapping.maskNumber = actMaskNumber; project.masks.get(actMaskNumber).locked = true; switchMask(true); } if (!checkForDuplicateKeyFrames(palMapping)) { project.palMappings.add(palMapping); keyframeTableViewer.refresh(); } else { warn("duplicate hash", "There is already another Keyframe that uses the same hash"); } } else { warn("no hash selected", "in order to create a key frame mapping, you must select a hash"); } } } private void warn(String header, String msg) { MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK); messageBox.setText(header); messageBox.setMessage(msg); messageBox.open(); } private void switchMask(boolean useMask) { this.useMask = useMask; Mask maskToUse = project.masks.get(maskSpinner.getSelection()); if (useMask) { activateMask(maskToUse); } else { deactivateMask(maskToUse); } editAniObserver.update(animationHandler, null); } private void deactivateMask(Mask mask) { DMD dmdMask = dmdWidget.getMask(); if (dmdMask != null) System.arraycopy(dmdMask.getFrame().planes.get(0).plane, 0, mask.data, 0, mask.data.length); int planes = dmd.getNumberOfPlanes(); paletteTool.setNumberOfPlanes(planes); dmdWidget.setMask(null, false); maskDmdObserver.setMask(null); animationHandler.setMask(emptyMask); } void activateMask(Mask mask) { DMD maskDMD = new DMD(128, 32); Frame frame = new Frame(); frame.planes.add(new Plane((byte) 0, mask.data)); maskDMD.setFrame(frame); paletteTool.setNumberOfPlanes(1); dmdWidget.setMask(maskDMD, mask.locked); maskDmdObserver.setMask(maskDMD); animationHandler.setMask(mask.data); } void maskNumberChanged(Event e) { int newMaskNumber = ((Spinner) e.widget).getSelection(); if (useMask && newMaskNumber != actMaskNumber) { log.info("mask number changed {} -> {}", actMaskNumber, newMaskNumber); deactivateMask(project.masks.get(actMaskNumber)); activateMask(project.masks.get(newMaskNumber)); actMaskNumber = newMaskNumber; editAniObserver.update(animationHandler, null); } } private void sortAnimations() { ArrayList<Entry<String, Animation>> list = new ArrayList<>(animations.entrySet()); Collections.sort(list, new Comparator<Entry<String, Animation>>() { @Override public int compare(Entry<String, Animation> o1, Entry<String, Animation> o2) { return o1.getValue().getDesc().compareTo(o2.getValue().getDesc()); } }); animations.clear(); for (Entry<String, Animation> entry : list) { animations.put(entry.getKey(), entry.getValue()); } } private void dmdRedraw() { dmdWidget.redraw(); previewDmd.redraw(); } void frameSeqChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); btnAddFrameSeq.setEnabled(selection.size() > 0); btnAddColormaskKeyFrame.setEnabled(selection.size() > 0); } void keyFrameChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); if (selection.size() > 0) { if (((PalMapping) selection.getFirstElement()).equals(selectedPalMapping)) { keyframeTableViewer.setSelection(StructuredSelection.EMPTY); selectedPalMapping = null; return; } // set new mapping selectedPalMapping = (PalMapping) selection.getFirstElement(); log.debug("selected new palMapping {}", selectedPalMapping); selectedHashIndex = selectedPalMapping.hashIndex; // current firmware always checks with and w/o mask // btnMask.setSelection(selectedPalMapping.withMask); // btnMask.notifyListeners(SWT.Selection, new Event()); txtDuration.setText(selectedPalMapping.durationInMillis + ""); paletteComboViewer .setSelection(new StructuredSelection(project.palettes.get(selectedPalMapping.palIndex))); for (int j = 0; j < numberOfHashes; j++) { btnHash[j].setSelection(j == selectedHashIndex); } selectedAnimation = Optional.of(animations.get(selectedPalMapping.animationName)); aniListViewer.setSelection(new StructuredSelection(selectedAnimation.get())); if (selectedPalMapping.frameSeqName != null) frameSeqViewer .setSelection(new StructuredSelection(animations.get(selectedPalMapping.frameSeqName))); animationHandler.setPos(selectedPalMapping.frameIndex); if (selectedPalMapping.withMask) { String txt = btnHash[selectedHashIndex].getText(); btnHash[selectedHashIndex].setText("M" + selectedPalMapping.maskNumber + " " + txt); } saveTimeCode = (int) selectedAnimation.get().getTimeCode(selectedPalMapping.frameIndex); } else { selectedPalMapping = null; } btnDeleteKeyframe.setEnabled(selection.size() > 0); btnFetchDuration.setEnabled(selection.size() > 0); } void paletteTypeChanged(SelectionChangedEvent e) { IStructuredSelection selection = (IStructuredSelection) e.getSelection(); PaletteType palType = (PaletteType) selection.getFirstElement(); activePalette.type = palType; if (PaletteType.DEFAULT.equals(palType)) { for (int i = 0; i < project.palettes.size(); i++) { if (i != activePalette.index) { // set previous default to // normal if (project.palettes.get(i).type.equals(PaletteType.DEFAULT)) { project.palettes.get(i).type = PaletteType.NORMAL; } ; } } } } /** * check if dirty. * * @return true, if not dirty or if user decides to ignore dirtyness (or * global ignore flag is set via cmdline) */ boolean dirtyCheck() { if (project.dirty && !nodirty) { MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK | SWT.CANCEL); messageBox.setText("Unsaved Changes"); messageBox.setMessage("There are unsaved changes in project. Proceed?"); int res = messageBox.open(); return (res == SWT.OK); } else { return true; } } /** * creates the top level menu */ private void createMenu(Shell shell) { Menu menu = new Menu(shell, SWT.BAR); shell.setMenuBar(menu); MenuItem mntmfile = new MenuItem(menu, SWT.CASCADE); mntmfile.setText("&File"); Menu menu_1 = new Menu(mntmfile); mntmfile.setMenu(menu_1); MenuItem mntmNewProject = new MenuItem(menu_1, SWT.NONE); mntmNewProject.setText("New Project"); mntmNewProject.addListener(SWT.Selection, e -> { if (dirtyCheck()) { createNewProject(); } }); MenuItem mntmLoadProject = new MenuItem(menu_1, SWT.NONE); mntmLoadProject.addListener(SWT.Selection, e -> loadProject()); mntmLoadProject.setText("Load Project"); MenuItem mntmSaveProject = new MenuItem(menu_1, SWT.NONE); mntmSaveProject.setText("Save Project"); mntmSaveProject.addListener(SWT.Selection, e -> saveProject()); MenuItem mntmRecentProjects = new MenuItem(menu_1, SWT.CASCADE); mntmRecentProjects.setText("Recent Projects"); menuPopRecentProjects = new Menu(mntmRecentProjects); mntmRecentProjects.setMenu(menuPopRecentProjects); new MenuItem(menu_1, SWT.SEPARATOR); MenuItem mntmImportProject = new MenuItem(menu_1, SWT.NONE); mntmImportProject.setText("Import Project"); mntmImportProject.addListener(SWT.Selection, e -> importProject()); MenuItem mntmExportProject = new MenuItem(menu_1, SWT.NONE); mntmExportProject.setText("Export Project"); mntmExportProject.addListener(SWT.Selection, e -> exportProject()); mntmUploadProject = new MenuItem(menu_1, SWT.NONE); mntmUploadProject.setText("Upload Project"); mntmUploadProject.addListener(SWT.Selection, e -> uploadProject()); new MenuItem(menu_1, SWT.SEPARATOR); MenuItem mntmExit = new MenuItem(menu_1, SWT.NONE); mntmExit.setText("Exit"); mntmExit.addListener(SWT.Selection, e -> { if (dirtyCheck()) { shell.close(); shell.dispose(); } }); MenuItem mntmedit = new MenuItem(menu, SWT.CASCADE); mntmedit.setText("&Edit"); Menu menu_5 = new Menu(mntmedit); mntmedit.setMenu(menu_5); MenuItem mntmUndo = new MenuItem(menu_5, SWT.NONE); mntmUndo.setText("Undo"); mntmUndo.addListener(SWT.Selection, e -> undo()); ObserverManager.bind(maskDmdObserver, e -> mntmUndo.setEnabled(e), () -> maskDmdObserver.canUndo()); MenuItem mntmRedo = new MenuItem(menu_5, SWT.NONE); mntmRedo.setText("Redo"); mntmRedo.addListener(SWT.Selection, e -> redo()); ObserverManager.bind(maskDmdObserver, e -> mntmRedo.setEnabled(e), () -> maskDmdObserver.canRedo()); MenuItem mntmAnimations = new MenuItem(menu, SWT.CASCADE); mntmAnimations.setText("&Animations"); Menu menu_2 = new Menu(mntmAnimations); mntmAnimations.setMenu(menu_2); MenuItem mntmLoadAnimation = new MenuItem(menu_2, SWT.NONE); mntmLoadAnimation.setText("Load Animation(s)"); mntmLoadAnimation.addListener(SWT.Selection, e -> aniAction.loadAniWithFC(true)); MenuItem mntmSaveAnimation = new MenuItem(menu_2, SWT.NONE); mntmSaveAnimation.setText("Save Animation(s)"); mntmSaveAnimation.addListener(SWT.Selection, e -> aniAction.saveAniWithFC(1)); MenuItem mntmSaveAniExt = new MenuItem(menu_2, SWT.NONE); mntmSaveAniExt.setText("Save Animation(s) v2"); mntmSaveAniExt.addListener(SWT.Selection, e -> aniAction.saveAniWithFC(2)); MenuItem mntmSaveSingleAnimation = new MenuItem(menu_2, SWT.NONE); mntmSaveSingleAnimation.setText("Save single Animation"); mntmSaveSingleAnimation.addListener(SWT.Selection, e -> aniAction.saveSingleAniWithFC(1)); MenuItem mntmRecentAnimationsItem = new MenuItem(menu_2, SWT.CASCADE); mntmRecentAnimationsItem.setText("Recent Animations"); mntmRecentAnimations = new Menu(mntmRecentAnimationsItem); mntmRecentAnimationsItem.setMenu(mntmRecentAnimations); new MenuItem(menu_2, SWT.SEPARATOR); MenuItem mntmExportAnimation = new MenuItem(menu_2, SWT.NONE); mntmExportAnimation.setText("Export Animation as GIF"); mntmExportAnimation.addListener(SWT.Selection, e -> { GifExporter exporter = new GifExporter(shell, activePalette, playingAnis.get(0)); exporter.open(); }); MenuItem mntmpalettes = new MenuItem(menu, SWT.CASCADE); mntmpalettes.setText("&Palettes / Mode"); Menu menu_3 = new Menu(mntmpalettes); mntmpalettes.setMenu(menu_3); MenuItem mntmLoadPalette = new MenuItem(menu_3, SWT.NONE); mntmLoadPalette.setText("Load Palette"); mntmLoadPalette.addListener(SWT.Selection, e -> paletteHandler.loadPalette()); MenuItem mntmSavePalette = new MenuItem(menu_3, SWT.NONE); mntmSavePalette.setText("Save Palette"); mntmSavePalette.addListener(SWT.Selection, e -> paletteHandler.savePalette()); MenuItem mntmRecentPalettesItem = new MenuItem(menu_3, SWT.CASCADE); mntmRecentPalettesItem.setText("Recent Palettes"); mntmRecentPalettes = new Menu(mntmRecentPalettesItem); mntmRecentPalettesItem.setMenu(mntmRecentPalettes); new MenuItem(menu_3, SWT.SEPARATOR); mntmUploadPalettes = new MenuItem(menu_3, SWT.NONE); mntmUploadPalettes.setText("Upload Palettes"); mntmUploadPalettes.addListener(SWT.Selection, e -> connector.upload(activePalette)); new MenuItem(menu_3, SWT.SEPARATOR); MenuItem mntmDevice = new MenuItem(menu_3, SWT.NONE); mntmDevice.setText("Create Device File / WiFi"); mntmDevice.addListener(SWT.Selection, e -> { DeviceConfig deviceConfig = new DeviceConfig(shell); deviceConfig.open(pin2dmdAdress); refreshPin2DmdHost(deviceConfig.getPin2DmdHost()); }); MenuItem mntmUsbconfig = new MenuItem(menu_3, SWT.NONE); mntmUsbconfig.setText("Configure Device via USB"); mntmUsbconfig.addListener(SWT.Selection, e -> new UsbConfig(shell).open()); MenuItem mntmhelp = new MenuItem(menu, SWT.CASCADE); mntmhelp.setText("&Help"); Menu menu_4 = new Menu(mntmhelp); mntmhelp.setMenu(menu_4); MenuItem mntmGetHelp = new MenuItem(menu_4, SWT.NONE); mntmGetHelp.setText("Get help"); mntmGetHelp.addListener(SWT.Selection, e -> Program.launch(HELP_URL)); mntmGodmd = new MenuItem(menu_4, SWT.CHECK); mntmGodmd.setText("goDMD Panel"); mntmGodmd.addListener(SWT.Selection, e -> { boolean goDMDenabled = mntmGodmd.getSelection(); goDmdGroup.grpGoDMDCrtls.setVisible(goDMDenabled); ApplicationProperties.put(ApplicationProperties.GODMD_ENABLED_PROP_KEY, Boolean.toString(goDMDenabled)); }); mntmGodmd.setSelection(ApplicationProperties.getBoolean(ApplicationProperties.GODMD_ENABLED_PROP_KEY)); MenuItem mntmRegister = new MenuItem(menu_4, SWT.NONE); mntmRegister.setText("Register"); mntmRegister.addListener(SWT.Selection, e -> new RegisterLicense(shell).open()); new MenuItem(menu_4, SWT.SEPARATOR); MenuItem mntmAbout = new MenuItem(menu_4, SWT.NONE); mntmAbout.setText("About"); mntmAbout.addListener(SWT.Selection, e -> new About(shell).open()); } private void redo() { maskDmdObserver.redo(); dmdRedraw(); } private void undo() { maskDmdObserver.undo(); dmdRedraw(); } public String getPrintableHashes(byte[] p) { StringBuffer hexString = new StringBuffer(); for (int j = 0; j < p.length; j++) hexString.append(String.format("%02X", p[j])); return hexString.toString(); } @Override public void notifyAni(AniEvent evt) { switch (evt.evtType) { case ANI: lblFrameNo.setText("" + evt.actFrame); lblTcval.setText("" + evt.timecode); lblDelayVal.setText("" + evt.delay); lblPlanesVal.setText("" + evt.nPlanes); // hashLabel.setText( int i = 0; for (byte[] p : evt.hashes) { String hash = getPrintableHashes(p); if (hash.startsWith("B2AA7578" /* "BF619EAC0CDF3F68D496EA9344137E8B" */)) { // disable // for // empty // frame btnHash[i].setText(""); btnHash[i].setEnabled(false); } else { btnHash[i].setText(hash); btnHash[i].setEnabled(true); } i++; if (i >= btnHash.length) break; } while (i < 4) { btnHash[i].setText(""); btnHash[i].setEnabled(false); i++; } saveHashes(evt.hashes); lastTimeCode = evt.timecode; if (livePreviewActive && evt.frame != null) { connector.sendFrame(evt.frame, handle); } break; case CLOCK: lblFrameNo.setText(""); lblTcval.setText(""); // sourceList.deselectAll(); for (int j = 0; j < 4; j++) btnHash[j++].setText(""); // clear hashes break; case CLEAR: for (int j = 0; j < 4; j++) btnHash[j++].setText(""); // clear hashes if (livePreviewActive) { connector.sendFrame(new Frame(), handle); } break; } dmdRedraw(); } }