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 @@ + + + + + ; + XRC + 1 + source_name + 0 + 0 + res + UTF-8 + connect + epple2 + 1000 + none + + + 0 + epple2 + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + Preferences + + 1024,768 + wxCAPTION|wxCLOSE_BOX|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxRESIZE_BORDER|wxSYSTEM_MENU + ; ; forward_declare + Preferences + + 0 + + + + + + szrvTop + wxVERTICAL + none + + 10 + wxALL|wxEXPAND + 1 + + + szrvMain + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_splitter1 + 1 + + + protected + 1 + + Resizable + 0.25 + 0 + -1 + 1 + + wxSPLIT_VERTICAL + wxSP_LIVE_UPDATE + ; ; forward_declare + 0 + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel1 + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + szrvItemTree + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + treItems + 1 + + + protected + 1 + + Resizable + 1 + + wxTR_FULL_ROW_HIGHLIGHT|wxTR_HAS_BUTTONS|wxTR_HAS_VARIABLE_ROW_HEIGHT|wxTR_LINES_AT_ROOT|wxTR_TWIST_BUTTONS + ; ; forward_declare + 0 + + + + wxHSCROLL|wxVSCROLL + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel2 + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + wxTAB_TRAVERSAL + + + szrvCurrentItem + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + + szrvItemFields + wxVERTICAL + none + + 0 + wxEXPAND + 0 + + + szrhItemControl + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Active + + 0 + + + 0 + + 1 + chkActive + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 25 + wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Duplicate + + 0 + + 0 + + + 0 + + 1 + btnDuplicate + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 0 + + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Delete + + 0 + + 0 + + + 0 + + 1 + btnDelete + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + 5 + wxBOTTOM|wxEXPAND|wxLEFT + 0 + + wxID_ANY + name + + szrsName + wxHORIZONTAL + 1 + none + + 7 + wxLEFT|wxTOP + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Example text here + 0 + + 0 + + + 0 + + 1 + txtName + 1 + + + protected + 1 + + Resizable + 1 + + wxST_ELLIPSIZE_MIDDLE + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxBOTTOM|wxEXPAND|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Rename + + 0 + + 0 + + + 0 + + 1 + btnRename + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + 5 + wxBOTTOM|wxEXPAND|wxTOP + 1 + + + szrhConfig + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + -1,-1 + 1 + txtConfig + 1 + + + protected + 1 + + Resizable + 1 + -1,-1 + wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_PROCESS_TAB + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + 0 + wxEXPAND + 0 + + + szrhControl + wxHORIZONTAL + none + + 0 + wxALIGN_BOTTOM + 1 + + 0 + protected + 0 + + + + 0 + wxALIGN_BOTTOM|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 1 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Close + + 0 + + 0 + + + 0 + + 1 + wxID_OK + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + m_btnCloseOnButtonClick + + + + + + + + + + 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 */