diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 20539cc..eb2d59d 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -7,7 +7,6 @@ on:
env:
name: "epple2"
desc: "Apple ][ emulator"
- deb_depends: "libsdl2-2.0-0"
jobs:
"build-roms":
@@ -24,7 +23,7 @@ jobs:
- name: "Build"
run: |
set -x
- cd rom
+ cd share/Resources/rom
mkdir build
cd build
cmake ..
@@ -33,174 +32,234 @@ jobs:
- uses: "actions/upload-artifact@v3"
with:
name: "epple2-roms"
- path: "./rom/*.a65"
+ path: "share/Resources/rom/*.a65"
- "build-epple2-ubuntu":
- needs: "build-roms"
- runs-on: "ubuntu-latest"
+ "build":
+ strategy:
+ matrix:
+ include:
+ - os: "ubuntu-22.04"
+ shell: "bash"
+ - os: "macos-12"
+ shell: "bash"
+ - os: "windows-latest"
+ shell: "msys2 {0}"
+
+ runs-on: "${{ matrix.os }}"
+
+ defaults:
+ run:
+ shell: "${{ matrix.shell }}"
+
steps:
- - name: "Prepare runner"
- run: |
- set -x
- sudo apt-get update -qqqq
- sudo apt-get install -qqqq libsdl2-dev tree
-
- uses: "actions/checkout@v3"
- - name: "Build"
- run: |
- set -x
- mkdir build
- cd build
- cmake ..
- cmake --build .
+ - uses: "msys2/setup-msys2@v2"
+ if: ${{ runner.os == 'Windows' }}
+ with:
+ update: true
+ pacboy: >-
+ toolchain:p
+ cmake:p
+ ninja:p
+ boost:p
+ wxwidgets3.2-msw:p
+ SDL2:p
+
+ - name: "Set up environment"
+ run: "./bin/${{ runner.os }}/setup.sh"
- uses: "actions/download-artifact@v3"
with:
name: "epple2-roms"
- path: "build/epple2-roms"
-
- - name: "Stage"
- run: |
- set -x
- pwd
- cd build
- mkdir -p stage/usr/local/bin
- mkdir -p stage/usr/local/etc/epple2
- mkdir -p stage/usr/local/lib/epple2/system
- mkdir -p stage/usr/local/lib/epple2/cards
- cp src/epple2 stage/usr/local/bin/
- cp conf/*.conf stage/usr/local/etc/epple2/
- cp epple2-roms/epple2sys.a65 stage/usr/local/lib/epple2/system/
- cp epple2-roms/stdout.a65 stage/usr/local/lib/epple2/cards/
- cp epple2-roms/stdin.a65 stage/usr/local/lib/epple2/cards/
- cp epple2-roms/clock.a65 stage/usr/local/lib/epple2/cards/
- tree stage
-
- - name: "Package"
- uses: "jiro4989/build-deb-action@v2"
- with:
- package: "${{ env.name }}"
- desc: "${{ env.desc }}"
- maintainer: "${{ github.repository_owner }}"
- version: "${{ github.ref }}"
- arch: "amd64"
- package_root: "build/stage"
- depends: "${{ env.deb_depends }}"
-
- - uses: "softprops/action-gh-release@v1"
- with:
- files: |
- *.deb
-
-
-
- "build-epple2-macos":
- needs: "build-roms"
- runs-on: "macos-latest"
- steps:
- - name: "Prepare runner"
- run: |
- set -x
- HOMEBREW_NO_AUTO_UPDATE=1 brew install sdl2 tree
-
- - uses: "actions/checkout@v3"
+ path: "share/Resources/rom"
- name: "Build"
- run: |
- set -x
- mkdir build
- cd build
- cmake ..
- cmake --build .
-
- - uses: "actions/download-artifact@v3"
- with:
- name: "epple2-roms"
- path: "build/epple2-roms"
-
- - name: "Stage"
- run: |
- set -x
- pwd
- cd build
- mkdir -p stage/local/bin
- mkdir -p stage/local/etc/epple2
- mkdir -p stage/local/lib/epple2/system
- mkdir -p stage/local/lib/epple2/cards
- cp src/epple2 stage/local/bin/
- cp conf/*.conf stage/local/etc/epple2/
- cp epple2-roms/epple2sys.a65 stage/local/lib/epple2/system/
- cp epple2-roms/stdout.a65 stage/local/lib/epple2/cards/
- cp epple2-roms/stdin.a65 stage/local/lib/epple2/cards/
- cp epple2-roms/clock.a65 stage/local/lib/epple2/cards/
- cp /usr/local/lib/libSDL2.dylib stage/local/bin/
- tree stage
-
- - name: "Package"
- run: |
- set -x
- pkgbuild --identifier nu.mine.mosher.$name --root build/stage --install-location /usr $name.pkg
+ run: "./bin/build.sh"
- uses: "softprops/action-gh-release@v1"
with:
+ draft: true
+ prerelease: true
+ tag_name: "${{ env.git_tag }}"
files: |
- *.pkg
+ tmp/cpack/*.zip
+ tmp/cpack/*.tar.gz
+ tmp/cpack/*.7z
+ tmp/cpack/*.deb
+ tmp/cpack/*.dmg
+ tmp/cpack/*.exe
-
-
- "build-epple2-windows":
- needs: "build-roms"
- runs-on: "windows-latest"
- steps:
- - name: "Prepare runner"
- run: |
- Invoke-WebRequest -Uri "https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-devel-2.24.2-VC.zip" -OutFile "C:/Program Files/SDL2-devel-VC.zip"
- Expand-Archive -LiteralPath "C:/Program Files/SDL2-devel-VC.zip" -DestinationPath "C:/Program Files/"
-
- - uses: "actions/checkout@v3"
-
- - name: "Build"
- shell: "bash"
- run: |
- set -x
- mkdir build
- cd build
- cmake ..
- cmake --build .
-
- - uses: "actions/download-artifact@v3"
- with:
- name: "epple2-roms"
- path: "build/epple2-roms"
-
- - name: "Stage"
- shell: "bash"
- run: |
- set -x
- pwd
- cd build
- mkdir -p stage/epple2/etc/epple2
- mkdir -p stage/epple2/lib/epple2/system
- mkdir -p stage/epple2/lib/epple2/cards
- cp src/Debug/epple2.exe stage/epple2/
- cp conf/*.conf stage/epple2/etc/epple2/
- cp epple2-roms/epple2sys.a65 stage/epple2/lib/epple2/system/
- cp epple2-roms/stdout.a65 stage/epple2/lib/epple2/cards/
- cp epple2-roms/stdin.a65 stage/epple2/lib/epple2/cards/
- cp epple2-roms/clock.a65 stage/epple2/lib/epple2/cards/
- cp "C:/Program Files/SDL2-2.24.2/lib/x64/SDL2.dll" stage/epple2/
-
- - name: "Package"
- uses: "thedoctor0/zip-release@main"
- with:
- directory: "build/stage"
- path: "${{ env.name }}"
- filename: "${{ env.name }}-windows.zip"
-
- - uses: "softprops/action-gh-release@v1"
- with:
- files: |
- build/stage/*.zip
+
+
+
+
+# "build-epple2-ubuntu":
+# needs: "build-roms"
+# runs-on: "ubuntu-latest"
+# steps:
+# - name: "Prepare runner"
+# run: |
+# set -x
+# sudo apt-get update -qqqq
+# sudo apt-get install -qqqq libsdl2-dev tree
+#
+# - uses: "actions/checkout@v3"
+#
+# - name: "Build"
+# run: |
+# set -x
+# mkdir build
+# cd build
+# cmake ..
+# cmake --build .
+#
+# - uses: "actions/download-artifact@v3"
+# with:
+# name: "epple2-roms"
+# path: "build/epple2-roms"
+#
+# - name: "Stage"
+# run: |
+# set -x
+# pwd
+# cd build
+# mkdir -p stage/usr/local/bin
+# mkdir -p stage/usr/local/etc/epple2
+# mkdir -p stage/usr/local/lib/epple2/system
+# mkdir -p stage/usr/local/lib/epple2/cards
+# cp src/epple2 stage/usr/local/bin/
+# cp conf/*.conf stage/usr/local/etc/epple2/
+# cp epple2-roms/epple2sys.a65 stage/usr/local/lib/epple2/system/
+# cp epple2-roms/stdout.a65 stage/usr/local/lib/epple2/cards/
+# cp epple2-roms/stdin.a65 stage/usr/local/lib/epple2/cards/
+# cp epple2-roms/clock.a65 stage/usr/local/lib/epple2/cards/
+# tree stage
+#
+# - name: "Package"
+# uses: "jiro4989/build-deb-action@v2"
+# with:
+# package: "${{ env.name }}"
+# desc: "${{ env.desc }}"
+# maintainer: "${{ github.repository_owner }}"
+# version: "${{ github.ref }}"
+# arch: "amd64"
+# package_root: "build/stage"
+# depends: "${{ env.deb_depends }}"
+#
+# - uses: "softprops/action-gh-release@v1"
+# with:
+# files: |
+# *.deb
+#
+#
+#
+# "build-epple2-macos":
+# needs: "build-roms"
+# runs-on: "macos-latest"
+# steps:
+# - name: "Prepare runner"
+# run: |
+# set -x
+# HOMEBREW_NO_AUTO_UPDATE=1 brew install sdl2 tree
+#
+# - uses: "actions/checkout@v3"
+#
+# - name: "Build"
+# run: |
+# set -x
+# mkdir build
+# cd build
+# cmake ..
+# cmake --build .
+#
+# - uses: "actions/download-artifact@v3"
+# with:
+# name: "epple2-roms"
+# path: "build/epple2-roms"
+#
+# - name: "Stage"
+# run: |
+# set -x
+# pwd
+# cd build
+# mkdir -p stage/local/bin
+# mkdir -p stage/local/etc/epple2
+# mkdir -p stage/local/lib/epple2/system
+# mkdir -p stage/local/lib/epple2/cards
+# cp src/epple2 stage/local/bin/
+# cp conf/*.conf stage/local/etc/epple2/
+# cp epple2-roms/epple2sys.a65 stage/local/lib/epple2/system/
+# cp epple2-roms/stdout.a65 stage/local/lib/epple2/cards/
+# cp epple2-roms/stdin.a65 stage/local/lib/epple2/cards/
+# cp epple2-roms/clock.a65 stage/local/lib/epple2/cards/
+# cp /usr/local/lib/libSDL2.dylib stage/local/bin/
+# tree stage
+#
+# - name: "Package"
+# run: |
+# set -x
+# pkgbuild --identifier nu.mine.mosher.$name --root build/stage --install-location /usr $name.pkg
+#
+# - uses: "softprops/action-gh-release@v1"
+# with:
+# files: |
+# *.pkg
+#
+#
+#
+# "build-epple2-windows":
+# needs: "build-roms"
+# runs-on: "windows-latest"
+# steps:
+# - name: "Prepare runner"
+# run: |
+# Invoke-WebRequest -Uri "https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-devel-2.24.2-VC.zip" -OutFile "C:/Program Files/SDL2-devel-VC.zip"
+# Expand-Archive -LiteralPath "C:/Program Files/SDL2-devel-VC.zip" -DestinationPath "C:/Program Files/"
+#
+# - uses: "actions/checkout@v3"
+#
+# - name: "Build"
+# shell: "bash"
+# run: |
+# set -x
+# mkdir build
+# cd build
+# cmake ..
+# cmake --build .
+#
+# - uses: "actions/download-artifact@v3"
+# with:
+# name: "epple2-roms"
+# path: "build/epple2-roms"
+#
+# - name: "Stage"
+# shell: "bash"
+# run: |
+# set -x
+# pwd
+# cd build
+# mkdir -p stage/epple2/etc/epple2
+# mkdir -p stage/epple2/lib/epple2/system
+# mkdir -p stage/epple2/lib/epple2/cards
+# cp src/Debug/epple2.exe stage/epple2/
+# cp conf/*.conf stage/epple2/etc/epple2/
+# cp epple2-roms/epple2sys.a65 stage/epple2/lib/epple2/system/
+# cp epple2-roms/stdout.a65 stage/epple2/lib/epple2/cards/
+# cp epple2-roms/stdin.a65 stage/epple2/lib/epple2/cards/
+# cp epple2-roms/clock.a65 stage/epple2/lib/epple2/cards/
+# cp "C:/Program Files/SDL2-2.24.2/lib/x64/SDL2.dll" stage/epple2/
+#
+# - name: "Package"
+# uses: "thedoctor0/zip-release@main"
+# with:
+# directory: "build/stage"
+# path: "${{ env.name }}"
+# filename: "${{ env.name }}-windows.zip"
+#
+# - uses: "softprops/action-gh-release@v1"
+# with:
+# files: |
+# build/stage/*.zip
diff --git a/.gitignore b/.gitignore
index bfb82e2..70015a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# cmake
/build/
+/tmp/
# netbeans
/nbproject/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0b9e8c..734eb64 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,10 +3,13 @@ cmake_minimum_required(VERSION 3.22.1)
set(CMAKE_VERBOSE_MAKEFILE TRUE CACHE BOOL "Echo make commands.")
set(PACKAGE_SUITE TRUE CACHE BOOL "Build entire suite of packages; otherwise just ZIP file.")
-#set(CMAKE_BUILD_TYPE RelWithDebInfo)
-set(CMAKE_BUILD_TYPE Debug)
+set(CMAKE_BUILD_TYPE RelWithDebInfo)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
+
+
+
+
set(APP_NAME epple2)
project(${APP_NAME}
@@ -100,6 +103,7 @@ paddlebuttonstates.cpp
paddles.cpp
picturegenerator.cpp
powerupreset.cpp
+PreferencesDialog.cpp
screenimage.cpp
SegmentCache.cpp
slots.cpp
@@ -123,10 +127,24 @@ wozfile.cpp
list(TRANSFORM sources PREPEND "src/")
set(resources
-#epple2.xrc
-#epple2.png
+epple2.xrc
+epple2.png
conf/epple2.conf
+conf/epple2.rev0bare.conf
+conf/epple2.a2bare.conf
+conf/epple2.a2dos31.conf
+conf/epple2.a2dos33.conf
+conf/epple2.a2loaded.conf
+conf/epple2.a2pbare.conf
+conf/epple2.a2pdos33.conf
+conf/epple2.a2ploaded.conf
+conf/epple2.visual6502.conf
+rom/epple2sys.a65
+rom/stdout.a65
+rom/clock.a65
+rom/stdin.a65
)
+list(TRANSFORM resources PREPEND "share/Resources/")
add_executable(${APP_NAME} WIN32 MACOSX_BUNDLE ${sources} ${resources})
@@ -158,6 +176,12 @@ target_link_libraries(${APP_NAME} PRIVATE ${wxWidgets_LIBRARIES})
+configure_file(src/config.h.in config.h)
+include_directories(${PROJECT_BINARY_DIR})
+
+
+
+# TODO: can we remove this without being too backwardly incompatible?
target_compile_definitions(${APP_NAME} PRIVATE ETCDIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SYSCONFDIR}")
@@ -165,3 +189,53 @@ target_compile_definitions(${APP_NAME} PRIVATE ETCDIR="${CMAKE_INSTALL_PREFIX}/$
set_target_properties(${APP_NAME} PROPERTIES RESOURCE "${resources}")
+
+
+
+
+
+include(InstallRequiredSystemLibraries)
+
+if(APPLE)
+ set_target_properties(${APP_NAME} PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST ${PROJECT_BINARY_DIR}/Info.plist)
+ install(TARGETS ${APP_NAME}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ BUNDLE DESTINATION .)
+ configure_file(share/FixBundle.cmake.in ${PROJECT_BINARY_DIR}/FixBundle.cmake @ONLY)
+ install(SCRIPT ${PROJECT_BINARY_DIR}/FixBundle.cmake)
+elseif(WIN32)
+ install(TARGETS ${APP_NAME}
+ RUNTIME_DEPENDENCIES
+ DIRECTORIES ${CMAKE_SYSTEM_LIBRARY_PATH}
+ PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
+ POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
+ BUNDLE DESTINATION .
+ RESOURCE DESTINATION share/Resources)
+else()
+ install(TARGETS ${APP_NAME}
+ RUNTIME_DEPENDENCIES
+ DIRECTORIES ${CMAKE_SYSTEM_LIBRARY_PATH}
+ RESOURCE DESTINATION share/Resources)
+endif()
+
+
+
+
+
+set(CPACK_GENERATOR ZIP)
+
+if(PACKAGE_SUITE)
+ set(CPACK_GENERATOR ${CPACK_GENERATOR} TGZ 7Z)
+ if(APPLE)
+ set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-macOS")
+ configure_file(share/Info.plist.in ${PROJECT_BINARY_DIR}/Info.plist)
+ set(CPACK_GENERATOR ${CPACK_GENERATOR} DragNDrop)
+ elseif(WIN32)
+ set(CPACK_GENERATOR ${CPACK_GENERATOR} NSIS)
+ else()
+ set(CPACK_GENERATOR ${CPACK_GENERATOR} DEB)
+ endif()
+endif()
+
+include(CPack)
diff --git a/bin/Linux/setup.sh b/bin/Linux/setup.sh
new file mode 100755
index 0000000..fcf5d6e
--- /dev/null
+++ b/bin/Linux/setup.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+echo "::group::configure kinetic"
+sudo add-apt-repository "deb http://mirror.math.princeton.edu/pub/ubuntu kinetic main universe"
+echo "::endgroup::"
+
+echo "::group::install dependencies"
+sudo apt-get -qqqq update
+sudo apt-get -qqqq install libboost-all-dev libwxgtk3.2-dev libsdl2-dev
+echo "::endgroup::"
diff --git a/bin/Windows/setup.sh b/bin/Windows/setup.sh
new file mode 100755
index 0000000..c52d3c2
--- /dev/null
+++ b/bin/Windows/setup.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exit 0
diff --git a/bin/build.sh b/bin/build.sh
new file mode 100755
index 0000000..bcf5d92
--- /dev/null
+++ b/bin/build.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -e
+
+if [ -e "$1/CMakeCache.txt" ] ; then
+ builddir="$1"
+else
+ mkdir -p tmp
+ builddir=$(mktemp -d tmp/tmp.XXXXXXXXXX)
+fi
+cd $builddir
+
+# TODO: fix version number
+if [ "$GITHUB_ENV" != "" ] ; then
+ echo "git_tag=2.0.0-$(date +%Y%m%d.%H%M%S)" >>$GITHUB_ENV
+fi
+
+echo "::group::cmake"
+cmake ../..
+echo "::endgroup::"
+
+echo "::group::cmake --build"
+cmake --build .
+echo "::endgroup::"
+
+if [ "$1" = "-n" ] ; then
+ exit 0
+fi
+
+echo "::group::cpack"
+cpack -B ../cpack/
+echo "::endgroup::"
diff --git a/bin/macOS/setup.sh b/bin/macOS/setup.sh
new file mode 100755
index 0000000..d4c1422
--- /dev/null
+++ b/bin/macOS/setup.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+echo "::group::install dependencies"
+export HOMEBREW_NO_AUTO_UPDATE=1
+brew install boost wxwidgets sdl2
+echo "::endgroup::"
diff --git a/share/FixBundle.cmake.in b/share/FixBundle.cmake.in
new file mode 100644
index 0000000..8f35844
--- /dev/null
+++ b/share/FixBundle.cmake.in
@@ -0,0 +1,16 @@
+include(BundleUtilities)
+
+# Set bundle to the full path name of the executable already
+# existing in the install tree:
+set(bundle "${CMAKE_INSTALL_PREFIX}/@APP_NAME@.app")
+
+# Set other_libs to a list of full path names to additional
+# libraries that cannot be reached by dependency analysis.
+# (Dynamically loaded PlugIns, for example.)
+set(other_libs "")
+
+# Set dirs to a list of directories where prerequisite libraries
+# may be found:
+set(dirs "@CMAKE_RUNTIME_OUTPUT_DIRECTORY@" "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@")
+
+fixup_bundle("${bundle}" "${other_libs}" "${dirs}")
diff --git a/share/Info.plist.in b/share/Info.plist.in
new file mode 100644
index 0000000..96e5477
--- /dev/null
+++ b/share/Info.plist.in
@@ -0,0 +1,22 @@
+
+
+
+
+ CFBundleName
+ @CMAKE_PROJECT_NAME@
+ CFBundleDisplayName
+ @CMAKE_PROJECT_NAME@
+ CFBundleIdentifier
+ @CPACK_PACKAGE_VENDOR@.@CMAKE_PROJECT_NAME@
+ CFBundleVersion
+ @PROJECT_VERSION@
+ CFBundlePackageType
+ APPL
+ CFBundleSignature
+ @PROJECT_FOURCC@
+ CFBundleExecutable
+ @CMAKE_PROJECT_NAME@
+ CFBundleIconFile
+ @CMAKE_PROJECT_NAME@
+
+
diff --git a/conf/epple2.a2bare.conf b/share/Resources/conf/epple2.a2bare.conf
similarity index 100%
rename from conf/epple2.a2bare.conf
rename to share/Resources/conf/epple2.a2bare.conf
diff --git a/conf/epple2.a2dos31.conf b/share/Resources/conf/epple2.a2dos31.conf
similarity index 100%
rename from conf/epple2.a2dos31.conf
rename to share/Resources/conf/epple2.a2dos31.conf
diff --git a/conf/epple2.a2dos33.conf b/share/Resources/conf/epple2.a2dos33.conf
similarity index 100%
rename from conf/epple2.a2dos33.conf
rename to share/Resources/conf/epple2.a2dos33.conf
diff --git a/conf/epple2.a2loaded.conf b/share/Resources/conf/epple2.a2loaded.conf
similarity index 100%
rename from conf/epple2.a2loaded.conf
rename to share/Resources/conf/epple2.a2loaded.conf
diff --git a/conf/epple2.a2pbare.conf b/share/Resources/conf/epple2.a2pbare.conf
similarity index 100%
rename from conf/epple2.a2pbare.conf
rename to share/Resources/conf/epple2.a2pbare.conf
diff --git a/conf/epple2.a2pdos33.conf b/share/Resources/conf/epple2.a2pdos33.conf
similarity index 100%
rename from conf/epple2.a2pdos33.conf
rename to share/Resources/conf/epple2.a2pdos33.conf
diff --git a/conf/epple2.a2ploaded.conf b/share/Resources/conf/epple2.a2ploaded.conf
similarity index 100%
rename from conf/epple2.a2ploaded.conf
rename to share/Resources/conf/epple2.a2ploaded.conf
diff --git a/conf/epple2.conf b/share/Resources/conf/epple2.conf
similarity index 100%
rename from conf/epple2.conf
rename to share/Resources/conf/epple2.conf
diff --git a/conf/epple2.rev0bare.conf b/share/Resources/conf/epple2.rev0bare.conf
similarity index 100%
rename from conf/epple2.rev0bare.conf
rename to share/Resources/conf/epple2.rev0bare.conf
diff --git a/conf/epple2.visual6502.conf b/share/Resources/conf/epple2.visual6502.conf
similarity index 100%
rename from conf/epple2.visual6502.conf
rename to share/Resources/conf/epple2.visual6502.conf
diff --git a/share/Resources/epple2.fbp b/share/Resources/epple2.fbp
new file mode 100644
index 0000000..0ad801f
--- /dev/null
+++ b/share/Resources/epple2.fbp
@@ -0,0 +1,879 @@
+
+
+
+
+
diff --git a/share/Resources/epple2.png b/share/Resources/epple2.png
new file mode 100644
index 0000000..09fc86c
Binary files /dev/null and b/share/Resources/epple2.png differ
diff --git a/share/Resources/epple2.xrc b/share/Resources/epple2.xrc
new file mode 100644
index 0000000..9134b60
--- /dev/null
+++ b/share/Resources/epple2.xrc
@@ -0,0 +1,181 @@
+
+
+
+
+ 1024,768
+ Preferences
+ 1
+
+ wxVERTICAL
+
+
+ wxALL|wxEXPAND
+ 10
+
+ wxVERTICAL
+
+
+ wxEXPAND
+ 5
+
+
+ 0
+ 0.25
+ 0
+ vertical
+
+
+
+ wxVERTICAL
+
+
+ wxALL|wxEXPAND
+ 5
+
+
+
+
+
+
+
+
+
+ wxVERTICAL
+
+
+ wxEXPAND
+ 5
+
+ wxVERTICAL
+
+
+ wxEXPAND
+ 0
+
+ wxHORIZONTAL
+
+
+ wxALIGN_CENTER_VERTICAL|wxLEFT
+ 5
+
+
+ 0
+
+
+
+
+ wxEXPAND
+ 5
+ 0,0
+
+
+
+ wxRIGHT
+ 25
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+ wxBOTTOM|wxEXPAND|wxLEFT
+ 5
+
+ wxHORIZONTAL
+
+
+
+ wxLEFT|wxTOP
+ 7
+
+
+
+ -1
+
+
+
+
+ wxBOTTOM|wxEXPAND|wxRIGHT
+ 5
+
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+ wxBOTTOM|wxEXPAND|wxTOP
+ 5
+
+ wxHORIZONTAL
+
+
+ wxEXPAND
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+ wxEXPAND
+ 0
+
+ wxHORIZONTAL
+
+
+ wxALIGN_BOTTOM
+ 0
+ 0,0
+
+
+
+ wxALIGN_BOTTOM|wxALL
+ 0
+
+
+ 1
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
diff --git a/rom/.gitignore b/share/Resources/rom/.gitignore
similarity index 100%
rename from rom/.gitignore
rename to share/Resources/rom/.gitignore
diff --git a/rom/CMakeLists.txt b/share/Resources/rom/CMakeLists.txt
similarity index 100%
rename from rom/CMakeLists.txt
rename to share/Resources/rom/CMakeLists.txt
diff --git a/rom/Makefile b/share/Resources/rom/Makefile
similarity index 100%
rename from rom/Makefile
rename to share/Resources/rom/Makefile
diff --git a/rom/asm.m4h b/share/Resources/rom/asm.m4h
similarity index 100%
rename from rom/asm.m4h
rename to share/Resources/rom/asm.m4h
diff --git a/rom/clock.m4 b/share/Resources/rom/clock.m4
similarity index 100%
rename from rom/clock.m4
rename to share/Resources/rom/clock.m4
diff --git a/rom/epple2sys.m4 b/share/Resources/rom/epple2sys.m4
similarity index 100%
rename from rom/epple2sys.m4
rename to share/Resources/rom/epple2sys.m4
diff --git a/rom/stdin.m4 b/share/Resources/rom/stdin.m4
similarity index 100%
rename from rom/stdin.m4
rename to share/Resources/rom/stdin.m4
diff --git a/rom/stdout.m4 b/share/Resources/rom/stdout.m4
similarity index 100%
rename from rom/stdout.m4
rename to share/Resources/rom/stdout.m4
diff --git a/src/E2wxFrame.cpp b/src/E2wxFrame.cpp
index 34d523d..bb755ca 100644
--- a/src/E2wxFrame.cpp
+++ b/src/E2wxFrame.cpp
@@ -20,7 +20,7 @@
#include "E2wxFrame.h"
#include "E2wxApp.h"
-//#include "PreferencesDialog.h"
+#include "PreferencesDialog.h"
#include
@@ -90,7 +90,7 @@ void E2wxFrame::OnAbout(wxCommandEvent& event) {
}
void E2wxFrame::OnPreferences(wxCommandEvent& event) {
-// PreferencesDialog *dlg = new PreferencesDialog(this);
-// dlg->OnInit();
-// dlg->ShowModal();
+ PreferencesDialog *dlg = new PreferencesDialog(this);
+ dlg->OnInit();
+ dlg->ShowModal();
}
diff --git a/src/PreferencesDialog.cpp b/src/PreferencesDialog.cpp
new file mode 100644
index 0000000..c2b9e2e
--- /dev/null
+++ b/src/PreferencesDialog.cpp
@@ -0,0 +1,335 @@
+#include
+#ifndef WX_PRECOMP
+ #include
+#endif
+
+#include "PreferencesDialog.h"
+#include "E2wxApp.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define CTRL(t,n) t* n = XRCCTRL(*this, #n, t)
+
+static const wxSize& SIZ_DLG = wxSize(1024,768);
+
+
+
+wxBEGIN_EVENT_TABLE(PreferencesDialog, wxDialog)
+ EVT_CLOSE(PreferencesDialog::OnClose)
+ EVT_BUTTON(wxID_OK, PreferencesDialog::OnCloseButton)
+ EVT_TREE_SEL_CHANGED(XRCID("treItems"), PreferencesDialog::OnTreeSelectionChanged)
+ EVT_CHECKBOX(XRCID("chkActive"), PreferencesDialog::OnActive)
+ EVT_BUTTON(XRCID("btnDuplicate"), PreferencesDialog::OnDuplicate)
+ EVT_BUTTON(XRCID("btnDelete"), PreferencesDialog::OnDelete)
+ EVT_BUTTON(XRCID("btnRename"), PreferencesDialog::OnRename)
+wxEND_EVENT_TABLE()
+
+
+
+
+
+
+
+class TreeItemData : public wxTreeItemData {
+ const std::filesystem::path *m_path;
+ const bool m_editable;
+public:
+ TreeItemData() : m_path(NULL), m_editable(false) {}
+ TreeItemData(const std::filesystem::path& path, bool editable) :
+ m_path(new std::filesystem::path(path)),
+ m_editable(editable) {
+ }
+ virtual bool isFile() const { return true; }
+ bool isEditable() const { return m_editable; }
+ const std::filesystem::path path() const { return *m_path; }
+};
+
+class EmptyTreeItem : public TreeItemData {
+ virtual bool isFile() const { return false; }
+};
+
+
+
+
+class TreeSink : public wxDirTraverser {
+public:
+ TreeSink(wxTreeCtrl *tree, wxTreeItemId parent, const std::filesystem::path& dir, bool editable) :
+ m_tree(tree), m_parent(parent), m_dir(dir), m_editable(editable) {
+ }
+
+ wxDirTraverseResult OnFile(const wxString& filename) {
+ wxFileName n = wxFileName::FileName(filename);
+ if (n.GetExt() == "conf") {
+ const std::filesystem::path& full = std::filesystem::path(n.GetFullName().t_str());
+ const std::filesystem::path path = m_dir / full;
+ m_tree->AppendItem(m_parent, n.GetName(), -1, -1, new TreeItemData(path, m_editable));
+ }
+ return wxDIR_CONTINUE;
+ }
+
+ wxDirTraverseResult OnDir(const wxString& dirname) {
+ return wxDIR_CONTINUE;
+ }
+private:
+ wxTreeCtrl *m_tree;
+ const wxTreeItemId m_parent;
+ const std::filesystem::path& m_dir;
+ const bool m_editable;
+};
+
+
+
+
+PreferencesDialog::PreferencesDialog(wxWindow* parent) : parent(parent) {
+}
+
+PreferencesDialog::~PreferencesDialog() {
+}
+
+void PreferencesDialog::OnClose(wxCloseEvent& event) {
+ CTRL(wxTreeCtrl, treItems);
+ const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
+ if (data->isFile()) {
+ if (data->isEditable()) {
+ Save(data->path());
+ }
+ }
+ EndModal(wxID_OK);
+}
+
+static void fillDir(wxTreeCtrl *treItems, wxTreeItemId item, const std::filesystem::path& dir, bool editable = false) {
+ TreeSink sink(treItems, item, dir, editable);
+ wxDir dirBuiltIn = wxDir(dir.c_str());
+ dirBuiltIn.Traverse(sink, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN);
+}
+
+void PreferencesDialog::BuildItemTree() {
+ CTRL(wxTreeCtrl, treItems);
+
+ treItems->DeleteAllItems();
+
+ wxTreeItemId configs = treItems->AddRoot(wxT("configurations"), -1, -1, new EmptyTreeItem());
+
+ wxTreeItemId user = treItems->AppendItem(configs, wxT("user"), -1, -1, new EmptyTreeItem());
+ fillDir(treItems, user, wxGetApp().GetConfigDir(), true);
+ treItems->SortChildren(user);
+
+ wxTreeItemId built_in = treItems->AppendItem(configs, wxT("built-in"), -1, -1, new EmptyTreeItem());
+ fillDir(treItems, built_in, wxGetApp().GetResDir());
+ treItems->SortChildren(built_in);
+
+ treItems->ExpandAll();
+
+ this->GetSizer()->Layout();
+}
+
+void PreferencesDialog::OnInit() {
+ wxConfigBase *appconf = wxConfigBase::Get();
+ if (!appconf->Read(wxT("/ActivePreferences/name"), &this->active)) {
+ // TODO what to do when no config?
+ this->active = ".template";
+ appconf->Write(wxT("/ActivePreferences/name"), this->active);
+ appconf->Flush();
+ }
+
+ wxXmlResource::Get()->LoadDialog(this, this->parent, "Preferences");
+
+ SetSize(SIZ_DLG);
+
+ BuildItemTree();
+
+ CTRL(wxTreeCtrl, treItems);
+ treItems->SetFocus();
+ treItems->SelectItem(treItems->GetRootItem());
+}
+
+void PreferencesDialog::Save(const std::filesystem::path& to) {
+ CTRL(wxTextCtrl, txtConfig);
+ const wxString sConfig = txtConfig->GetValue();
+ if (sConfig != this->sOrigConfig) {
+ std::ofstream out(to);
+ out << sConfig;
+ this->sOrigConfig = sConfig;
+ }
+}
+
+void PreferencesDialog::OnTreeSelectionChanged(wxTreeEvent& evt) {
+ // note: we don't get the first select upon dialog creation,
+ // nor the final de-select upon dialog destruction
+
+ CTRL(wxTreeCtrl, treItems);
+ CTRL(wxStaticText, txtName);
+ CTRL(wxTextCtrl, txtConfig);
+ CTRL(wxCheckBox, chkActive);
+
+ const TreeItemData *dataOld = (TreeItemData*)treItems->GetItemData(evt.GetOldItem());
+ wxString pathOld = "(not a file)";
+ if (dataOld->isFile()) {
+ pathOld = dataOld->path().c_str();
+ Save(dataOld->path());
+ }
+
+ const TreeItemData *dataNew = (TreeItemData*)treItems->GetItemData(evt.GetItem());
+ wxString pathNew = "(not a file)";
+ if (dataNew->isFile()) {
+ pathNew = dataNew->path().c_str();
+ std::ifstream t(dataNew->path());
+ std::stringstream buffer;
+ buffer << t.rdbuf();
+ this->sOrigConfig = buffer.str();
+ txtConfig->SetValue(this->sOrigConfig);
+ wxString name = wxFileName::FileName(dataNew->path().c_str()).GetName();
+ txtName->SetLabel(name);
+ chkActive->SetValue(name == this->active);
+ } else {
+ this->sOrigConfig = wxEmptyString;
+ txtConfig->SetValue(this->sOrigConfig);
+ txtName->SetLabel(wxEmptyString);
+ chkActive->SetValue(false);
+ }
+}
+
+void PreferencesDialog::PreSelectUserConfigItemName(const std::filesystem::path& n) {
+ CTRL(wxTreeCtrl, treItems);
+ wxTreeItemId id = treItems->GetRootItem();
+ wxTreeItemIdValue ctx;
+ wxTreeItemId i = treItems->GetFirstChild(id, ctx);
+ while (i.IsOk() && treItems->GetItemText(i) != wxT("user")) {
+ i = treItems->GetNextChild(id, ctx);
+ }
+ if (!i.IsOk()) {
+ return;
+ }
+
+ id = i;
+ i = treItems->GetFirstChild(id, ctx);
+ while (i.IsOk() && treItems->GetItemText(i) != wxFileName::FileName(n.c_str()).GetName()) {
+ i = treItems->GetNextChild(id, ctx);
+ }
+ if (!i.IsOk()) {
+ return;
+ }
+
+ treItems->SelectItem(i);
+ treItems->SetFocus();
+}
+
+const std::filesystem::path BuildNewConfFilePath() {
+ std::filesystem::path f = wxGetApp().GetConfigDir();
+
+ wxString ts = to_iso_string(boost::posix_time::microsec_clock::universal_time());
+ ts.Replace(wxT("."), wxT("_"));
+ f /= (wxT("Untitled_") + ts + wxT(".conf")).t_str();
+
+ BOOST_LOG_TRIVIAL(info) << "will create file: " << f.c_str();
+
+ return f;
+}
+
+void PreferencesDialog::OnActive(wxCommandEvent& evt) {
+ if (evt.IsChecked()) {
+ CTRL(wxTreeCtrl, treItems);
+ const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
+ if (data->isFile()) {
+ const std::filesystem::path p = data->path();
+ wxString name = wxFileName::FileName(p.c_str()).GetName();
+ this->active = name;
+ wxConfigBase::Get()->Write(wxT("/ActivePreferences/name"), this->active);
+ BuildItemTree(); // invalidates "data" pointer variable
+ PreSelectUserConfigItemName(p);
+ }
+ } else {
+ // TODO what if they uncheck the active checkbox?
+ }
+}
+
+void PreferencesDialog::OnDuplicate(wxCommandEvent& evt) {
+ CTRL(wxTreeCtrl, treItems);
+ const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
+ if (data->isFile()) {
+ if (data->isEditable()) {
+ Save(data->path());
+ }
+ const std::filesystem::path f = BuildNewConfFilePath();
+ BOOST_LOG_TRIVIAL(info) << "copy from: " << data->path().c_str();
+ if (!std::filesystem::exists(f)) {
+ std::filesystem::copy_file(data->path(), f, std::filesystem::copy_options::skip_existing);
+ BuildItemTree();
+ PreSelectUserConfigItemName(f);
+ } else {
+ BOOST_LOG_TRIVIAL(error) << "file already exists: " << f.c_str();
+ }
+ }
+}
+
+void PreferencesDialog::OnDelete(wxCommandEvent& evt) {
+ CTRL(wxTreeCtrl, treItems);
+ CTRL(wxStaticText, txtName);
+ CTRL(wxTextCtrl, txtConfig);
+ const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
+ if (data->isFile()) {
+ if (data->isEditable()) {
+ if (wxMessageBox(
+ wxT("Are you sure to want to permanently DELETE this configuration file?"),
+ wxT("Delete"), wxYES_NO|wxCENTER, this) == wxYES) {
+ std::filesystem::remove(data->path());
+ BuildItemTree();
+ treItems->SetFocus();
+ treItems->SelectItem(treItems->GetRootItem());
+ // clear out fields (TODO is there a better way?)
+ this->sOrigConfig = wxEmptyString;
+ txtConfig->SetValue(this->sOrigConfig);
+ txtName->SetLabel(wxEmptyString);
+ }
+ }
+ }
+}
+
+void PreferencesDialog::OnRename(wxCommandEvent& evt) {
+ CTRL(wxTreeCtrl, treItems);
+ const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
+ if (data->isFile()) {
+ if (data->isEditable()) {
+ Save(data->path());
+ wxString name = wxFileName::FileName(data->path().c_str()).GetName();
+ wxString newname = wxGetTextFromUser(wxT("new name:"), wxT("Rename configuration"), name, this, -1, -1, true);
+ if (!newname.IsEmpty() && newname != name) {
+ wxFileName fn(data->path().c_str());
+ fn.SetName(newname);
+ // TODO should we check for existence of name in built-in (to prevent override)?
+ if (fn.Exists()) {
+ wxMessageBox(wxT("That name is already being used."), wxT("File exists"), wxOK|wxCENTER, this);
+ } else {
+ const std::filesystem::path newpath(fn.GetFullPath().t_str());
+ std::filesystem::rename(data->path(), newpath);
+ BuildItemTree();
+ PreSelectUserConfigItemName(newpath);
+ }
+ }
+ }
+ }
+}
+
+void PreferencesDialog::OnCloseButton(wxCommandEvent& evt) {
+ CTRL(wxTreeCtrl, treItems);
+ const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());
+ if (data->isFile()) {
+ if (data->isEditable()) {
+ Save(data->path());
+ }
+ }
+ EndModal(wxID_OK);
+}
diff --git a/src/PreferencesDialog.h b/src/PreferencesDialog.h
new file mode 100644
index 0000000..3efa79b
--- /dev/null
+++ b/src/PreferencesDialog.h
@@ -0,0 +1,35 @@
+#ifndef PREFERENCESDIALOG_H
+#define PREFERENCESDIALOG_H
+
+#include
+#include
+#include
+#include
+
+class PreferencesDialog : public wxDialog {
+ wxWindow *parent;
+ wxString sOrigConfig;
+ wxString active;
+
+ void BuildItemTree();
+ void PreSelectUserConfigItemName(const std::filesystem::path& n);
+ void Save(const std::filesystem::path& to);
+
+ void OnClose(wxCloseEvent& event);
+ void OnCloseButton(wxCommandEvent& evt);
+ void OnTreeSelectionChanged(wxTreeEvent& evt);
+ void OnActive(wxCommandEvent& evt);
+ void OnDuplicate(wxCommandEvent& evt);
+ void OnDelete(wxCommandEvent& evt);
+ void OnRename(wxCommandEvent& evt);
+
+ wxDECLARE_EVENT_TABLE();
+
+public:
+ PreferencesDialog(wxWindow *parent);
+ ~PreferencesDialog();
+
+ void OnInit();
+};
+
+#endif /* PREFERENCESDIALOG_H */
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..6a5c50c
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,9 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#cmakedefine PROJECT_VENDOR @PROJECT_VENDOR@
+#cmakedefine PROJECT_NAME @PROJECT_NAME@
+#cmakedefine PROJECT_VERSION @PROJECT_VERSION@
+#cmakedefine PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@"
+
+#endif /* CONFIG_H */