From 69f520428d1f7345d28ed75debffa546cd94816c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 2 Apr 2018 22:42:41 -0400 Subject: [PATCH 1/8] Makes a first, ugly attempt at a 'new machine' dialogue for the Mac. Which has implied getting much more specific about MSX disk drive attachment, and has prompted an excuse to offer the ZX80 with the ZX81 ROM. --- Analyser/Static/Acorn/Target.hpp | 6 +- Analyser/Static/AmstradCPC/Target.hpp | 6 +- Analyser/Static/Atari/Target.hpp | 6 +- Analyser/Static/Commodore/Target.hpp | 6 +- Analyser/Static/MSX/StaticAnalyser.cpp | 9 +- Analyser/Static/MSX/Target.hpp | 26 ++ Analyser/Static/Oric/StaticAnalyser.cpp | 6 +- Analyser/Static/Oric/Target.hpp | 8 +- Analyser/Static/ZX8081/StaticAnalyser.cpp | 8 +- Analyser/Static/ZX8081/Target.hpp | 9 +- Machines/MSX/MSX.cpp | 6 +- Machines/Oric/Oric.cpp | 2 +- Machines/ZX8081/ZX8081.cpp | 6 +- .../Clock Signal.xcodeproj/project.pbxproj | 29 +- .../DocumentController.swift | 16 + .../Documents/MachineDocument.swift | 62 +-- .../Machine/StaticAnalyser/CSStaticAnalyser.h | 29 ++ .../StaticAnalyser/CSStaticAnalyser.mm | 121 ++++++ .../New Group/Base.lproj/MachinePicker.xib | 387 ++++++++++++++++++ .../New Group/MachinePicker.swift | 90 ++++ 20 files changed, 775 insertions(+), 63 deletions(-) create mode 100644 Analyser/Static/MSX/Target.hpp create mode 100644 OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib create mode 100644 OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift diff --git a/Analyser/Static/Acorn/Target.hpp b/Analyser/Static/Acorn/Target.hpp index c2f9f22b4..e7e97d467 100644 --- a/Analyser/Static/Acorn/Target.hpp +++ b/Analyser/Static/Acorn/Target.hpp @@ -6,8 +6,8 @@ // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef Target_h -#define Target_h +#ifndef Analyser_Static_Acorn_Target_h +#define Analyser_Static_Acorn_Target_h #include "../StaticAnalyser.hpp" @@ -25,4 +25,4 @@ struct Target: public ::Analyser::Static::Target { } } -#endif /* Target_h */ +#endif /* Analyser_Static_Acorn_Target_h */ diff --git a/Analyser/Static/AmstradCPC/Target.hpp b/Analyser/Static/AmstradCPC/Target.hpp index 795106435..ae656399e 100644 --- a/Analyser/Static/AmstradCPC/Target.hpp +++ b/Analyser/Static/AmstradCPC/Target.hpp @@ -6,8 +6,8 @@ // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef Target_h -#define Target_h +#ifndef Analyser_Static_AmstradCPC_Target_h +#define Analyser_Static_AmstradCPC_Target_h #include "../StaticAnalyser.hpp" @@ -30,4 +30,4 @@ struct Target: public ::Analyser::Static::Target { } -#endif /* Target_h */ +#endif /* Analyser_Static_AmstradCPC_Target_h */ diff --git a/Analyser/Static/Atari/Target.hpp b/Analyser/Static/Atari/Target.hpp index 9f0119ada..d102368cc 100644 --- a/Analyser/Static/Atari/Target.hpp +++ b/Analyser/Static/Atari/Target.hpp @@ -6,8 +6,8 @@ // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef Target_h -#define Target_h +#ifndef Analyser_Static_Atari_Target_h +#define Analyser_Static_Atari_Target_h #include "../StaticAnalyser.hpp" @@ -40,4 +40,4 @@ struct Target: public ::Analyser::Static::Target { } } -#endif /* Target_h */ +#endif /* Analyser_Static_Atari_Target_h */ diff --git a/Analyser/Static/Commodore/Target.hpp b/Analyser/Static/Commodore/Target.hpp index 35b4c3426..1dc54db88 100644 --- a/Analyser/Static/Commodore/Target.hpp +++ b/Analyser/Static/Commodore/Target.hpp @@ -6,8 +6,8 @@ // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef Target_h -#define Target_h +#ifndef Analyser_Static_Commodore_Target_h +#define Analyser_Static_Commodore_Target_h #include "../StaticAnalyser.hpp" @@ -39,4 +39,4 @@ struct Target: public ::Analyser::Static::Target { } } -#endif /* Target_h */ +#endif /* Analyser_Static_Commodore_Target_h */ diff --git a/Analyser/Static/MSX/StaticAnalyser.cpp b/Analyser/Static/MSX/StaticAnalyser.cpp index 26351bf2b..9b4e4377a 100644 --- a/Analyser/Static/MSX/StaticAnalyser.cpp +++ b/Analyser/Static/MSX/StaticAnalyser.cpp @@ -10,6 +10,8 @@ #include "Cartridge.hpp" #include "Tape.hpp" +#include "Target.hpp" + #include "../Disassembler/Z80.hpp" #include "../Disassembler/AddressMapper.hpp" @@ -32,7 +34,7 @@ static std::unique_ptr CartridgeTarget( output_segments.emplace_back(start_address, segment.data); } - std::unique_ptr target(new Analyser::Static::Target); + std::unique_ptr target(new Analyser::Static::MSX::Target); target->machine = Analyser::Machine::MSX; target->confidence = confidence; @@ -259,9 +261,9 @@ static std::vector> CartridgeTargetsFr return targets; } -void Analyser::Static::MSX::AddTargets(const Media &media, std::vector> &destination) { +void Analyser::Static::MSX::AddTargets(const Media &media, std::vector> &destination) { // Append targets for any cartridges that look correct. - std::vector> cartridge_targets = CartridgeTargetsFrom(media.cartridges); + auto cartridge_targets = CartridgeTargetsFrom(media.cartridges); std::move(cartridge_targets.begin(), cartridge_targets.end(), std::back_inserter(destination)); // Consider building a target for disks and/or tapes. @@ -283,6 +285,7 @@ void Analyser::Static::MSX::AddTargets(const Media &media, std::vectormedia.disks = media.disks; + target->has_disk_drive = !media.disks.empty(); if(!target->media.empty()) { target->machine = Machine::MSX; diff --git a/Analyser/Static/MSX/Target.hpp b/Analyser/Static/MSX/Target.hpp new file mode 100644 index 000000000..6c6c2cea7 --- /dev/null +++ b/Analyser/Static/MSX/Target.hpp @@ -0,0 +1,26 @@ +// +// Target.hpp +// Clock Signal +// +// Created by Thomas Harte on 02/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef Analyser_Static_MSX_Target_h +#define Analyser_Static_MSX_Target_h + +#include "../StaticAnalyser.hpp" + +namespace Analyser { +namespace Static { +namespace MSX { + +struct Target: public ::Analyser::Static::Target { + bool has_disk_drive = false; +}; + +} +} +} + +#endif /* Analyser_Static_MSX_Target_h */ diff --git a/Analyser/Static/Oric/StaticAnalyser.cpp b/Analyser/Static/Oric/StaticAnalyser.cpp index 7f7391e96..e53aa6463 100644 --- a/Analyser/Static/Oric/StaticAnalyser.cpp +++ b/Analyser/Static/Oric/StaticAnalyser.cpp @@ -134,17 +134,17 @@ void Analyser::Static::Oric::AddTargets(const Media &media, std::vectorhas_microdisc = true; + target->has_microdrive = true; target->media.disks.push_back(disk); } } } else { - target->has_microdisc = false; + target->has_microdrive = false; } // TODO: really this should add two targets if not all votes agree target->use_atmos_rom = basic11_votes >= basic10_votes; - if(target->has_microdisc) target->use_atmos_rom = true; + if(target->has_microdrive) target->use_atmos_rom = true; if(target->media.tapes.size() || target->media.disks.size() || target->media.cartridges.size()) destination.push_back(std::move(target)); diff --git a/Analyser/Static/Oric/Target.hpp b/Analyser/Static/Oric/Target.hpp index f920d7e5a..4bd5bb16e 100644 --- a/Analyser/Static/Oric/Target.hpp +++ b/Analyser/Static/Oric/Target.hpp @@ -6,8 +6,8 @@ // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef Target_h -#define Target_h +#ifndef Analyser_Static_Oric_Target_h +#define Analyser_Static_Oric_Target_h namespace Analyser { namespace Static { @@ -15,11 +15,11 @@ namespace Oric { struct Target: public ::Analyser::Static::Target { bool use_atmos_rom = false; - bool has_microdisc = false; + bool has_microdrive = false; }; } } } -#endif /* Target_h */ +#endif /* Analyser_Static_Oric_Target_h */ diff --git a/Analyser/Static/ZX8081/StaticAnalyser.cpp b/Analyser/Static/ZX8081/StaticAnalyser.cpp index 4ee957325..a405bf8bf 100644 --- a/Analyser/Static/ZX8081/StaticAnalyser.cpp +++ b/Analyser/Static/ZX8081/StaticAnalyser.cpp @@ -40,11 +40,11 @@ void Analyser::Static::ZX8081::AddTargets(const Media &media, std::vectorisZX81 = files.front().isZX81; + target->is_ZX81 = files.front().isZX81; break; - case TargetPlatform::ZX80: target->isZX81 = false; break; - case TargetPlatform::ZX81: target->isZX81 = true; break; + case TargetPlatform::ZX80: target->is_ZX81 = false; break; + case TargetPlatform::ZX81: target->is_ZX81 = true; break; } /*if(files.front().data.size() > 16384) { @@ -57,7 +57,7 @@ void Analyser::Static::ZX8081::AddTargets(const Media &media, std::vectormedia.tapes = media.tapes; // TODO: how to run software once loaded? Might require a BASIC detokeniser. - if(target->isZX81) { + if(target->is_ZX81) { target->loading_command = "J\"\"\n"; } else { target->loading_command = "W\n"; diff --git a/Analyser/Static/ZX8081/Target.hpp b/Analyser/Static/ZX8081/Target.hpp index 7fef01e08..5f40e0bdf 100644 --- a/Analyser/Static/ZX8081/Target.hpp +++ b/Analyser/Static/ZX8081/Target.hpp @@ -6,8 +6,8 @@ // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef Target_h -#define Target_h +#ifndef Analyser_Static_ZX8081_Target_h +#define Analyser_Static_ZX8081_Target_h #include "../StaticAnalyser.hpp" @@ -23,11 +23,12 @@ struct Target: public ::Analyser::Static::Target { }; MemoryModel memory_model = MemoryModel::Unexpanded; - bool isZX81 = false; + bool is_ZX81 = false; + bool ZX80_uses_ZX81_ROM = false; }; } } } -#endif /* Target_h */ +#endif /* Analyser_Static_ZX8081_Target_h */ diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 87fea0628..3502c8161 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -40,6 +40,8 @@ #include "../../Configurable/StandardOptions.hpp" #include "../../ClockReceiver/ForceInline.hpp" +#include "../../Analyser/Static/MSX/Target.hpp" + namespace MSX { std::vector> get_options() { @@ -183,8 +185,10 @@ class ConcreteMachine: } void configure_as_target(const Analyser::Static::Target *target) override { + auto *const msx_target = dynamic_cast(target); + // Add a disk cartridge if any disks were supplied. - if(!target->media.disks.empty()) { + if(msx_target->has_disk_drive) { map(2, 0, 0x4000, 0x2000); unmap(2, 0x6000, 0x2000); memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source)); diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index afd2dc1da..5215badf4 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -271,7 +271,7 @@ class ConcreteMachine: void configure_as_target(const Analyser::Static::Target *target) override final { auto *const oric_target = dynamic_cast(target); - if(oric_target->has_microdisc) { + if(oric_target->has_microdrive) { microdisc_is_enabled_ = true; microdisc_did_change_paging_flags(µdisc_); microdisc_.set_delegate(this); diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index ec30e8ebc..7d17bd357 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -285,7 +285,7 @@ template class ConcreteMachine: void configure_as_target(const Analyser::Static::Target *target) override final { auto *const zx8081_target = dynamic_cast(target); - is_zx81_ = zx8081_target->isZX81; + is_zx81_ = zx8081_target->is_ZX81; if(is_zx81_) { rom_ = zx81_rom_; tape_trap_address_ = 0x37c; @@ -295,7 +295,7 @@ template class ConcreteMachine: automatic_tape_motor_start_address_ = 0x0340; automatic_tape_motor_end_address_ = 0x03c3; } else { - rom_ = zx80_rom_; + rom_ = zx8081_target->ZX80_uses_ZX81_ROM ? zx81_rom_ : zx80_rom_; tape_trap_address_ = 0x220; tape_return_address_ = 0x248; vsync_start_ = HalfCycles(26); @@ -528,7 +528,7 @@ Machine *Machine::ZX8081(const Analyser::Static::Target *target_hint) { const Analyser::Static::ZX8081::Target *const hint = dynamic_cast(target_hint); // Instantiate the correct type of machine. - if(hint->isZX81) + if(hint->is_ZX81) return new ZX8081::ConcreteMachine(); else return new ZX8081::ConcreteMachine(); diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 202c24e47..ef546f057 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -291,6 +291,8 @@ 4B9BE400203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */; }; 4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */; }; 4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; }; + 4BA141BD2072E8A500A31EC9 /* MachinePicker.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BA141BB2072E8A400A31EC9 /* MachinePicker.xib */; }; + 4BA141BF2072E8AF00A31EC9 /* MachinePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA141BE2072E8AF00A31EC9 /* MachinePicker.swift */; }; 4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; }; 4BAD13441FF709C700FD114A /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; }; 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */; }; @@ -975,6 +977,9 @@ 4B9BE3FF203A0C0600FFAE60 /* MultiSpeaker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiSpeaker.hpp; sourceTree = ""; }; 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Data/ZX8081.cpp; sourceTree = ""; }; 4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Data/ZX8081.hpp; sourceTree = ""; }; + 4BA141BC2072E8A400A31EC9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MachinePicker.xib; sourceTree = ""; }; + 4BA141BE2072E8AF00A31EC9 /* MachinePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachinePicker.swift; sourceTree = ""; }; + 4BA141C12073100800A31EC9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; 4BA61EAE1D91515900B3C876 /* NSData+StdVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+StdVector.h"; sourceTree = ""; }; 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSData+StdVector.mm"; sourceTree = ""; }; 4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = ""; }; @@ -2235,6 +2240,7 @@ 4B047075201ABC180047AB0D /* Cartridge.hpp */, 4B894510201967B4007DE474 /* StaticAnalyser.hpp */, 4B894511201967B4007DE474 /* Tape.hpp */, + 4BA141C12073100800A31EC9 /* Target.hpp */, ); path = MSX; sourceTree = ""; @@ -2259,6 +2265,16 @@ path = Implementation; sourceTree = ""; }; + 4BA141C02072E8B300A31EC9 /* MachinePicker */ = { + isa = PBXGroup; + children = ( + 4BA141BE2072E8AF00A31EC9 /* MachinePicker.swift */, + 4BA141BB2072E8A400A31EC9 /* MachinePicker.xib */, + ); + name = MachinePicker; + path = "New Group"; + sourceTree = ""; + }; 4BAB62AA1D3272D200DF5BA0 /* Disk */ = { isa = PBXGroup; children = ( @@ -2622,11 +2638,12 @@ 4BB73EAD1B587A5100552FC2 /* Info.plist */, 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */, 4BB73EA81B587A5100552FC2 /* Assets.xcassets */, - 4BB73EAA1B587A5100552FC2 /* MainMenu.xib */, 4B2A538F1D117D36003C6002 /* Audio */, 4B643F3D1D77B88000D431D6 /* Document Controller */, 4B55CE551C3B7D360093A61B /* Documents */, 4B2A53921D117D36003C6002 /* Machine */, + 4BA141C02072E8B300A31EC9 /* MachinePicker */, + 4BB73EAA1B587A5100552FC2 /* MainMenu.xib */, 4BE5F85A1C3E1C2500C43F01 /* Resources */, 4BD5F1961D1352A000631CD1 /* Updater */, 4B55CE5A1C3B7D6F0093A61B /* Views */, @@ -3114,6 +3131,7 @@ 4B2C45421E3C3896002A2389 /* cartridge.png in Resources */, 4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */, 4B79E4451E3AF38600141F11 /* floppy35.png in Resources */, + 4BA141BD2072E8A500A31EC9 /* MachinePicker.xib in Resources */, 4B1EDB451E39A0AC009D6819 /* chip.png in Resources */, 4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */, 4BD61664206B2AC800236112 /* QuickLoadOptions.xib in Resources */, @@ -3650,6 +3668,7 @@ 4B71368E1F788112008B8ED9 /* Parser.cpp in Sources */, 4B12C0ED1FCFA98D005BFD93 /* Keyboard.cpp in Sources */, 4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */, + 4BA141BF2072E8AF00A31EC9 /* MachinePicker.swift in Sources */, 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */, 4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */, 4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */, @@ -3829,6 +3848,14 @@ name = QuickLoadCompositeOptions.xib; sourceTree = ""; }; + 4BA141BB2072E8A400A31EC9 /* MachinePicker.xib */ = { + isa = PBXVariantGroup; + children = ( + 4BA141BC2072E8A400A31EC9 /* Base */, + ); + name = MachinePicker.xib; + sourceTree = ""; + }; 4BB73EAA1B587A5100552FC2 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( diff --git a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift index 01ec875bb..62e221294 100644 --- a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift +++ b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift @@ -9,4 +9,20 @@ import Cocoa class DocumentController: NSDocumentController { + override func newDocument(_ sender: Any?) { + // Show the new document window. + Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil) + } + + @IBOutlet var machinePicker: MachinePicker? + @IBAction func createMachine(_ sender: NSButton?) { + let machine = machinePicker!.selectedMachine() + let document: MachineDocument = try! openUntitledDocumentAndDisplay(true) as! MachineDocument + document.configureAs(machine) + sender?.window?.close() + } + + @IBAction func cancelCreateMachine(_ sender: NSButton?) { + sender?.window?.close() + } } diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index f2e72b86a..defa25a86 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -48,30 +48,34 @@ class MachineDocument: override func windowControllerDidLoadNib(_ aController: NSWindowController) { super.windowControllerDidLoadNib(aController) + aController.window?.contentAspectRatio = self.aspectRatio() + setupMachineOutput() + } - // establish the output aspect ratio and audio - let displayAspectRatio = self.aspectRatio() - aController.window?.contentAspectRatio = displayAspectRatio - openGLView.perform(glContext: { - self.machine.setView(self.openGLView, aspectRatio: Float(displayAspectRatio.width / displayAspectRatio.height)) - }) + fileprivate func setupMachineOutput() { + if let machine = self.machine, let openGLView = self.openGLView { + // establish the output aspect ratio and audio + let aspectRatio = self.aspectRatio() + openGLView.perform(glContext: { + machine.setView(openGLView, aspectRatio: Float(aspectRatio.width / aspectRatio.height)) + }) - self.machine.delegate = self - self.bestEffortUpdater = CSBestEffortUpdater() + machine.delegate = self + self.bestEffortUpdater = CSBestEffortUpdater() - // callbacks from the OpenGL may come on a different thread, immediately following the .delegate set; - // hence the full setup of the best-effort updater prior to setting self as a delegate - self.openGLView.delegate = self - self.openGLView.responderDelegate = self + // callbacks from the OpenGL may come on a different thread, immediately following the .delegate set; + // hence the full setup of the best-effort updater prior to setting self as a delegate + openGLView.delegate = self + openGLView.responderDelegate = self - setupAudioQueueClockRate() - self.optionsPanel?.establishStoredOptions() + setupAudioQueueClockRate() - // bring OpenGL view-holding window on top of the options panel - self.openGLView.window!.makeKeyAndOrderFront(self) + // bring OpenGL view-holding window on top of the options panel + openGLView.window!.makeKeyAndOrderFront(self) - // start accepting best effort updates - self.bestEffortUpdater!.delegate = self + // start accepting best effort updates + self.bestEffortUpdater!.delegate = self + } } func machineSpeakerDidChangeInputClock(_ machine: CSMachine!) { @@ -95,9 +99,11 @@ class MachineDocument: optionsPanel = nil bestEffortLock.lock() - bestEffortUpdater!.delegate = nil - bestEffortUpdater!.flush() - bestEffortUpdater = nil + if let bestEffortUpdater = bestEffortUpdater { + bestEffortUpdater.delegate = nil + bestEffortUpdater.flush() + self.bestEffortUpdater = nil + } bestEffortLock.unlock() actionLock.lock() @@ -115,12 +121,14 @@ class MachineDocument: func configureAs(_ analysis: CSStaticAnalyser) { if let machine = CSMachine(analyser: analysis) { self.machine = machine - } + setupMachineOutput() - if let optionsPanelNibName = analysis.optionsPanelNibName { - Bundle.main.loadNibNamed(NSNib.Name(rawValue: optionsPanelNibName), owner: self, topLevelObjects: nil) - self.optionsPanel.machine = self.machine - showOptions(self) + if let optionsPanelNibName = analysis.optionsPanelNibName { + Bundle.main.loadNibNamed(NSNib.Name(rawValue: optionsPanelNibName), owner: self, topLevelObjects: nil) + self.optionsPanel.machine = self.machine + self.optionsPanel?.establishStoredOptions() + showOptions(self) + } } } @@ -185,7 +193,7 @@ class MachineDocument: // MARK: Input management func windowDidResignKey(_ notification: Notification) { - self.machine.clearAllKeys() +// self.machine.clearAllKeys() } func keyDown(_ event: NSEvent) { diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h index 47457adeb..17082cff3 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -10,10 +10,39 @@ @class CSMachine; +typedef NS_ENUM(NSInteger, CSMachineCPCModel) { + CSMachineCPCModel464, + CSMachineCPCModel664, + CSMachineCPCModel6128 +}; + +typedef NS_ENUM(NSInteger, CSMachineOricModel) { + CSMachineOricModelOric1, + CSMachineOricModelOricAtmos +}; + +typedef NS_ENUM(NSInteger, CSMachineVic20Region) { + CSMachineVic20RegionAmerican, + CSMachineVic20RegionEuropean, + CSMachineVic20RegionDanish, + CSMachineVic20RegionSwedish, + CSMachineVic20RegionJapanese, +}; + +typedef int Kilobytes; + @interface CSStaticAnalyser : NSObject - (instancetype)initWithFileAtURL:(NSURL *)url; +- (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs; +- (instancetype)initWithAmstradCPCModel:(CSMachineCPCModel)model; +- (instancetype)initWithMSXHasDiskDrive:(BOOL)hasDiskDrive; +- (instancetype)initWithOricModel:(CSMachineOricModel)model hasMicrodrive:(BOOL)hasMicrodrive; +- (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540; +- (instancetype)initWithZX80MemorySize:(Kilobytes)memorySize useZX81ROM:(BOOL)useZX81ROM; +- (instancetype)initWithZX81MemorySize:(Kilobytes)memorySize; + @property(nonatomic, readonly) NSString *optionsPanelNibName; @property(nonatomic, readonly) NSString *displayName; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index b1849f8f1..de2b46097 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -13,6 +13,13 @@ #include "StaticAnalyser.hpp" +#include "../../../../../Analyser/Static/Acorn/Target.hpp" +#include "../../../../../Analyser/Static/AmstradCPC/Target.hpp" +#include "../../../../../Analyser/Static/Commodore/Target.hpp" +#include "../../../../../Analyser/Static/MSX/Target.hpp" +#include "../../../../../Analyser/Static/Oric/Target.hpp" +#include "../../../../../Analyser/Static/ZX8081/Target.hpp" + #import "Clock_Signal-Swift.h" @implementation CSStaticAnalyser { @@ -32,6 +39,120 @@ return self; } +- (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs { + self = [super init]; + if(self) { + using Target = Analyser::Static::Acorn::Target; + std::unique_ptr target(new Target); + target->machine = Analyser::Machine::Electron; + target->has_dfs = !!dfs; + target->has_adfs = !!adfs; + _targets.push_back(std::move(target)); + } + return self; +} + +- (instancetype)initWithAmstradCPCModel:(CSMachineCPCModel)model { + self = [super init]; + if(self) { + using Target = Analyser::Static::AmstradCPC::Target; + std::unique_ptr target(new Target); + target->machine = Analyser::Machine::AmstradCPC; + switch(model) { + case CSMachineCPCModel464: target->model = Analyser::Static::AmstradCPC::Target::Model::CPC464; break; + case CSMachineCPCModel664: target->model = Analyser::Static::AmstradCPC::Target::Model::CPC664; break; + case CSMachineCPCModel6128: target->model = Analyser::Static::AmstradCPC::Target::Model::CPC6128; break; + } + _targets.push_back(std::move(target)); + } + return self; +} + +- (instancetype)initWithMSXHasDiskDrive:(BOOL)hasDiskDrive { + self = [super init]; + if(self) { + using Target = Analyser::Static::MSX::Target; + std::unique_ptr target(new Target); + target->machine = Analyser::Machine::MSX; + target->has_disk_drive = !!hasDiskDrive; + _targets.push_back(std::move(target)); + } + return self; +} + +- (instancetype)initWithOricModel:(CSMachineOricModel)model hasMicrodrive:(BOOL)hasMicrodrive { + self = [super init]; + if(self) { + using Target = Analyser::Static::Oric::Target; + std::unique_ptr target(new Target); + target->machine = Analyser::Machine::Oric; + target->use_atmos_rom = (model == CSMachineOricModelOricAtmos); + target->has_microdrive = !!hasMicrodrive; + _targets.push_back(std::move(target)); + } + return self; +} + +- (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540 { + self = [super init]; + if(self) { + using Target = Analyser::Static::Commodore::Target; + std::unique_ptr target(new Target); + target->machine = Analyser::Machine::Vic20; + switch(region) { + case CSMachineVic20RegionDanish: target->region = Target::Region::Danish; break; + case CSMachineVic20RegionSwedish: target->region = Target::Region::Swedish; break; + case CSMachineVic20RegionAmerican: target->region = Target::Region::American; break; + case CSMachineVic20RegionEuropean: target->region = Target::Region::European; break; + case CSMachineVic20RegionJapanese: target->region = Target::Region::Japanese; break; + } + switch(memorySize) { + default: target->memory_model = Target::MemoryModel::Unexpanded; break; + case 8: target->memory_model = Target::MemoryModel::EightKB; break; + case 32: target->memory_model = Target::MemoryModel::ThirtyTwoKB; break; + } + target->has_c1540 = !!hasC1540; + _targets.push_back(std::move(target)); + } + return self; +} + +static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(Kilobytes size) { + using MemoryModel = Analyser::Static::ZX8081::Target::MemoryModel; + switch(size) { + default: return MemoryModel::Unexpanded; + case 16: return MemoryModel::SixteenKB; + case 64: return MemoryModel::SixtyFourKB; + } +} + +- (instancetype)initWithZX80MemorySize:(Kilobytes)memorySize useZX81ROM:(BOOL)useZX81ROM { + self = [super init]; + if(self) { + using Target = Analyser::Static::ZX8081::Target; + std::unique_ptr target(new Target); + target->machine = Analyser::Machine::ZX8081; + target->is_ZX81 = false; + target->ZX80_uses_ZX81_ROM = !!useZX81ROM; + target->memory_model = ZX8081MemoryModelFromSize(memorySize); + _targets.push_back(std::move(target)); + } + return self; +} + +- (instancetype)initWithZX81MemorySize:(Kilobytes)memorySize { + self = [super init]; + if(self) { + using Target = Analyser::Static::ZX8081::Target; + std::unique_ptr target(new Target); + target->machine = Analyser::Machine::ZX8081; + target->is_ZX81 = true; + target->memory_model = ZX8081MemoryModelFromSize(memorySize); + _targets.push_back(std::move(target)); + } + return self; +} + - (NSString *)optionsPanelNibName { switch(_targets.front()->machine) { case Analyser::Machine::AmstradCPC: return nil; diff --git a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib new file mode 100644 index 000000000..b3841fa84 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift b/OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift new file mode 100644 index 000000000..b133ea980 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift @@ -0,0 +1,90 @@ +// +// MachinePicker.swift +// Clock Signal +// +// Created by Thomas Harte on 02/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +import Cocoa + +class MachinePicker: NSObject { + @IBOutlet var machineSelector: NSTabView? + + // MARK: - Electron properties + @IBOutlet var electronDFSButton: NSButton? + @IBOutlet var electronADFSButton: NSButton? + + // MARK: - CPC properties + @IBOutlet var cpcModelTypeButton: NSPopUpButton? + + // MARK: - MSX properties + @IBOutlet var msxHasDiskDriveButton: NSButton? + + // MARK: - Oric properties + @IBOutlet var oricModelTypeButton: NSPopUpButton? + @IBOutlet var oricHasMicrodriveButton: NSButton? + + // MARK: - Vic-20 properties + @IBOutlet var vic20RegionButton: NSPopUpButton? + @IBOutlet var vic20MemorySizeButton: NSPopUpButton? + @IBOutlet var vic20HasC1540Button: NSButton? + + // MARK: - ZX80 properties + @IBOutlet var zx80MemorySizeButton: NSPopUpButton? + @IBOutlet var zx80UsesZX81ROMButton: NSButton? + + // MARK: - ZX81 properties + @IBOutlet var zx81MemorySizeButton: NSPopUpButton? + + // MARK: - Machine builder + func selectedMachine() -> CSStaticAnalyser { + switch machineSelector!.selectedTabViewItem!.identifier as! String { + case "electron": + return CSStaticAnalyser(electronDFS: electronDFSButton!.state == .on, adfs: electronADFSButton!.state == .on)! + + case "cpc": + switch cpcModelTypeButton!.selectedItem!.tag { + case 464: return CSStaticAnalyser(amstradCPCModel: .model464) + case 664: return CSStaticAnalyser(amstradCPCModel: .model664) + case 6128: fallthrough + default: return CSStaticAnalyser(amstradCPCModel: .model6128) + } + + case "msx": + return CSStaticAnalyser(msxHasDiskDrive: msxHasDiskDriveButton!.state == .on) + + case "oric": + let hasMicrodrive = oricHasMicrodriveButton!.state == .on + switch oricModelTypeButton!.selectedItem!.tag { + case 1: return CSStaticAnalyser(oricModel: .oric1, hasMicrodrive: hasMicrodrive) + default: return CSStaticAnalyser(oricModel: .oricAtmos, hasMicrodrive: hasMicrodrive) + } + + case "vic20": + let memorySize = Kilobytes(vic20MemorySizeButton!.selectedItem!.tag) + let hasC1540 = vic20HasC1540Button!.state == .on + switch vic20RegionButton!.selectedItem?.tag { + case 1: + return CSStaticAnalyser(vic20Region: .american, memorySize: memorySize, hasC1540: hasC1540) + case 2: + return CSStaticAnalyser(vic20Region: .danish, memorySize: memorySize, hasC1540: hasC1540) + case 3: + return CSStaticAnalyser(vic20Region: .swedish, memorySize: memorySize, hasC1540: hasC1540) + case 4: + return CSStaticAnalyser(vic20Region: .japanese, memorySize: memorySize, hasC1540: hasC1540) + case 0: fallthrough + default: + return CSStaticAnalyser(vic20Region: .european, memorySize: memorySize, hasC1540: hasC1540) + } + + case "zx80": + return CSStaticAnalyser(zx80MemorySize: Kilobytes(zx80MemorySizeButton!.selectedItem!.tag), useZX81ROM: zx80UsesZX81ROMButton!.state == .on) + + case "zx81": + return CSStaticAnalyser(zx81MemorySize: Kilobytes(zx81MemorySizeButton!.selectedItem!.tag)) + + default: return CSStaticAnalyser() + } + } +} From cde2faeda6fd6d732f4d7ebf5ed97cf0220082d6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 2 Apr 2018 23:31:36 -0400 Subject: [PATCH 2/8] Makes an unsuccessful attempt to show the new machine dialogue as a sheet. Also corrects the 'open' case versus recent changes. --- .../Base.lproj/MachineDocument.xib | 8 ++- .../DocumentController.swift | 32 ++++----- .../Documents/MachineDocument.swift | 65 +++++++++++++++---- .../New Group/Base.lproj/MachinePicker.xib | 33 +++++++--- 4 files changed, 96 insertions(+), 42 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib index 5dd259d78..60249a6b3 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib @@ -1,10 +1,12 @@ - + - + + + - + diff --git a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift index 62e221294..78c1c53da 100644 --- a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift +++ b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift @@ -9,20 +9,20 @@ import Cocoa class DocumentController: NSDocumentController { - override func newDocument(_ sender: Any?) { - // Show the new document window. - Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil) - } - - @IBOutlet var machinePicker: MachinePicker? - @IBAction func createMachine(_ sender: NSButton?) { - let machine = machinePicker!.selectedMachine() - let document: MachineDocument = try! openUntitledDocumentAndDisplay(true) as! MachineDocument - document.configureAs(machine) - sender?.window?.close() - } - - @IBAction func cancelCreateMachine(_ sender: NSButton?) { - sender?.window?.close() - } +// override func newDocument(_ sender: Any?) { +// // Show the new document window. +// Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil) +// } +// +// @IBOutlet var machinePicker: MachinePicker? +// @IBAction func createMachine(_ sender: NSButton?) { +// let machine = machinePicker!.selectedMachine() +// let document: MachineDocument = try! openUntitledDocumentAndDisplay(true) as! MachineDocument +// document.configureAs(machine) +// sender?.window?.close() +// } +// +// @IBAction func cancelCreateMachine(_ sender: NSButton?) { +// sender?.window?.close() +// } } diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index defa25a86..2322a876d 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -28,6 +28,7 @@ class MachineDocument: return nil } } + var optionsPanelNibName: String? func aspectRatio() -> NSSize { return NSSize(width: 4.0, height: 3.0) @@ -50,6 +51,14 @@ class MachineDocument: super.windowControllerDidLoadNib(aController) aController.window?.contentAspectRatio = self.aspectRatio() setupMachineOutput() + + // If there's no machine but the NIB has loaded, show the new machine dialogue + if self.machine == nil { + Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil) + aController.window?.beginSheet(self.machinePickerPanel!, completionHandler: { (response) in + Swift.print("\(response)") + }) + } } fileprivate func setupMachineOutput() { @@ -60,6 +69,14 @@ class MachineDocument: machine.setView(openGLView, aspectRatio: Float(aspectRatio.width / aspectRatio.height)) }) + // attach an options panel if one is available + if let optionsPanelNibName = self.optionsPanelNibName { + Bundle.main.loadNibNamed(NSNib.Name(rawValue: optionsPanelNibName), owner: self, topLevelObjects: nil) + self.optionsPanel.machine = machine + self.optionsPanel?.establishStoredOptions() + showOptions(self) + } + machine.delegate = self self.bestEffortUpdater = CSBestEffortUpdater() @@ -121,14 +138,8 @@ class MachineDocument: func configureAs(_ analysis: CSStaticAnalyser) { if let machine = CSMachine(analyser: analysis) { self.machine = machine + self.optionsPanelNibName = analysis.optionsPanelNibName setupMachineOutput() - - if let optionsPanelNibName = analysis.optionsPanelNibName { - Bundle.main.loadNibNamed(NSNib.Name(rawValue: optionsPanelNibName), owner: self, topLevelObjects: nil) - self.optionsPanel.machine = self.machine - self.optionsPanel?.establishStoredOptions() - showOptions(self) - } } } @@ -141,6 +152,11 @@ class MachineDocument: } } + convenience init(type typeName: String) throws { + self.init() + self.fileType = typeName + } + // MARK: the pasteboard func paste(_ sender: AnyObject!) { let pasteboard = NSPasteboard.general @@ -193,21 +209,42 @@ class MachineDocument: // MARK: Input management func windowDidResignKey(_ notification: Notification) { -// self.machine.clearAllKeys() + if let machine = self.machine { + machine.clearAllKeys() + } } func keyDown(_ event: NSEvent) { - self.machine.setKey(event.keyCode, characters: event.characters, isPressed: true) + if let machine = self.machine { + machine.setKey(event.keyCode, characters: event.characters, isPressed: true) + } } func keyUp(_ event: NSEvent) { - self.machine.setKey(event.keyCode, characters: event.characters, isPressed: false) + if let machine = self.machine { + machine.setKey(event.keyCode, characters: event.characters, isPressed: false) + } } func flagsChanged(_ newModifiers: NSEvent) { - self.machine.setKey(VK_Shift, characters: nil, isPressed: newModifiers.modifierFlags.contains(.shift)) - self.machine.setKey(VK_Control, characters: nil, isPressed: newModifiers.modifierFlags.contains(.control)) - self.machine.setKey(VK_Command, characters: nil, isPressed: newModifiers.modifierFlags.contains(.command)) - self.machine.setKey(VK_Option, characters: nil, isPressed: newModifiers.modifierFlags.contains(.option)) + if let machine = self.machine { + machine.setKey(VK_Shift, characters: nil, isPressed: newModifiers.modifierFlags.contains(.shift)) + machine.setKey(VK_Control, characters: nil, isPressed: newModifiers.modifierFlags.contains(.control)) + machine.setKey(VK_Command, characters: nil, isPressed: newModifiers.modifierFlags.contains(.command)) + machine.setKey(VK_Option, characters: nil, isPressed: newModifiers.modifierFlags.contains(.option)) + } + } + + // MARK: New machine creation + @IBOutlet var machinePicker: MachinePicker? + @IBOutlet var machinePickerPanel: NSPanel? + @IBAction func createMachine(_ sender: NSButton?) { + self.configureAs(machinePicker!.selectedMachine()) + machinePicker = nil + sender?.window?.close() + } + + @IBAction func cancelCreateMachine(_ sender: NSButton?) { + close() } } diff --git a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib index b3841fa84..3841f759e 100644 --- a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib +++ b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib @@ -6,15 +6,16 @@ - + + - - + + @@ -62,7 +63,7 @@ - + @@ -76,7 +77,7 @@ - + @@ -84,14 +85,28 @@ + + + + + + + + + + + + + + - - + + - - + + From 7dac791290d8015ed95cfd5a4262b7cca378a7e3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Apr 2018 18:47:07 -0400 Subject: [PATCH 3/8] Causes the machine picker to show as a sheet. Albeit with some user experience issues lingering. --- .../Base.lproj/MachineDocument.xib | 2 +- .../Documents/MachineDocument.swift | 21 +++++++++----- .../New Group/Base.lproj/MachinePicker.xib | 28 +++++-------------- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib index 60249a6b3..5525b968c 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib @@ -25,7 +25,7 @@ - + diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 2322a876d..7ee64df33 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -52,12 +52,16 @@ class MachineDocument: aController.window?.contentAspectRatio = self.aspectRatio() setupMachineOutput() - // If there's no machine but the NIB has loaded, show the new machine dialogue - if self.machine == nil { + } + + // Attempting to show a sheet before the window is visible (such as when the NIB is loaded) results in + // a sheet mysteriously floating on its own. For now, use windowDidUpdate as a proxy to know that the window + // is visible, though it's a little premature. + func windowDidUpdate(_ notification: Notification) { + if self.shouldShowNewMachinePanel { + self.shouldShowNewMachinePanel = false Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil) - aController.window?.beginSheet(self.machinePickerPanel!, completionHandler: { (response) in - Swift.print("\(response)") - }) + self.windowControllers[0].window?.beginSheet(self.machinePickerPanel!, completionHandler: nil) } } @@ -87,8 +91,9 @@ class MachineDocument: setupAudioQueueClockRate() - // bring OpenGL view-holding window on top of the options panel + // bring OpenGL view-holding window on top of the options panel and show the content openGLView.window!.makeKeyAndOrderFront(self) + openGLView.isHidden = false // start accepting best effort updates self.bestEffortUpdater!.delegate = self @@ -143,6 +148,7 @@ class MachineDocument: } } + fileprivate var shouldShowNewMachinePanel = false override func read(from url: URL, ofType typeName: String) throws { if let analyser = CSStaticAnalyser(fileAt: url) { self.displayName = analyser.displayName @@ -155,6 +161,7 @@ class MachineDocument: convenience init(type typeName: String) throws { self.init() self.fileType = typeName + self.shouldShowNewMachinePanel = true } // MARK: the pasteboard @@ -237,7 +244,7 @@ class MachineDocument: // MARK: New machine creation @IBOutlet var machinePicker: MachinePicker? - @IBOutlet var machinePickerPanel: NSPanel? + @IBOutlet var machinePickerPanel: NSWindow? @IBAction func createMachine(_ sender: NSButton?) { self.configureAs(machinePicker!.selectedMachine()) machinePicker = nil diff --git a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib index 3841f759e..c89b145f0 100644 --- a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib +++ b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib @@ -14,7 +14,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -85,28 +85,14 @@ - - - - - - - - - - - - - - - - + + + + - - From c697dd78f071f10814ec37617996a8e04676d457 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Apr 2018 22:22:39 -0400 Subject: [PATCH 4/8] Ensures a new machine starts as first responder. --- OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 7ee64df33..ee9fda911 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -92,8 +92,9 @@ class MachineDocument: setupAudioQueueClockRate() // bring OpenGL view-holding window on top of the options panel and show the content - openGLView.window!.makeKeyAndOrderFront(self) openGLView.isHidden = false + openGLView.window!.makeKeyAndOrderFront(self) + openGLView.window!.makeFirstResponder(openGLView) // start accepting best effort updates self.bestEffortUpdater!.delegate = self From 3ffa9e275157272686f368fd515c299b78eb4588 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Apr 2018 23:01:12 -0400 Subject: [PATCH 5/8] Ensures complete machine picker state is preserved. --- .../Documents/MachineDocument.swift | 1 + .../New Group/MachinePicker.swift | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index ee9fda911..9444ff2c7 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -61,6 +61,7 @@ class MachineDocument: if self.shouldShowNewMachinePanel { self.shouldShowNewMachinePanel = false Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil) + self.machinePicker?.establishStoredOptions() self.windowControllers[0].window?.beginSheet(self.machinePickerPanel!, completionHandler: nil) } } diff --git a/OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift b/OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift index b133ea980..d20f0ffc4 100644 --- a/OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift +++ b/OSBindings/Mac/Clock Signal/New Group/MachinePicker.swift @@ -37,8 +37,79 @@ class MachinePicker: NSObject { // MARK: - ZX81 properties @IBOutlet var zx81MemorySizeButton: NSPopUpButton? + // MARK: - Preferences + func establishStoredOptions() { + let standardUserDefaults = UserDefaults.standard + + // Machine type + if let machineIdentifier = standardUserDefaults.string(forKey: "new.machine") { + machineSelector?.selectTabViewItem(withIdentifier: machineIdentifier as Any) + } + + // Electron settings + electronDFSButton?.state = standardUserDefaults.bool(forKey: "new.electronDFS") ? .on : .off + electronADFSButton?.state = standardUserDefaults.bool(forKey: "new.electronADFS") ? .on : .off + + // CPC settings + cpcModelTypeButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.cpcModel")) + + // MSX settings + msxHasDiskDriveButton?.state = standardUserDefaults.bool(forKey: "new.msxDiskDrive") ? .on : .off + + // Oric settings + oricHasMicrodriveButton?.state = standardUserDefaults.bool(forKey: "new.oricMicrodrive") ? .on : .off + oricModelTypeButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.oricModel")) + + // Vic-20 settings + vic20RegionButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.vic20Region")) + vic20MemorySizeButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.vic20MemorySize")) + vic20HasC1540Button?.state = standardUserDefaults.bool(forKey: "new.vic20C1540") ? .on : .off + + // ZX80 + zx80MemorySizeButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.zx80MemorySize")) + zx80UsesZX81ROMButton?.state = standardUserDefaults.bool(forKey: "new.zx80UsesZX81ROM") ? .on : .off + + // ZX81 + zx81MemorySizeButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.zx81MemorySize")) + } + + fileprivate func storeOptions() { + let standardUserDefaults = UserDefaults.standard + + // Machine type + standardUserDefaults.set(machineSelector!.selectedTabViewItem!.identifier as! String, forKey: "new.machine") + + // Electron settings + standardUserDefaults.set(electronDFSButton!.state == .on, forKey: "new.electronDFS") + standardUserDefaults.set(electronADFSButton!.state == .on, forKey: "new.electronADFS") + + // CPC settings + standardUserDefaults.set(cpcModelTypeButton!.selectedTag(), forKey: "new.cpcModel") + + // MSX settings + standardUserDefaults.set(msxHasDiskDriveButton?.state == .on, forKey: "new.msxDiskDrive") + + // Oric settings + standardUserDefaults.set(oricHasMicrodriveButton?.state == .on, forKey: "new.oricMicrodrive") + standardUserDefaults.set(oricModelTypeButton!.selectedTag(), forKey: "new.oricModel") + + // Vic-20 settings + standardUserDefaults.set(vic20RegionButton!.selectedTag(), forKey: "new.vic20Region") + standardUserDefaults.set(vic20MemorySizeButton!.selectedTag(), forKey: "new.vic20MemorySize") + standardUserDefaults.set(vic20HasC1540Button?.state == .on, forKey: "new.vic20C1540") + + // ZX80 + standardUserDefaults.set(zx80MemorySizeButton!.selectedTag(), forKey: "new.zx80MemorySize") + standardUserDefaults.set(zx80UsesZX81ROMButton?.state == .on, forKey: "new.zx80UsesZX81ROM") + + // ZX81 + standardUserDefaults.set(zx81MemorySizeButton!.selectedTag(), forKey: "new.zx81MemorySize") + } + // MARK: - Machine builder func selectedMachine() -> CSStaticAnalyser { + storeOptions() + switch machineSelector!.selectedTabViewItem!.identifier as! String { case "electron": return CSStaticAnalyser(electronDFS: electronDFSButton!.state == .on, adfs: electronADFSButton!.state == .on)! From d3418550eb905364e24b66aa41d4cd30f632f93d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Apr 2018 23:06:48 -0400 Subject: [PATCH 6/8] Attempts explicitly to disable promise of saving. --- .../Mac/Clock Signal/Base.lproj/MainMenu.xib | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib index 5c4f1b32f..9a56125ec 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib @@ -1,7 +1,8 @@ - - + + - + + @@ -93,30 +94,30 @@ - + - + - + - + - + @@ -128,23 +129,23 @@ - + - + - + - + @@ -154,19 +155,19 @@ - + - + - + From eaf148218215658ac9529107b1278a238de32759 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Apr 2018 23:11:19 -0400 Subject: [PATCH 7/8] Reverts the once-again-unused document controller. --- .../Document Controller/DocumentController.swift | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift index 78c1c53da..01ec875bb 100644 --- a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift +++ b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift @@ -9,20 +9,4 @@ import Cocoa class DocumentController: NSDocumentController { -// override func newDocument(_ sender: Any?) { -// // Show the new document window. -// Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil) -// } -// -// @IBOutlet var machinePicker: MachinePicker? -// @IBAction func createMachine(_ sender: NSButton?) { -// let machine = machinePicker!.selectedMachine() -// let document: MachineDocument = try! openUntitledDocumentAndDisplay(true) as! MachineDocument -// document.configureAs(machine) -// sender?.window?.close() -// } -// -// @IBAction func cancelCreateMachine(_ sender: NSButton?) { -// sender?.window?.close() -// } } From 46b1c57bf4e086796b8d003a63d0a8f109369aa7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 Apr 2018 23:22:26 -0400 Subject: [PATCH 8/8] Enables the titlebar, inexplicably allowing the sheet to obtain focus. --- .../New Group/Base.lproj/MachinePicker.xib | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib index c89b145f0..4c687ad94 100644 --- a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib +++ b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib @@ -15,16 +15,16 @@ - + - + - + - + @@ -365,7 +365,7 @@ Gw - +