[
  {
    "path": ".appveyor.yml",
    "content": "version: '2.4.0-git-{build}'\nimage: 'Visual Studio 2019'\nclone_depth: 1\n\n# Build configuration\nconfiguration:\n  - Release\n\n# Environment\nenvironment:\n  NINJA_URL: https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-win.zip\n  VSVARSALLPATH: 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat'\n  ARCH: x64\n  PYTHON: 'C:\\Python36-x64'\n  PACKAGE_NAME: cutter-git-x64.Windows\n\ninstall:\n  - ps: $env:path = ($env:path -split \";\").Where({!($_ -like \"*Microsoft SQL Server*\")}) -join \";\"\n  - cmd: C:\\msys64\\usr\\bin\\bash -lc \"cd $APPVEYOR_BUILD_FOLDER && scripts/fetch_deps.sh\"\n  - cmd: set \"CUTTER_DEPS=%APPVEYOR_BUILD_FOLDER%\\cutter-deps\"\n  - cmd: set \"PATH=%CD%;%PYTHON%;%PATH%\"\n  - cmd: call \"%VSVARSALLPATH%\" %ARCH%\n  - cmd: set \"PATH=%CUTTER_DEPS%\\qt\\bin;%PYTHON%\\Scripts\\;%PATH%\"\n  - cmd: echo %PATH%\n  - cmd: python -m pip install meson ninja\n  - ps: choco install winflexbison3\n  # Artifacts\n  - cmd: set \"ARTIFACT_PATH=build\\%ARTIFACT_NAME%\"\n\nbefore_build:\n  - cmd: git submodule update --init --recursive\n\n# Build config\nbuild_script:\n  - cmd: |\n     mkdir build\n     cd build\n     set PACKAGE_NAME=cutter-git-x64.Windows\n  - cmd: \"cmake\n       -DCMAKE_BUILD_TYPE=Release\n       -DCUTTER_USE_BUNDLED_RIZIN=ON\n       -DCUTTER_ENABLE_PYTHON=ON\n       -DCUTTER_ENABLE_PYTHON_BINDINGS=ON\n       -DCUTTER_ENABLE_PACKAGING=ON\n       -DCUTTER_PACKAGE_DEPENDENCIES=ON\n       -DCUTTER_PACKAGE_RZ_GHIDRA=ON\n       -DCUTTER_PACKAGE_JSDEC=ON\n       -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON\n       -DCMAKE_PREFIX_PATH=%CUTTER_DEPS%\\\\pyside\n       -DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME%\n       -G Ninja\n       ..\"\n  - cmd: cmake --build . --config Release\n  - cmd: cmake --build . --config Release --target package\n\n# Tests\ntest: off\n\n# Artifacts\nartifacts:\n  - path: \"%ARTIFACT_PATH%\"\n    name: \"%ARTIFACT_NAME%\"\n\n#deploy:\n#  description: 'Cutter binaries'\n#  provider: GitHub\n#  auth_token:\n#    secure: 2SmsqS2RaX2N5c9UwUcfBwNmMX64FfPAZFShLyxIkZXiC8vLaYCHToWxBYEuWRSk\n#  artifact: \"%ARTIFACT_NAME%\"\n#  draft: true\n#  prerelease: true\n#  on:\n#    appveyor_repo_tag: true\n#    DEPLOY: true\n\nfor:\n  -\n    branches:\n      only:\n        - dev\n        - stable\n  -\n    skip_non_tags: true\n\n"
  },
  {
    "path": ".dockerignore",
    "content": "*\n!docker/*.sh\n!scripts/\n!src/\n!build*.sh\n!rizin/*/\nrizin/.*/\nrizin/doc\nrizin/man\n!rizin/config-user.mk.acr\n!rizin/configure\n!rizin/Makefile\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n**Environment information**\n* Operating System:\n* Cutter version:\n* Obtained from:\n  - [x] Built from source\n  - [ ] Downloaded release from Cutter website or GitHub \n  - [ ] Distribution repository\n* File format:\n\n**Describe the bug**\n\n<!-- A clear and concise description of what the bug is. -->\n\n**To Reproduce**\n\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\n\n<!-- A clear and concise description of what you expected to happen. -->\n\n\n**Screenshots**\n\n<!-- If applicable, add screenshots to help explain your problem. -->\n\n\n**Additional context**\n\n<!-- Add any other context about the problem here. -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n\ncontact_links:\n  - name: Questions Telegram\n    url: https://t.me/cutter_re\n    about: Please ask questions about Cutter here or one of the other community channels, not in the issue tracker.\n\n  - name: Questions IRC\n    url: https://web.libera.chat/#cutter\n    about: \"#cutter on https://web.libera.chat/\"\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem? Please describe.**\n\n<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->\n\n**Describe the solution you'd like**\n\n<!-- A clear and concise description of what you want to happen.\nFor UI requests, don't hesitate to post some draws. -->\n\n**Describe alternatives you've considered**\n\n<!-- A clear and concise description of any alternative solutions or features you've considered. -->\n\n**Additional context**\n\n<!-- Add any other context or screenshots about the feature request here. -->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!-- Filling this template is mandatory -->\n\n**Your checklist for this pull request**\n- [ ] I've read the [guidelines for contributing](https://cutter.re/docs/contributing/code/getting-started.html) to this repository\n- [ ] I made sure to follow the project's [coding style](https://cutter.re/docs/contributing/code/development-guidelines.html)\n- [ ] I've updated the [documentation](https://cutter.re/docs/user-docs.html) with the relevant information (if needed)\n- [ ] I've used AI tools to generate fully or partially these code changes and I'm sure the changes are not copyrighted by somebody else.\n\n\n**Detailed description**\n\n<!-- Explain the **details** for making this change. Is a new feature implemented? What existing problem does the pull request solve? How does the pull request solve these issues? Please provide enough information so that others can review your pull request. If you have used AI tools to generate these code changes, please disclose software used, model name. -->\n\n**Test plan (required)**\n\n<!-- What steps should the reviewer take to test your pull request? Demonstrate that the code is solid. Example: The exact actions you made and their outcome. Add screenshots/videos if the pull request changes UI. This is your time to re-check that everything works and that you covered all the edge cases -->\n\n\n<!-- **Code formatting**\nMake sure you ran clang-format on your code before making the PR. Check our contribution guidelines here: https://cutter.re/docs/contributing/code/getting-started.html -->\n\n**Closing issues**\n\n<!-- put \"closes #XXXX\" in your comment to auto-close the issue that your PR fixes (if such). -->\n"
  },
  {
    "path": ".github/actions/build-linux-old/action.yml",
    "content": "name: 'Build linux'\ndescription: 'Build cutter in a docker image'\ninputs: \n  system-deps:\n    description: 'Use system libraries instead of cutter-deps'\n    required: true\n  image:\n    description: 'Docker image'\n    required: true\n  qt-major:\n    description: 'Qt major version'\n    required: true\n  package:\n    description: 'Package appimage'\n    required: true\nruns:\n  using: 'docker'\n  image: 'ubuntu:18.04'\n  entrypoint: './.github/actions/build-linux-old/entrypoint.sh'\n  args: \n    - ${{ inputs.system-deps }}\n  env:\n    package: ${{ inputs.package }}\n    qt_major: ${{ inputs.qt-major }}\n    image: ${{ inputs.image }}\n    system_deps: ${{ inputs.system-deps }}"
  },
  {
    "path": ".github/actions/build-linux-old/entrypoint.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\npwd\nls\n\n#export TZ=UTC\n#ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\n\napt-get -y update\n\n# latest git and cmake\nexport GIT_VERSION=\"git-2.36.1\"\nexport CMAKE_VERSION=\"3.25.3\"\n\napt-get -y install wget libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev build-essential\n\nwget \"https://www.kernel.org/pub/software/scm/git/$GIT_VERSION.tar.gz\"\ntar -zxf \"$GIT_VERSION.tar.gz\"\n# build.\n#make -C \"$GIT_VERSION\" prefix=/usr install -j > \"$GIT_VERSION/build.log\"\n# ensure git is installed.\n#git version\nwget \"https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-linux-x86_64.sh\"\nbash ./cmake-$CMAKE_VERSION-linux-x86_64.sh --skip-license --prefix=/usr\n# ensure cmake is installed.\ncmake --version\n# cleanup dev environment.\nrm -rf \"$GIT_VERSION.tar.gz\" \"$GIT_VERSION\" cmake-$CMAKE_VERSION-linux-x86_64.sh\nunset CMAKE_VERSION\nunset GIT_VERSION\n\napt-get -y install libgraphviz-dev \\\n    mesa-common-dev \\\n    libxkbcommon-x11-dev \\\n    ninja-build \\\n    python3-pip \\\n    curl \\\n    libpcre2-dev \\\n    libfuse2 \\\n    pkg-config \\\n    git\n\n\nif [ \"$image\" = \"ubuntu:18.04\" ]; then\n    # install additional packages needed for appimage\n    apt-get -y install gcc-7 \\\n                        libglu1-mesa-dev \\\n                        freeglut3-dev \\\n                        mesa-common-dev \\\n                        libclang-10-dev \\\n                        llvm-10\n    ln -s /usr/bin/llvm-config-10 /usr/bin/llvm-config\nfi\nif [ \"$image\" = \"ubuntu:18.04\" ] || [ \"$image\" = \"ubuntu:20.04\" ]; then\n    # install additional packages needed for appimage\n    apt-get -y install libxcb1-dev \\\n                        libxkbcommon-dev \\\n                        libxcb-*-dev \\\n                        libegl1\nfi\nif [ \"$image\" = \"ubuntu:20.04\" ] && [ \"$system_deps\" = \"false\" ]; then\n    # install additional packages needed for appimage\n    apt-get -y install libclang-12-dev \\\n                        llvm-12 \\\n                        libsm6 \\\n                        libwayland-dev  \\\n                        libgl1-mesa-dev\nfi\nif [ \"$image\" = \"ubuntu:18.04\" ] && [ \"$system_deps\" = \"true\" ]; then\n    apt-get -y install qt5-default \\\n                        libqt5svg5-dev \\\n                        qttools5-dev \\\n                        qttools5-dev-tools\nfi\nif [ \"$image\" = \"ubuntu:22.04\" ]; then\n    apt-get -y install libclang-12-dev \\\n                        llvm-12 \\\n                        qt6-base-dev \\\n                        qt6-tools-dev \\\n                        qt6-tools-dev-tools \\\n                        libqt6svg6-dev \\\n                        libqt6core5compat6-dev \\\n                        libqt6svgwidgets6 \\\n                        qt6-l10n-tools \\\n                        gcc-12 \\\n                        g++-12\nfi\n\n# https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true\npython3 -m pip install meson==0.61.5\n\n\nif [ \"$system_deps\" = \"false\" ]\nthen\n    CUTTER_QT=\"$qt_major\" scripts/fetch_deps.sh\n    set +u # TODO: remove temp code after updating cutter_deps\n    . cutter-deps/env.sh\n    set -u\n    #export LD_LIBRARY_PATH=\"`llvm-config --libdir`:$LD_LIBRARY_PATH\"\nfi\n#if [ \"${{ matrix.cc-override }}\" != \"default\" ]\n#then\n#    export CC=\"${{matrix.cc-override}}\"\n#    export CXX=\"${{matrix.cxx-override}}\"\n#fi\n\n# otherwise git complains about dubious ownership, due to code being checked out outside the container with a different user\ngit config --global --add safe.directory /github/workspace/rizin\n\nmkdir build\ncd build\nif [ \"$system_deps\" = \"false\" ]\nthen\n    locale\n    locale -a\n    export LANG=\"C.UTF-8\"\n    export LC_ALL=\"C.UTF-8\"\n    cmake \\\n    -G Ninja \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCUTTER_ENABLE_PYTHON=ON \\\n    -DPython3_ROOT_DIR=\"$CUTTER_DEPS_PYTHON_PREFIX\" \\\n    -DCUTTER_ENABLE_PYTHON_BINDINGS=ON \\\n    -DCUTTER_ENABLE_GRAPHVIZ=ON \\\n    -DCUTTER_USE_BUNDLED_RIZIN=ON \\\n    -DCUTTER_APPIMAGE_BUILD=ON \\\n    -DCUTTER_ENABLE_PACKAGING=ON \\\n    -DCUTTER_ENABLE_KSYNTAXHIGHLIGHTING=OFF \\\n    -DCUTTER_ENABLE_SIGDB=ON \\\n    -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \\\n    -DCUTTER_PACKAGE_RZ_GHIDRA=ON \\\n    -DCUTTER_PACKAGE_JSDEC=ON \\\n    -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \\\n    -DCUTTER_PACKAGE_RZ_LIBYARA=ON \\\n    -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \\\n    -DCMAKE_INSTALL_PREFIX=appdir/usr \\\n    -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \\\n    -DCUTTER_QT=$qt_major \\\n    ..\nelse\n    cmake \\\n    -G Ninja \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCUTTER_USE_BUNDLED_RIZIN=ON \\\n    -DCUTTER_QT=$qt_major \\\n    ..\nfi\nninja\nif [ \"$package\" = \"true\" ]\nthen\n    export CUTTER_VERSION=$(python ../scripts/get_version.py)\n    export VERSION=$CUTTER_VERSION\n    ninja install\n    if [ $qt_major == \"6\" ]\n    then\n        pyside_ver=6\n    else\n        pyside_ver=2\n    fi\n    \"../scripts/appimage_embed_python.sh\" appdir $pyside_ver\n    APP_PREFIX=`pwd`/appdir/usr\n    export LD_LIBRARY_PATH=\"${LD_LIBRARY_PATH:-}:$APP_PREFIX/lib/rizin/plugins\"\n    export PATH=$PATH:${APP_PREFIX}/bin\n    wget -c \"https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage\"\n    chmod a+x linuxdeployqt*.AppImage\n    rm -fv \"../cutter-deps/qt/plugins/imageformats/libqjp2.so\"\n    if [ \"$qt_major\" == \"5\" ]; then\n    export APPIMAGE_FILE=\"Cutter-${PACKAGE_ID}-Linux-Qt5-x86_64.AppImage\"\n    ./linuxdeployqt*.AppImage --appimage-extract-and-run \\\n        ./appdir/usr/share/applications/*.desktop \\\n        -executable=./appdir/usr/bin/python3 \\\n        -appimage \\\n        -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \\\n        -ignore-glob=usr/lib/python3.12/**/* \\\n        -verbose=2\n    else\n    export APPIMAGE_FILE=\"Cutter-${PACKAGE_ID}-Linux-x86_64.AppImage\"\n    ./linuxdeployqt*.AppImage --appimage-extract-and-run \\\n        ./appdir/usr/share/applications/*.desktop \\\n        -executable=./appdir/usr/bin/python3 \\\n        -appimage \\\n        -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \\\n        -exclude-libs=\"libwayland-client.so,libwayland-egl.so,libwayland-cursor.so\" \\\n        -ignore-glob=usr/lib/python3.12/**/* \\\n        -extra-plugins=\"platforms/libqwayland-egl.so,platforms/libqwayland-generic.so,wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-integration,wayland-graphics-integration-server\" \\\n        -verbose=2\n    fi\n    find ./appdir -executable -type f -exec ldd {} \\; | cut -d \" \" -f 2-3 | sort | uniq\n    # find ./appdir -executable -type f -exec ldd {} \\; | grep \" => /usr\" | cut -d \" \" -f 2-3 | sort | uniq\n    \n    mv Cutter-*-x86_64.AppImage \"$APPIMAGE_FILE\"\n    echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_ENV\n    echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_OUTPUT\n    echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_ENV\n    echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_OUTPUT\n    echo UPLOAD_ASSET_TYPE=application/x-executable >> $GITHUB_ENV\nfi"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n    - dev\n    - stable\n    tags:\n    - v*\n    - upload-test*\n    \n  pull_request:\n    branches:\n    - dev\n    - stable\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  build-linux:\n    name: ${{ matrix.name }}\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ matrix.image }}\n      options: --privileged\n    strategy:\n      matrix:\n        name: [\n            linux-x86_64,\n            linux-x86_64-system-deps,\n            tarball\n        ]\n        include:\n          - qt-major: 6\n            cc-override: default\n            cxx-override: default\n          - name: linux-x86_64-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 22.04 using sytem libraries\n            image: ubuntu:22.04\n            python-version: 3.11.x\n            system-deps: true\n            package: false\n            tarball: false\n            cc-override: '/usr/bin/gcc-12'\n            cxx-override: '/usr/bin/g++-12'\n          - name: linux-x86_64 # main Appimage build\n            image: ubuntu:20.04\n            python-version: 3.6.x\n            system-deps: false\n            package: true\n            tarball: false\n          - name: tarball\n            python-version: 3.6.x\n            image: ubuntu:20.04\n            system-deps: false\n            package: false\n            tarball: true\n      # Prevent one job from pausing the rest\n      fail-fast: false\n    steps:\n    - name: set timezone\n      run: |\n        # Fix timezone on ubuntu to prevent user input request during the apt-get phase.\n        export TZ=UTC\n        ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\n\n    - name: install latest git and cmake\n      shell: bash\n      run: |\n        set -e\n        apt-get -y update\n        echo \"Using image: ${{ matrix.image }}\"\n\n        export GIT_VERSION=\"git-2.36.1\"\n        export CMAKE_VERSION=\"3.25.3\"\n\n        apt-get -y install wget libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev build-essential\n\n        wget \"https://www.kernel.org/pub/software/scm/git/$GIT_VERSION.tar.gz\"\n        tar -zxf \"$GIT_VERSION.tar.gz\"\n\n        # build.\n        make -C \"$GIT_VERSION\" prefix=/usr install -j > \"$GIT_VERSION/build.log\"\n\n        # ensure git is installed.\n        git version\n\n        wget \"https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-linux-x86_64.sh\"\n        bash ./cmake-$CMAKE_VERSION-linux-x86_64.sh --skip-license --prefix=/usr\n\n        # ensure cmake is installed.\n        cmake --version\n\n        # cleanup dev environment.\n        rm -rf \"$GIT_VERSION.tar.gz\" \"$GIT_VERSION\" cmake-$CMAKE_VERSION-linux-x86_64.sh\n        unset CMAKE_VERSION\n        unset GIT_VERSION\n\n    - uses: actions/checkout@v4\n      with:\n        submodules: recursive\n        persist-credentials: false\n\n    - name: apt cutter dependencies\n      shell: bash\n      run: |\n        # install needed packages\n        apt-get -y install libgraphviz-dev \\\n                           mesa-common-dev \\\n                           libxkbcommon-x11-dev \\\n                           ninja-build \\\n                           python3-pip \\\n                           curl \\\n                           libpcre2-dev \\\n                           libfuse2 \\\n                           pkg-config\n\n        if [ \"${{ matrix.image }}\" = \"ubuntu:18.04\" ]; then\n          # install additional packages needed for appimage\n          apt-get -y install gcc-7 \\\n                             libglu1-mesa-dev \\\n                             freeglut3-dev \\\n                             mesa-common-dev \\\n                             libclang-8-dev \\\n                             llvm-8\n          ln -s /usr/bin/llvm-config-8 /usr/bin/llvm-config\n        fi\n        if [ \"${{ matrix.image }}\" = \"ubuntu:18.04\" ] || [ \"${{ matrix.image }}\" = \"ubuntu:20.04\" ]; then\n          # install additional packages needed for appimage\n          apt-get -y install libxcb1-dev \\\n                             libxkbcommon-dev \\\n                             libxcb-*-dev \\\n                             libegl1\n        fi\n        if [ \"${{ matrix.image }}\" = \"ubuntu:20.04\" ] && [ \"${{ matrix.system-deps }}\" = \"false\" ]; then\n          # install additional packages needed for appimage\n          apt-get -y install libclang-12-dev \\\n                             llvm-12 \\\n                             libsm6 \\\n                             libwayland-dev  \\\n                             libgl1-mesa-dev\n        fi\n        if [ \"${{ matrix.image }}\" = \"ubuntu:18.04\" ] && [ \"${{ matrix.system-deps }}\" = \"true\" ]; then\n          apt-get -y install qt5-default \\\n                             libqt5svg5-dev \\\n                             qttools5-dev \\\n                             qttools5-dev-tools\n        fi\n        if [ \"${{ matrix.image }}\" = \"ubuntu:22.04\" ]; then\n          apt-get -y install libclang-12-dev \\\n                             llvm-12 \\\n                             qt6-base-dev \\\n                             qt6-tools-dev \\\n                             qt6-tools-dev-tools \\\n                             libqt6svg6-dev \\\n                             libqt6core5compat6-dev \\\n                             libqt6svgwidgets6 \\\n                             qt6-l10n-tools \\\n                             gcc-12 \\\n                             g++-12\n        fi\n    - uses: actions/setup-python@v5\n      if: matrix.system-deps == false\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: py dependencies\n      run: |\n        python3 --version\n        which python3\n        # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true\n        python3 -m pip install meson==0.61.5\n    - name: Prepare package id\n      shell: bash\n      run: |\n        if [ \"${{ startsWith(github.event.ref, 'refs/tags')}}\" = \"true\" ]\n        then\n          PACKAGE_ID=\"${{ github.event.ref }}\"\n        else\n          PACKAGE_ID=\"git-`date \"+%Y-%m-%d\"`-${{ format('{0}', github.sha) }}\"\n        fi\n        PACKAGE_ID=${PACKAGE_ID##refs/tags/}\n        echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV\n    - name: cmake ubuntu\n      shell: bash\n      run: |\n        if [ \"${{ matrix.system-deps }}\" = \"false\" ]\n        then\n          CUTTER_QT=\"${{ matrix.qt-major }}\" scripts/fetch_deps.sh\n          . cutter-deps/env.sh\n          export LD_LIBRARY_PATH=\"`llvm-config --libdir`:$LD_LIBRARY_PATH\"\n        fi\n        set -e #TODO: move to top once cutter-deps doesn't fail\n        if [ \"${{ matrix.cc-override }}\" != \"default\" ]\n        then\n          export CC=\"${{matrix.cc-override}}\"\n          export CXX=\"${{matrix.cxx-override}}\"\n        fi\n\n        mkdir build\n        cd build\n        if [ \"${{ matrix.system-deps }}\" = \"false\" ]\n        then\n          locale\n          locale -a\n          export LANG=\"C.UTF-8\"\n          export LC_ALL=\"C.UTF-8\"\n          cmake \\\n            -G Ninja \\\n            -DCMAKE_BUILD_TYPE=Release \\\n            -DCUTTER_ENABLE_PYTHON=ON \\\n            -DPython3_ROOT_DIR=\"$CUTTER_DEPS_PYTHON_PREFIX\" \\\n            -DCUTTER_ENABLE_PYTHON_BINDINGS=ON \\\n            -DCUTTER_ENABLE_GRAPHVIZ=ON \\\n            -DCUTTER_USE_BUNDLED_RIZIN=ON \\\n            -DCUTTER_APPIMAGE_BUILD=ON \\\n            -DCUTTER_ENABLE_PACKAGING=ON \\\n            -DCUTTER_ENABLE_KSYNTAXHIGHLIGHTING=OFF \\\n            -DCUTTER_ENABLE_SIGDB=ON \\\n            -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \\\n            -DCUTTER_PACKAGE_RZ_GHIDRA=ON \\\n            -DCUTTER_PACKAGE_JSDEC=ON \\\n            -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \\\n            -DCUTTER_PACKAGE_RZ_LIBYARA=ON \\\n            -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \\\n            -DCMAKE_INSTALL_PREFIX=appdir/usr \\\n            -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \\\n            -DCUTTER_QT=${{ matrix.qt-major }} \\\n            ..\n        else\n          cmake \\\n            -G Ninja \\\n            -DCMAKE_BUILD_TYPE=Release \\\n            -DCUTTER_QT=${{ matrix.qt-major }} \\\n            -DCUTTER_USE_BUNDLED_RIZIN=ON \\\n            ..\n        fi\n        ninja\n        if [ \"${{ matrix.package }}\" = \"true\" ]\n        then\n          export CUTTER_VERSION=$(python ../scripts/get_version.py)\n          export VERSION=$CUTTER_VERSION\n          ninja install\n          \"../scripts/appimage_embed_python.sh\" appdir ${{ matrix.qt-major == '6' && '6' || '2' }}\n          APP_PREFIX=`pwd`/appdir/usr\n          export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:$APP_PREFIX/lib/rizin/plugins\"\n          export PATH=$PATH:${APP_PREFIX}/bin\n          wget -c \"https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage\"\n          chmod a+x linuxdeployqt*.AppImage\n          rm -fv \"../cutter-deps/qt/plugins/imageformats/libqjp2.so\"\n          if [ \"${{ matrix.qt-major }}\" == \"5\" ]; then\n            export APPIMAGE_FILE=\"Cutter-${PACKAGE_ID}-Linux-Qt5-x86_64.AppImage\"\n            ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop \\\n              -executable=./appdir/usr/bin/python3 \\\n              -appimage \\\n              -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \\\n              -ignore-glob=usr/lib/python3.12/**/* \\\n              -verbose=2\n          else\n            export APPIMAGE_FILE=\"Cutter-${PACKAGE_ID}-Linux-x86_64.AppImage\"\n            ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop \\\n              -executable=./appdir/usr/bin/python3 \\\n              -appimage \\\n              -no-strip -exclude-libs=libnss3.so,libnssutil3.so,libqjp2.so \\\n              -exclude-libs=\"libwayland-client.so,libwayland-egl.so,libwayland-cursor.so\" \\\n              -ignore-glob=usr/lib/python3.12/**/* \\\n              -extra-plugins=\"platforms/libqwayland-egl.so,platforms/libqwayland-generic.so,wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-integration,wayland-graphics-integration-server\" \\\n              -verbose=2\n          fi\n          find ./appdir -executable -type f -exec ldd {} \\; | cut -d \" \" -f 2-3 | sort | uniq\n          # find ./appdir -executable -type f -exec ldd {} \\; | grep \" => /usr\" | cut -d \" \" -f 2-3 | sort | uniq\n          \n          mv Cutter-*-x86_64.AppImage \"$APPIMAGE_FILE\"\n          echo PACKAGE_NAME=$APPIMAGE_FILE >> $GITHUB_ENV\n          echo PACKAGE_PATH=build/$APPIMAGE_FILE >> $GITHUB_ENV\n          echo UPLOAD_ASSET_TYPE=application/x-executable >> $GITHUB_ENV\n        fi\n    - name: Create tarball\n      if: matrix.tarball\n      shell: bash\n      run: |\n        scripts/tarball.sh \"Cutter-${PACKAGE_ID}\"\n        echo PACKAGE_NAME=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV\n        echo PACKAGE_PATH=Cutter-${PACKAGE_ID}-src.tar.gz >> $GITHUB_ENV\n        echo UPLOAD_ASSET_TYPE=application/gzip >> $GITHUB_ENV\n    - uses: actions/upload-artifact@v4\n      if: env.PACKAGE_NAME != null\n      with:\n        name: ${{ env.PACKAGE_NAME }}\n        path: ${{ env.PACKAGE_PATH }}\n    - name: Get release\n      if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')\n      id: get_release\n      uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    - name: Upload release assets\n      if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null\n      uses: actions/upload-release-asset@v1.0.2\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      with:\n          upload_url: ${{ steps.get_release.outputs.upload_url }}\n          asset_path: ${{ env.PACKAGE_PATH }}\n          asset_name: ${{ env.PACKAGE_NAME }}\n          asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }}\n    - name: Set output\n      id: output_setup\n      if: ${{ matrix.name == 'linux-x86_64' }}\n      run: |\n        echo \"artifact_name=$PACKAGE_NAME\" >> \"$GITHUB_OUTPUT\"\n    outputs:\n      artifact_linux: ${{ steps.output_setup.outputs.artifact_name }}\n  build-linux-old:\n    name: ${{ matrix.name }}\n    runs-on: ubuntu-latest # only a host environement, actual building happens in a docker image\n    strategy:\n      matrix:\n        name: [\n            linux-x86_64-qt5,\n            linux-x86_64-qt5-system-deps,\n        ]\n        include:\n          - qt-major: 6\n            cc-override: default\n            cxx-override: default\n          - name: linux-x86_64-qt5-system-deps # ensure that Cutter can be built at least in basic config on Ubuntu 18.04 using sytem libraries\n            image: ubuntu:18.04\n            python-version: 3.6.x\n            system-deps: true\n            package: false\n            cc-override: '/usr/bin/gcc-7'\n            cxx-override: '/usr/bin/g++-7'\n            qt-major: 5\n          - name: linux-x86_64-qt5 # qt5 Appimage build for increased compatibility with older distros\n            image: ubuntu:18.04\n            python-version: 3.6.x\n            system-deps: false\n            package: true\n            qt-major: 5\n      # Prevent one job from pausing the rest\n      fail-fast: false\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        submodules: recursive\n        persist-credentials: false\n    - name: Prepare package id\n      shell: bash\n      run: |\n        if [ \"${{ startsWith(github.event.ref, 'refs/tags')}}\" = \"true\" ]\n        then\n          PACKAGE_ID=\"${{ github.event.ref }}\"\n        else\n          PACKAGE_ID=\"git-`date \"+%Y-%m-%d\"`-${{ format('{0}', github.sha) }}\"\n        fi\n        PACKAGE_ID=${PACKAGE_ID##refs/tags/}\n        echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV\n        ls -alh\n        who\n\n    - name: build linux\n      id: build\n      uses: ./.github/actions/build-linux-old\n      with:\n        system-deps: ${{ matrix.system-deps }}\n        image: ${{ matrix.image }}\n        qt-major: ${{ matrix.qt-major }}\n        package: ${{ matrix.package }}\n\n    - uses: actions/upload-artifact@v4\n      if: env.PACKAGE_NAME != null\n      with:\n        name: ${{ steps.build.outputs.PACKAGE_NAME }}\n        path: ${{ steps.build.outputs.PACKAGE_PATH }}\n    - name: Get release\n      if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')\n      id: get_release\n      uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    - name: Upload release assets\n      if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null\n      uses: actions/upload-release-asset@v1.0.2\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      with:\n          upload_url: ${{ steps.get_release.outputs.upload_url }}\n          asset_path: ${{ env.PACKAGE_PATH }}\n          asset_name: ${{ env.PACKAGE_NAME }}\n          asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }}\n\n\n  build:\n    name: ${{ matrix.name }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        name: [\n            macos-x86_64,\n            macos-arm64,\n            windows-x86_64,\n        ]\n        include:\n          - python-version: 3.12.x\n          - system-deps: false\n          - output-id: \"\"\n          - name: windows-x86_64\n            os: windows-2022\n            package: true\n            python-version: 3.12.x\n            output-id: artifact_windows\n          - name: macos-x86_64\n            os: macos-14\n            arch: x86_64\n            package: true\n          - name: macos-arm64\n            os: macos-14\n            arch: arm64\n            package: true\n            output-id: artifact_macos\n      # Prevent one job from pausing the rest\n      fail-fast: false\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        submodules: recursive\n        persist-credentials: false\n    - uses: actions/setup-python@v5\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: homebrew dependencies\n      if: contains(matrix.os, 'macos')\n      run: |\n        cd scripts\n        rm /usr/local/bin/2to3* # symlink to some kind of existing python2.7 installation conflicts with brew python3 which gets installed as indirect dependency\n        brew bundle\n        brew install pkg-config || brew link --overwrite pkgconf\n        brew install kadwanev/brew/retry\n    - name: py dependencies\n      run: |\n        #python3 -m pip install -U pip==21.3.1\n        pip install meson # ==0.61.5 # https://github.com/rizinorg/cutter/runs/7170222817?check_suite_focus=true\n        pip install setuptools\n    - name: Prepare package id\n      shell: bash\n      run: |\n        if [[ \"${{ startsWith(github.event.ref, 'refs/tags')}}\" = \"true\" ]]\n        then\n          PACKAGE_ID=\"${{ github.event.ref }}\"\n        else\n          PACKAGE_ID=\"git-`date \"+%Y-%m-%d\"`-${{ format('{0}', github.sha) }}\"\n        fi\n        PACKAGE_ID=${PACKAGE_ID##refs/tags/}\n        echo PACKAGE_ID=$PACKAGE_ID >> $GITHUB_ENV\n    - name: cmake macos\n      shell: bash\n      if: contains(matrix.os, 'macos')\n      run: |\n        export MACOSX_DEPLOYMENT_TARGET=10.15\n        scripts/fetch_deps.sh\n        install_name_tool -delete_rpath /Users/runner/work/cutter-deps/cutter-deps/qt/lib ./cutter-deps/pyside/lib/libpyside6.cpython-312-darwin.6.7.2.dylib #TODO: do this in cutter-deps\n        source cutter-deps/env.sh\n        set -euo pipefail\n        export PATH=/usr/local/opt/llvm/bin:$PATH\n        mkdir build\n        cd build\n        PACKAGE_NAME=Cutter-${PACKAGE_ID}-macOS-${{ matrix.arch }}\n        cmake \\\n                -DCMAKE_BUILD_TYPE=Release \\\n                -DPython3_ROOT_DIR=\"${CUTTER_DEPS_PYTHON_PREFIX}\" \\\n                -DPython_ROOT_DIR=\"${CUTTER_DEPS_PYTHON_PREFIX}\" \\\n                -DPython3_FIND_STRATEGY=\"LOCATION\" \\\n                -DPython_FIND_STRATEGY=\"LOCATION\" \\\n                -DCUTTER_ENABLE_PYTHON=ON \\\n                -DCUTTER_ENABLE_PYTHON_BINDINGS=ON \\\n                -DCUTTER_USE_BUNDLED_RIZIN=ON \\\n                -DCUTTER_ENABLE_PACKAGING=ON \\\n                -DCUTTER_ENABLE_SIGDB=ON \\\n                -DCUTTER_PACKAGE_DEPENDENCIES=ON \\\n                -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON \\\n                -DCUTTER_PACKAGE_RZ_GHIDRA=ON \\\n                -DCUTTER_PACKAGE_JSDEC=ON \\\n                -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON \\\n                -DCUTTER_PACKAGE_RZ_LIBYARA=ON \\\n                -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON \\\n                -DCPACK_PACKAGE_FILE_NAME=\"$PACKAGE_NAME\" \\\n                -DCPACK_BUNDLE_APPLE_CERT_APP=\"-\" \\\n                .. && \\\n              make -j4;\n        # Reduce chance for random hdiutil \"Resource busy\" error when creating the dmg\n        # https://github.com/actions/runner-images/issues/7522#issuecomment-1556766641\n        echo killing XProtectBehaviorService; sudo pkill -9 XProtect >/dev/null || true;\n        echo waiting for XProtectBehaviorService kill; while pgrep XProtect; do sleep 3; done;\n        retry make package\n        export CUTTER_VERSION=$(python3 ../scripts/get_version.py)\n        echo PACKAGE_NAME=${PACKAGE_NAME}.dmg >> $GITHUB_ENV\n        echo PACKAGE_PATH=build/${PACKAGE_NAME}.dmg >> $GITHUB_ENV\n        echo UPLOAD_ASSET_TYPE=application/x-apple-diskimage >> $GITHUB_ENV\n    - name: windows dependencies\n      if: contains(matrix.os, 'windows')\n      shell: bash\n      run: |\n        pip install ninja\n        scripts/fetch_deps.sh\n        choco install winflexbison3\n    - name: windows cmake\n      if: contains(matrix.os, 'windows')\n      shell: cmd\n      run: |\n        set ARCH=x64\n        set CUTTER_DEPS=%CD%\\cutter-deps\n        set PATH=%CD%\\cutter-deps\\qt\\bin;%PATH%\n        call \"C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build\\vcvarsall.bat\" x64\n        cd\n        mkdir build\n        cd build\n        set PACKAGE_NAME=Cutter-%PACKAGE_ID%-Windows-x86_64\n        cmake ^\n          -DCMAKE_BUILD_TYPE=Release ^\n          -DCUTTER_USE_BUNDLED_RIZIN=ON ^\n          -DCUTTER_ENABLE_PYTHON=ON ^\n          -DCUTTER_ENABLE_PYTHON_BINDINGS=ON ^\n          -DCUTTER_ENABLE_PACKAGING=ON ^\n          -DPython3_FIND_REGISTRY=NEVER ^\n          -DPython3_FIND_STRATEGY=LOCATION ^\n          -DPython_FIND_REGISTRY=NEVER ^\n          -DPython_FIND_STRATEGY=LOCATION ^\n          -DCUTTER_ENABLE_SIGDB=ON ^\n          -DCUTTER_PACKAGE_DEPENDENCIES=ON ^\n          -DCUTTER_PACKAGE_RZ_GHIDRA=ON ^\n          -DCUTTER_PACKAGE_RZ_LIBSWIFT=ON ^\n          -DCUTTER_PACKAGE_RZ_LIBYARA=ON ^\n          -DCUTTER_PACKAGE_RZ_SILHOUETTE=ON ^\n          -DCUTTER_PACKAGE_JSDEC=ON ^\n          -DCUTTER_ENABLE_DEPENDENCY_DOWNLOADS=ON ^\n          -DCMAKE_PREFIX_PATH=\"%CUTTER_DEPS%\\pyside\" ^\n          -DCPACK_PACKAGE_FILE_NAME=%PACKAGE_NAME% ^\n          -G Ninja ^\n          ..\n        cmake --build . --config Release\n        cmake --build . --config Release --target package\n        echo PACKAGE_NAME=%PACKAGE_NAME%.zip >> %GITHUB_ENV%\n        echo PACKAGE_PATH=build/%PACKAGE_NAME%.zip >> %GITHUB_ENV%\n        echo UPLOAD_ASSET_TYPE=application/zip >> %GITHUB_ENV%\n    - uses: actions/upload-artifact@v4\n      if: env.PACKAGE_NAME != null\n      with:\n        name: ${{ env.PACKAGE_NAME }}\n        path: ${{ env.PACKAGE_PATH }}\n    - name: Get release\n      if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')\n      id: get_release\n      uses: rizinorg/gha-get-release@c8074dd5d13ddd0a194d8c9205a1466973c7dc0d\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    - name: Upload release assets\n      if: steps.get_release.outputs.upload_url != null && env.PACKAGE_NAME != null\n      uses: actions/upload-release-asset@v1.0.2\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      with:\n          upload_url: ${{ steps.get_release.outputs.upload_url }}\n          asset_path: ${{ env.PACKAGE_PATH }}\n          asset_name: ${{ env.PACKAGE_NAME }}\n          asset_content_type: ${{ env.UPLOAD_ASSET_TYPE }}\n\n    - name: Set output\n      if: matrix.output-id != ''\n      id: output_setup\n      shell: bash\n      run: |\n        echo \"${{ matrix.output-id }}=${PACKAGE_NAME}\" >> \"$GITHUB_OUTPUT\"\n    outputs:\n      artifact_macos: ${{ steps.output_setup.outputs.artifact_macos }}\n      artifact_windows: ${{ steps.output_setup.outputs.artifact_windows }}\n\n  plugin-test:\n    name: plugin-test-${{ matrix.name }}\n    runs-on: ${{ matrix.os }}\n    needs: [build-linux, build]\n    strategy:\n      matrix:\n        name: [\n            linux-x86_64,\n            macos-arm64,\n            windows,\n        ]\n        include:\n          - name: linux-x86_64 \n            os: ubuntu-22.04\n            artifact-job: build-linux\n            artifact-output: artifact_linux\n          - name: macos-arm64\n            os: macos-14\n            artifact-job: build\n            artifact-output: artifact_macos\n          - name: windows\n            os: windows-2022\n            artifact-job: build\n            artifact-output: artifact_windows\n      fail-fast: false\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        path: 'cutter-src-tmp'\n        persist-credentials: false\n     # Only needed for QT, in theory could use qt that's sourced in other way.\n     # But that introduces risk of problems caused by incompatibility between the two versions.\n    - name: Cutter deps \n      shell: bash\n      run: |\n        echo \"PACKAGE_NAME=${{needs[matrix.artifact-job].outputs[matrix.artifact-output] || 'bad_id'}}\" >> $GITHUB_ENV\n        ls -alh\n        pwd\n        ./cutter-src-tmp/scripts/fetch_deps.sh\n        cp -r ./cutter-src-tmp/src/plugins/sample-cpp .\n        rm -r ./cutter-src-tmp/cutter-deps/python/ || true # copying python folder on macOs doesn't work well, it's not needed for plugin compilation\n        cp -r ./cutter-src-tmp/cutter-deps .\n        rm -r cutter-src-tmp\n        ls -alh\n        pwd\n\n    - name: Download cutter build\n      uses: actions/download-artifact@v4\n      with:\n        name: ${{ needs[matrix.artifact-job].outputs[matrix.artifact-output] || 'bad_id2' }}\n    - name: build-linux\n      if: contains(matrix.name, 'linux')\n      run: |\n        sudo apt-get -y install libxcb1-dev \\\n                    libxkbcommon-dev \\\n                    libxcb-*-dev \\\n                    libegl1 \\\n                    mesa-common-dev\n        ls\n        chmod +x ./$PACKAGE_NAME\n        ./$PACKAGE_NAME --appimage-extract\n\n        cd sample-cpp\n        mkdir build\n        cmake -B build -DCMAKE_PREFIX_PATH=\"$PWD/../squashfs-root/usr/;$PWD/../cutter-deps/qt\" --debug-find-pkg=Qt6Widgets -DQT_DEBUG_FIND_PACKAGE=ON\n        cmake --build build\n    - name: build-macos\n      if: contains(matrix.name, 'macos')\n      run: |\n        hdiutil attach $PACKAGE_NAME\n        ls /Volumes/Cutter/\n        cd sample-cpp\n        mkdir build\n        cmake -B build -DCMAKE_PREFIX_PATH=\"/Volumes/Cutter/Cutter.app/Contents/Resources;../cutter-deps/qt\"\n        cmake --build build\n    - name: Build windows\n      if: contains(matrix.name, 'windows')\n      shell: cmd\n      run: |\n        7z.exe x %PACKAGE_NAME%\n        ren %PACKAGE_NAME:~0,-4% cutter\n        dir\n        cd sample-cpp\n        mkdir build\n        cmake -B build -DCMAKE_PREFIX_PATH=\"%CD%/../cutter;%CD%/../cutter-deps/qt\"\n        cmake --build build\n"
  },
  {
    "path": ".github/workflows/coverity-scan.yml",
    "content": "name: coverity-scan\non:\n  schedule:\n    - cron: \"0 9 * * 1,5\" # Bi-weekly at 09:00 UTC on Monday and Thursday\n\njobs:\n  latest:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n      - uses: actions/setup-python@v5\n        with:\n          python-version: 3.9.x\n\n      - name: Download Coverity Build Tool\n        run: |\n          wget -q https://scan.coverity.com/download/cxx/linux64 --post-data \"token=$TOKEN&project=rizinorg%2Fcutter\" -O cov-analysis-linux64.tar.gz\n          mkdir cov-analysis-linux64\n          tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64\n        env:\n          TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}\n\n      - name: Fixed world writable dirs\n        run: |\n          chmod go-w $HOME\n          sudo chmod -R go-w /usr/share\n\n      - name: apt dependencies\n        run: sudo apt-get install ninja-build libgraphviz-dev mesa-common-dev\n\n      - name: py dependencies\n        run: |\n          pip install meson\n\n      - name: CMake\n        run: |\n          scripts/fetch_deps.sh\n          source cutter-deps/env.sh\n          export LD_LIBRARY_PATH=\"`llvm-config --libdir`:$LD_LIBRARY_PATH\"\n          mkdir build\n          cd build\n          cmake ..\n\n      - name: Build with cov-build\n        run: |\n          export PATH=`pwd`/cov-analysis-linux64/bin:$PATH\n          cd build\n          cov-build --dir cov-int make\n\n      - name: Submit the result to Coverity Scan\n        run: |\n          cd build\n          tar czvf cutter.tgz cov-int\n          curl \\\n            --form project=radareorg-cutter \\\n            --form token=$TOKEN \\\n            --form email=noreply@radare.org \\\n            --form file=@cutter.tgz \\\n            --form version=trunk \\\n            --form description=\"Cutter\" \\\n            https://scan.coverity.com/builds?project=radareorg%2Fcutter\n        env:\n          TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "name: Docs\n\non:\n  push:\n    branches:\n      - dev\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n      - name: install dependencies\n        run: |\n          sudo apt-get install -y doxygen\n          pip install -U sphinx breathe sphinx-rtd-theme recommonmark\n      - name: build docs\n        run: |\n          export PATH=\"/home/runner/.local/bin:$PATH\"\n          cd docs\n          make html\n          cd ..\n      - name: deploy docs\n        run: |\n          openssl aes-256-cbc -K ${{ secrets.CUTTER_DOCS_KEY }} -iv ${{ secrets.CUTTER_DOCS_IV }} -in scripts/deploy_docs_rsa.enc -out scripts/deploy_docs_rsa -d\n          chmod 600 scripts/deploy_docs_rsa\n          export GIT_SSH_COMMAND=\"/usr/bin/ssh -i $PWD/scripts/deploy_docs_rsa\"\n          git config --global user.name \"Github Actions\"\n          git config --global user.email \"actions@github.com\"\n          bash scripts/deploy_docs.sh\n"
  },
  {
    "path": ".github/workflows/linter.yml",
    "content": "name: \"Linter\"\n\non:\n  push:\n    branches:\n    - dev\n    - stable\n  pull_request:\n    branches:\n    - dev\n    - stable\n\njobs:\n  changes:\n    runs-on: ubuntu-latest\n    outputs:\n      clang-format: ${{ steps.filter.outputs.clang-format }}\n    steps:\n    - uses: actions/checkout@v4\n    - uses: dorny/paths-filter@v2\n      id: filter\n      with:\n        filters: |\n          clang-format:\n            - '**.cpp'\n            - '**.c'\n            - '**.h'\n            - '.github/workflows/linter.yml'\n            - 'scripts/clang-format.py'\n            - '_clang-format'\n\n  clang-format:\n    needs: changes\n    runs-on: ubuntu-22.04\n    if: ${{ needs.changes.outputs.clang-format == 'true' }}\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v4\n\n    - name: Install wget, software-properties-common, lsb-release (dependencies of LLVM install script)\n      run: sudo apt --assume-yes install wget software-properties-common lsb-release\n\n    - name: Uninstall old conflicting packages\n      run: sudo apt purge --assume-yes --auto-remove llvm python3-lldb-14 llvm-14\n\n    - name: Install automatic LLVM 16\n      run: wget https://apt.llvm.org/llvm.sh -O /tmp/llvm-install.sh; chmod +x /tmp/llvm-install.sh; sudo /tmp/llvm-install.sh 16\n\n    - name: Install clang-format-16\n      run: sudo apt --assume-yes install clang-format-16\n\n    - name: Install gitpython\n      run: sudo pip install gitpython\n\n    - name: Run clang-format\n      run: |\n        sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-16 160\n        clang-format --version\n        python scripts/clang-format.py --check --verbose\n"
  },
  {
    "path": ".gitignore",
    "content": "# OSX files\n*.DS_Store\n.AppleDouble\n.LSOverride\n\n# C++ objects and libs\n*.slo\n*.lo\n*.o\n*.a\n*.la\n*.lai\n*.so\n*.dll\n*.dylib\n\n# Qt files\n*.qmake.cache\n*.qmake.stash\n*.pro.user\n*.pro.user.*\n*.qbs.user\n*.qbs.user.*\n*.moc\nmoc_*.cpp\nqrc_*.cpp\nmoc_*.h\nui_*.h\n/build*/\ncmake-build-*/\n/src-build/\n\n# Build folders\nDebug\nRelease\n\n# QtCreator\n*.autosave\n*.qmlproject.user\n*.qmlproject.user.*\nCMakeLists.txt.user\n\n# CLion\n.idea\n\n# Vim\n*.swp\n*~\n\n# Git mergetool\n~.orig\n\n# CMake Files:\nsrc/*_automoc.cpp\n*CMakeCache.txt*\n*cmake_install.cmake*\nsrc/CMakeFiles/*\nCMakeSettings.json\n\n# Prepare_r2\nninja.exe\nr2_dist_x86/\nr2_dist_x64/\nr2_dist/\n*.pdb\n\n# Mesonbuild\nsrc/subprojects/\n\n# Python\n__pycache__\n/python/\n\n# Other\ncompile_commands.json\n.ccls-cache\n.cache\ndocs/source/_build\n\n# vscode\n/.vscode/*\n!/.vscode/settings.json\n!/.vscode/extensions.json\n\n# Visual Studio CMake\n/out\n.vs\n\n# cutter-deps\n/cutter-deps\n\n/breakpad\n\n# Local gdb files\n.gdb_history\n.gdbinit\n\n# Kdevelop\n.kdev/\n*.kdev4\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"rizin\"]\n\tpath = rizin\n\turl = https://github.com/rizinorg/rizin\n[submodule \"src/translations\"]\n\tpath = src/translations\n\turl = https://github.com/rizinorg/cutter-translations\n"
  },
  {
    "path": ".lgtm.yml",
    "content": "extraction:\n  cpp:\n    prepare:\n      packages:\n      - \"build-essential\"\n      - \"cmake\"\n      - \"libzip-dev\"\n      - \"zlib1g-dev\"\n      - \"qt5-default\"\n      - \"libqt5svg5-dev\"\n      - \"qttools5-dev\"\n      - \"qttools5-dev-tools\"\n    configure:\n      command:\n      - \"pip3 install --upgrade --user meson\"\n      - \"cmake -B build\"\n    index:\n      build_command: \"cmake --build build\""
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n\t\"recommendations\": [\n\t\t\"ms-vscode.cpptools\",\n\t\t\"ms-vscode.cmake-tools\"\n\t]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"cmake.sourceDirectory\": \"${workspaceFolder}\"\n}"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.12)\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake\")\ninclude(DisallowInSource)\ninclude(Utils)\n\nset(CUTTER_PYTHON_MIN 3.5)\n\noption(CUTTER_USE_BUNDLED_RIZIN \"Use rizin from ./rizin submodule instead of searching for it on the system\" ON)\noption(CUTTER_USE_ADDITIONAL_RIZIN_PATHS \"Search rizin in additional paths which are not part of default system library paths.\\\n Disable this option if you are linking against rizin pacakged as proper system library or in a custom path and additional are paths causing problems.\" ON)\noption(CUTTER_ENABLE_PYTHON \"Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}.\" OFF)\noption(CUTTER_ENABLE_PYTHON_BINDINGS \"Enable generating Python bindings with Shiboken. Unused if CUTTER_ENABLE_PYTHON=OFF.\" OFF)\noption(CUTTER_APPIMAGE_BUILD \"Enable Appimage specific changes. Doesn't cause building of Appimage itself.\" OFF)\ntri_option(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING \"Use KSyntaxHighlighting\" AUTO)\ntri_option(CUTTER_ENABLE_GRAPHVIZ \"Enable use of graphviz for graph layout\" AUTO)\nset(SHIBOKEN_EXTRA_OPTIONS \"\" CACHE STRING \"Extra options for shiboken generator\")\nset(CUTTER_EXTRA_PLUGIN_DIRS \"\" CACHE STRING \"List of addition plugin locations\")\noption(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS \"Enable downloading of dependencies. Setting to OFF doesn't affect any downloads done by rizin build.\" OFF)\noption(CUTTER_ENABLE_PACKAGING \"Enable building platform-specific packages for distributing\" OFF)\noption(CUTTER_ENABLE_SIGDB \"Downloads and installs sigdb (only available when CUTTER_USE_BUNDLED_RIZIN=ON).\" OFF)\noption(CUTTER_PACKAGE_DEPENDENCIES \"During install step include the third party dependencies.\" OFF)\noption(CUTTER_PACKAGE_RZ_GHIDRA \"Compile and install rz-ghidra during install step.\" OFF)\noption(CUTTER_PACKAGE_RZ_LIBSWIFT \"Compile and install rz-libswift demangler during the install step.\" OFF)\noption(CUTTER_PACKAGE_RZ_LIBYARA \"Compile and install rz-libyara during the install step.\" OFF)\noption(CUTTER_PACKAGE_RZ_SILHOUETTE \"Compile and install rz-silhouette during the install step.\" OFF)\noption(CUTTER_PACKAGE_JSDEC \"Compile and install jsdec during install step.\" OFF)\nset(\"CUTTER_QT\" 6 CACHE STRING \"Major QT version to use 5|6\")\nset_property(CACHE \"CUTTER_QT\" PROPERTY STRINGS 5 6)\noption(CUTTER_INCLUDE_GIT_HASH \"Include git hash in full version\" ON)\nset(CUTTER_VERSION_SUFFIX \"\" CACHE STRING \"Can be used by packagers to differentiate multiple packages using same source, for example build number or presence of additional patches.\")\n\nif(NOT CUTTER_ENABLE_PYTHON)\n    set(CUTTER_ENABLE_PYTHON_BINDINGS OFF)\nendif()\n\nset(CUTTER_VERSION_MAJOR 2)\nset(CUTTER_VERSION_MINOR 4)\nset(CUTTER_VERSION_PATCH 0)\n\nset(CUTTER_VERSION \"${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}\")\n\n\nif (CUTTER_INCLUDE_GIT_HASH)\n    execute_process(COMMAND git  -C \"${CMAKE_CURRENT_SOURCE_DIR}\" --git-dir=.git log --pretty=format:'%h' -n 1\n                    OUTPUT_VARIABLE GIT_REV\n                    ERROR_QUIET)\nendif()\n\n# Check whether we got any revision (which isn't\n# always the case, e.g. when someone downloaded a zip file\nif (NOT CUTTER_INCLUDE_GIT_HASH OR \"${GIT_REV}\" STREQUAL \"\")\n    set(CUTTER_VERSION_FULL \"${CUTTER_VERSION}${CUTTER_VERSION_SUFFIX}\")\nelse()\n    execute_process(\n        COMMAND git -C \"${CMAKE_CURRENT_SOURCE_DIR}\" --git-dir=.git rev-parse --abbrev-ref HEAD\n        OUTPUT_VARIABLE GIT_BRANCH)\n    string(STRIP \"${GIT_REV}\" GIT_REV)\n    string(SUBSTRING \"${GIT_REV}\" 1 7 GIT_REV)\n    string(STRIP \"${GIT_BRANCH}\" GIT_BRANCH)\n    set(CUTTER_VERSION_FULL \"${CUTTER_VERSION}${CUTTER_VERSION_SUFFIX}-${GIT_BRANCH}-${GIT_REV}\")\nendif()\n\nproject(Cutter VERSION \"${CUTTER_VERSION}\")\n\n# Enable solution folder support\nset_property(GLOBAL PROPERTY USE_FOLDERS ON)\n\n# Put Qt files in a separate folder\nset_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP \"Generated Files\")\n\nset(CMAKE_CXX_STANDARD 20)\n\ninclude(CutterInstallDirs)\n\nif(CUTTER_USE_BUNDLED_RIZIN)\n    include(BundledRizin)\n    set(RIZIN_TARGET Rizin)\nelse()\n    find_package(Rizin COMPONENTS Core REQUIRED)\n    set(RIZIN_TARGET Rizin::Core)\nendif()\n\nset(CMAKE_INCLUDE_CURRENT_DIR ON)\nset(CMAKE_AUTOMOC ON)\nset(CMAKE_AUTOUIC ON)\nset(CMAKE_AUTORCC ON)\nset(QT_COMPONENTS Core Widgets Gui Svg Network)\nif (CUTTER_QT GREATER_EQUAL 6)\n    list(APPEND QT_COMPONENTS Core5Compat SvgWidgets OpenGLWidgets)\nendif()\nset(QT_PREFIX \"Qt${CUTTER_QT}\")\nfind_package(${QT_PREFIX} REQUIRED COMPONENTS ${QT_COMPONENTS})\n\nif(CUTTER_ENABLE_PYTHON)\n    find_package (Python3 ${CUTTER_PYTHON_MIN} REQUIRED COMPONENTS Interpreter Development)\n    set (PYTHON_EXECUTABLE ${Python3_EXECUTABLE})\n\n    add_definitions(-DCUTTER_ENABLE_PYTHON)\n\n    if(CUTTER_ENABLE_PYTHON_BINDINGS)\n        if (CUTTER_QT GREATER_EQUAL 6)\n            # 6.12.3 => 6.12\n            if(\"${Qt6_VERSION}\" MATCHES \"^([0-9]+\\\\.[0-9]+)\\\\.[0-9]+\")\n                set(Shiboken6_VERSION_REQUIRED \"${CMAKE_MATCH_1}\")\n            else()\n                message(FATAL_ERROR \"Failed to recognize Qt version\")\n            endif()\n            find_package(Shiboken6 \"${Shiboken6_VERSION_REQUIRED}\" REQUIRED)\n            find_package(Shiboken6Tools \"${Shiboken6_VERSION_REQUIRED}\" REQUIRED)\n            find_package(PySide6 \"${Shiboken6_VERSION_REQUIRED}\" REQUIRED)\n            get_target_property(LIBSHIBOKEN_INCLUDE_DIRS Shiboken6::libshiboken INTERFACE_INCLUDE_DIRECTORIES)\n            get_target_property(PYSIDE_INCLUDE_DIRS PySide6::pyside6 INTERFACE_INCLUDE_DIRECTORIES)\n            # Check the presence of \"pysidecleanup.h\"\n            include(CheckIncludeFileCXX)\n            set(CMAKE_REQUIRED_INCLUDES \"${PYSIDE_INCLUDE_DIRS};${LIBSHIBOKEN_INCLUDE_DIRS}\")\n            CHECK_INCLUDE_FILE_CXX(\"pysidecleanup.h\" HAVE_PYSIDECLEANUP)\n            add_compile_definitions(\"HAVE_PYSIDECLEANUP=${HAVE_PYSIDECLEANUP}\")\n        elseif(CUTTER_QT EQUAL 5)\n            # 5.12.3 => 5.12\n            if(\"${Qt5_VERSION}\" MATCHES \"^([0-9]+\\\\.[0-9]+)\\\\.[0-9]+\")\n                set(Shiboken2_VERSION_REQUIRED \"${CMAKE_MATCH_1}\")\n            else()\n                message(FATAL_ERROR \"Failed to recognize Qt version\")\n            endif()\n            find_package(Shiboken2 \"${Shiboken2_VERSION_REQUIRED}\" REQUIRED)\n            find_package(PySide2 \"${Shiboken2_VERSION_REQUIRED}\" REQUIRED)\n            get_target_property(PYSIDE_INCLUDE_DIRS PySide2::pyside2 INTERFACE_INCLUDE_DIRECTORIES)\n        else()\n            message(FATAL_ERROR \"Unsupported QT version ${CUTTER_QT}\")\n        endif()\n\n        foreach(_dir IN LISTS PYSIDE_INCLUDE_DIRS)\n            include_directories(${_dir}\n                ${_dir}/QtCore\n                ${_dir}/QtGui\n                ${_dir}/QtWidgets)\n        endforeach()\n\n        add_definitions(-DCUTTER_ENABLE_PYTHON_BINDINGS)\n    endif()\nendif()\n\nif(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING)\n    if(CUTTER_ENABLE_KSYNTAXHIGHLIGHTING STREQUAL AUTO)\n        find_package(KF${CUTTER_QT}SyntaxHighlighting)\n        if(KF${CUTTER_QT}SyntaxHighlighting_FOUND)\n            set(KSYNTAXHIGHLIGHTING_STATUS ON)\n        else()\n            set(KSYNTAXHIGHLIGHTING_STATUS \"OFF (KSyntaxHighlighting not found)\")\n        endif()\n    else()\n        find_package(KF${CUTTER_QT}SyntaxHighlighting REQUIRED)\n        set(KSYNTAXHIGHLIGHTING_STATUS ON)\n    endif()\nendif()\n\nif (CUTTER_ENABLE_GRAPHVIZ)\n    if (CUTTER_ENABLE_GRAPHVIZ STREQUAL AUTO)\n        find_package(Graphviz)\n    else()\n        find_package(Graphviz REQUIRED)\n    endif()\n    set (CUTTER_ENABLE_GRAPHVIZ ${Graphviz_FOUND})\nendif()\n\nmessage(STATUS \"\")\nmessage(STATUS \"Building Cutter version ${CUTTER_VERSION_FULL}\")\nmessage(STATUS \"Options:\")\nmessage(STATUS \"- Bundled rizin: ${CUTTER_USE_BUNDLED_RIZIN}\")\nif(CUTTER_USE_BUNDLED_RIZIN)\n    message(STATUS \"- Bundled sigdb: ${CUTTER_ENABLE_SIGDB}\")\nendif()\nmessage(STATUS \"- Python: ${CUTTER_ENABLE_PYTHON}\")\nmessage(STATUS \"- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}\")\nmessage(STATUS \"- KSyntaxHighlighting: ${KSYNTAXHIGHLIGHTING_STATUS}\")\nmessage(STATUS \"- Graphviz: ${CUTTER_ENABLE_GRAPHVIZ}\")\nmessage(STATUS \"- Downloads dependencies: ${CUTTER_ENABLE_DEPENDENCY_DOWNLOADS}\")\nmessage(STATUS \"- Enable Packaging: ${CUTTER_ENABLE_PACKAGING}\")\nmessage(STATUS \"- Package Dependencies: ${CUTTER_PACKAGE_DEPENDENCIES}\")\nmessage(STATUS \"- Package RzGhidra: ${CUTTER_PACKAGE_RZ_GHIDRA}\")\nmessage(STATUS \"- Package RzLibSwift: ${CUTTER_PACKAGE_RZ_LIBSWIFT}\")\nmessage(STATUS \"- Package RzLibYara: ${CUTTER_PACKAGE_RZ_LIBYARA}\")\nmessage(STATUS \"- Package RzSilhouette: ${CUTTER_PACKAGE_RZ_SILHOUETTE}\")\nmessage(STATUS \"- Package JSDec: ${CUTTER_PACKAGE_JSDEC}\")\nmessage(STATUS \"- QT: ${CUTTER_QT}\")\nmessage(STATUS \"\")\n\nif (CUTTER_QT LESS 5 OR CUTTER_QT GREATER 6)\n    message(FATAL_ERROR \"Unsupported QT major version\")\nendif()\n\nadd_subdirectory(src)\n\nif(CUTTER_ENABLE_PACKAGING)\n\tadd_subdirectory(dist)\nendif()\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Cutter\n\nThere are different ways you can help contributing to Cutter:\n\n## Opening an issue\n\nMake a clear description of the bug/feature, use screenshots, send binaries, etc.\nThis will help us improve the software for you.\nYou can create an issue by clicking on [this link](https://github.com/rizinorg/cutter/issues/new/choose).\n\n## Contributing to the code\n\nNote that cutter is still under development and many parts of the code are to be improved.\nThe best way is to check the opened issues [here](https://github.com/rizinorg/cutter/issues) or discuss with rizinorg team.\nPlease follow our contribution guidelines: https://cutter.re/docs/contributing.html\n\n## Contributing to the documentation\n\nThe documentation is something important for newcomers. As of today the documentation can be found [here](https://cutter.re/docs/) and it stands in the [docs](docs) folder.\nThe API Reference is automatically generated from the source code, so it is strongly advised to document your code.\nCheck issues marked as \"Documentation\" on our issues [list](https://github.com/rizinorg/cutter/issues?q=is%3Aissue+is%3Aopen+label%3ADocumentation).\n\n## Translations\n\nYou can help Cutter by adding translations to the project! We use the [Crowdin](https://crowdin.com/project/cutter) platform to help us share translations. Feel free to contribute and add translations to the project. If you need to add a language, ask any rizinorg developer.\n\n## Usage of AI tools\n\nFollowing the widespread availability of large language models and generative AI, Rizin Organization has received a growing number of changes generated partially or entirely using such tools. Many of these are completely unusable in our codebase.\nWhile AI tools can help to draft changes, they must not replace human understanding and proper code modifications.\n\nIf you use AI tools to help prepare a code change, you must:\n\n- **Disclose** which AI tools were used and specify what they were used for.\n- **Verify** that the code compiles, works and is not copyrighted by somebody else.\n- **Avoid** fabricated code, placeholder text, or references to non-existent code.\n\nChanges that appear to be unverified AI output will be closed without response.\nRepeated low-quality submissions may result in a ban.\n\nWe align with similar policies adopted by other major open-source projects, which have described the flood of unverified AI-generated code changes as disruptive, counterproductive, and a drain on limited team resources.\n\n> [!IMPORTANT]\n> AI tools must not be used to fix issues labelled `good first issue`.\n> These issues are generally not urgent, and are intended to be learning opportunities for new contributors to get familiar with the codebase.\n> Whether you are a newcomer or not, fully automating the process of fixing this issue squanders the learning opportunity and doesn't add much value to the project.\n> **Using AI tools to fix issues labelled as \"good first issues\" is forbidden**.\n\n## Requirements for new contributors\n\nDue to the high number of AI-generated contributions,\nwe raised the standard for new contributors.\n\nYou must provide a \"before\" and \"after\" screenshot or video showing that the change fixed the issue.\n\n> [!IMPORTANT]\n> If this requirement is not met, we won't review the PR and will close it\n> if there are no visible attempts to meet it.\n\n"
  },
  {
    "path": "COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n\n"
  },
  {
    "path": "README.md",
    "content": "<img width=\"150\" height=\"150\" align=\"left\" style=\"float: left; margin: 0 10px 0 0;\" alt=\"Cutter logo\" src=\"https://raw.githubusercontent.com/rizinorg/cutter/dev/src/img/cutter.svg?sanitize=true\">\n\n# Cutter\n\nCutter is a free and open-source reverse engineering platform powered by [rizin](https://github.com/rizinorg/rizin). It aims at being an advanced and customizable reverse engineering platform while keeping the user experience in mind. Cutter is created by reverse engineers for reverse engineers.  \n\n[![Cutter CI](https://github.com/rizinorg/cutter/workflows/Cutter%20CI/badge.svg)](https://github.com/rizinorg/cutter/actions?query=workflow%3A%22Cutter+CI%22)\n[![Build status](https://ci.appveyor.com/api/projects/status/tn7kttv55b8wf799/branch/dev?svg=true)](https://ci.appveyor.com/project/rizinorg/cutter/branch/dev)\n\n![Screenshot](https://raw.githubusercontent.com/rizinorg/cutter/dev/docs/source/images/screenshot.png)\n\n## Learn more at [cutter.re](https://cutter.re).\n\n## Getting Cutter\n### Download\n\nCutter release binaries for all major platforms (Linux, macOS, Windows) can be downloaded from [GitHub Releases](https://github.com/rizinorg/cutter/releases).\n\n- **Linux**: If your distribution provides it, check for `cutter` package in your package manager (or `cutter-re` / `rz-cutter`). If not available there, we have setup repositories in [OBS](https://openbuildservice.org/) for some common distributions. Look at [https://software.opensuse.org/package/cutter-re](https://software.opensuse.org/download/package?package=cutter-re&project=home%3ARizinOrg) and follow the instructions there. Otherwise download the `.AppImage` file from our release, make it executable and run as below or use [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher).\n\n    `chmod +x Cutter*.AppImage; ./Cutter*.AppImage`\n- **macOS**: Download the `.dmg` file or use [Homebrew Cask](https://github.com/Homebrew/homebrew-cask):\n\n    `brew install --cask cutter`\n- **Windows**: Download the `.zip` archive, or use either [Chocolatey](https://chocolatey.org) or [Scoop](https://scoop.sh/):\n\n    `choco install cutter`\n    \n    `scoop bucket add extras` followed by `scoop install cutter`\n\n### Build from sources\n\nTo build Cutter from sources, please check the [Building Docs](https://cutter.re/docs/building.html).\n\n### Docker image\n\nTo deploy *cutter* using a pre-built `Dockerfile`, it's possible to use the [provided configuration](docker). The corresponding `README.md` file also contains instructions on how to get started using the docker image with minimal effort.\n\n## Documentation\n\n### [User Guide](https://cutter.re/docs/user-docs.html)\n\n### [Contribution Guidelines](https://cutter.re/docs/contributing.html)\n\n### [Developers Docs](https://cutter.re/docs/contributing/code.html)\n\n## Plugins\nCutter supports both Python and Native C++ plugins.\n\nOur community has built many plugins and useful scripts for Cutter such as the native integration of [Ghidra decompiler](https://github.com/rizinorg/rz-ghidra) or the plugin to visualize DynamoRIO code coverage. You can find a list of cutter plugins linked below. Feel free to extend it with your own plugins and scripts for Cutter.\n\n**[Official & Community Plugins](https://github.com/rizinorg/cutter-plugins)**\n\n**[Plugins Development Guide](https://cutter.re/docs/plugins.html)**\n\n## Getting Help\n\nPlease use the following channels to ask for help from Cutter developers and community:\n\n- **Telegram:** https://t.me/cutter_re\n- **Mattermost:** https://im.rizin.re\n- **IRC:** #cutter on https://web.libera.chat/\n- **Twitter:** [@cutter_re](https://twitter.com/cutter_re)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\n| Version          | Supported          |\n| ---------------- | ------------------ |\n| latest-release   | :white_check_mark: |\n| *                | :x:                |\n\n## Reporting a Vulnerability\n\nSecurity issues in the Cutter repository should be reported by email to security@cutter.re. Your email will be delivered to a small security team that will handle the report. Your email will be acknowledged within 48 hours, and you'll receive a more detailed response to your email within 72 hours indicating the next steps in handling your report.\n\nFor your convenience, we accept reports written in one of the languages listed on our [security.txt](https://cutter.re/.well-known/security.txt) page, but we prefer reports in English.\n\nIf you have not received a reply to your email within 48 hours, or have not heard from the security team for the past week, there are a few steps you can take (in order):\n\n- Directly contact [Itay Cohen](https://www.megabeets.net/about.html#contact) from the Security Team\n- Inform the team over the [public chats](https://cutter.re/#community) that you sent a message regarding a security issue.\n\n**Important:** Don't disclose any information regarding the issue itself in the public chats.\n\nPlease note that the Cutter Security team isn't handling security issues on the rizin repository.\n\n## AI generated vulnerability reports \n\nFollowing the widespread availability of large language models and generative AI, we have seen a number of security reports generated partially or entirely using such tools. Many of these contain inaccurate, misleading, or fictitious content.\nWhile AI tools can help draft or analyze reports, they must not replace human understanding and review.\n\nIf you use AI tools to help prepare a report, you must:\n\n- **Disclose** which AI tools were used and specify what they were used for (analysis, writing the description, writing the exploit, etc).\n- **Verify** that the issue describes a real, reproducible vulnerability that otherwise meets these reporting guidelines.\n- **Avoid** fabricated code, placeholder text, or references to non-existent code.\n\nReports that appear to be unverified AI output will be closed without response.\nRepeated low-quality submissions may result in a ban.\n\nFor these reasons, we decided to align with similar policies adopted by other major open-source projects, which have described the flood of unverified AI-generated reports as disruptive, counterproductive, and a drain on limited security team resources.\n"
  },
  {
    "path": "_clang-format",
    "content": "# Do not edit this file! Automatically generated using scripts/udate_clang_format.sh and scripts/_clang_format\n# See update_clang_format.sh for more information.\n# generated using clang-format version 8.0.0-3~ubuntu16.04.1 (tags/RELEASE_800/final)\n---\nLanguage:        Cpp\nAccessModifierOffset: -4\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Right\nAlignOperands:   false\nAlignTrailingComments: false\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: Yes\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:   \n  AfterClass:      true\n  AfterControlStatement: false\n  AfterEnum:       false\n  AfterFunction:   true\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     true\n  AfterUnion:      false\n  AfterExternBlock: false\n  BeforeCatch:     false\n  BeforeElse:      false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: NonAssignment\nBreakBeforeBraces: Custom\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     100\nCommentPragmas:  '^!|^:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 8\nCpp11BracedListStyle: false\nDerivePointerAlignment: false\nDisableFormat:   false\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: false\nForEachMacros:   \n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\n  - forever\n  - Q_FOREVER\n  - QBENCHMARK\n  - QBENCHMARK_ONCE\n  - CutterRzListForeach\nIncludeBlocks:   Preserve\nIncludeCategories: \n  - Regex:           '^\"(llvm|llvm-c|clang|clang-c)/'\n    Priority:        2\n  - Regex:           '^(<|\"(gtest|gmock|isl|json)/)'\n    Priority:        3\n  - Regex:           '.*'\n    Priority:        1\nIncludeIsMainRegex: '(Test)?$'\nIndentCaseLabels: false\nIndentPPDirectives: AfterHash\nIndentWidth:     4\nIndentWrappedFunctionNames: false\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: true\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 4\nObjCSpaceAfterProperty: true\nObjCSpaceBeforeProtocolList: true\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 19\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 60\nPointerAlignment: Right\nReflowComments:  true\nSortIncludes:    false\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterTemplateKeyword: false\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCpp11BracedList: true\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles:  false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStandard:        Cpp11\nStatementMacros: \n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\nTabWidth:        8\nUseTab:          Never\n...\n"
  },
  {
    "path": "cmake/BundledRizin.cmake",
    "content": "\ninclude(ExternalProject)\n\nset(RIZIN_SOURCE_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/rizin\")\nif(WIN32)\n    set(RIZIN_INSTALL_DIR \"${CMAKE_CURRENT_BINARY_DIR}\")\n    if (\"${CMAKE_GENERATOR}\" MATCHES \"Visual Studio\")\n        set(RIZIN_INSTALL_DIR \"${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>\")\n    endif()\n    set(RIZIN_INSTALL_BINPATH \".\")\n    set(MESON_OPTIONS \"--prefix=${RIZIN_INSTALL_DIR}\" \"--bindir=${RIZIN_INSTALL_BINPATH}\")\nelse()\n    set(RIZIN_INSTALL_DIR \"${CMAKE_CURRENT_BINARY_DIR}/Rizin-prefix\")\n    set(MESON_OPTIONS \"--prefix=${RIZIN_INSTALL_DIR}\" --libdir=lib)\nendif()\n\nif (CUTTER_ENABLE_PACKAGING)\n    list(APPEND MESON_OPTIONS \"-Dportable=true\")\nendif()\n\nif (CUTTER_ENABLE_SIGDB)\n    list(APPEND MESON_OPTIONS \"-Dinstall_sigdb=true\")\nendif()\n\nfind_program(MESON meson)\nif(NOT MESON)\n    message(FATAL_ERROR \"Failed to find meson, which is required to build bundled rizin\")\nendif()\n\nfind_program(NINJA ninja)\nif(NOT NINJA)\n    message(FATAL_ERROR \"Failed to find ninja, which is required to build bundled rizin\")\nendif()\n\nExternalProject_Add(Rizin-Bundled\n        SOURCE_DIR \"${RIZIN_SOURCE_DIR}\"\n        CONFIGURE_COMMAND \"${MESON}\" \"<SOURCE_DIR>\" ${MESON_OPTIONS} && \"${MESON}\" configure ${MESON_OPTIONS} --buildtype \"$<$<CONFIG:Debug>:debug>$<$<NOT:$<CONFIG:Debug>>:release>\"\n        BUILD_COMMAND \"${NINJA}\"\n        BUILD_ALWAYS TRUE\n        INSTALL_COMMAND \"${NINJA}\" install)\n\nset(Rizin_INCLUDE_DIRS \"${RIZIN_INSTALL_DIR}/include/librz\" \"${RIZIN_INSTALL_DIR}/include/librz/sdb\")\n\nadd_library(Rizin INTERFACE)\nadd_dependencies(Rizin Rizin-Bundled)\nif(NOT (${CMAKE_VERSION} VERSION_LESS \"3.13.0\"))\n    target_link_directories(Rizin INTERFACE\n        $<BUILD_INTERFACE:${RIZIN_INSTALL_DIR}/lib>\n        $<INSTALL_INTERFACE:${CMAKE_INSTALL_LIBDIR}>)\nelse()\n    link_directories(\"${RIZIN_INSTALL_DIR}/lib\")\nendif()\n\n# TODO: This version number should be fetched automatically\n# instead of being hardcoded.\nset (Rizin_VERSION 0.9)\n\nset (RZ_LIBS rz_core rz_config rz_cons rz_io rz_util rz_flag rz_mark rz_arch rz_debug\n        rz_hash rz_bin rz_lang rz_il rz_egg rz_reg rz_search rz_syscall\n        rz_socket rz_magic rz_crypto rz_type rz_diff rz_sign rz_demangler)\nset (RZ_EXTRA_LIBS rz_main)\nset (RZ_BIN rz-bin rizin rz-diff rz-find rz-gg rz-hash rz-run rz-asm rz-ax rz-sign)\n\ntarget_link_libraries(Rizin INTERFACE\n        ${RZ_LIBS})\ntarget_include_directories(Rizin INTERFACE\n    \"$<BUILD_INTERFACE:${Rizin_INCLUDE_DIRS}>\"\n    \"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/librz>\"\n    \"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/librz/sdb>\")\n\ninstall(TARGETS Rizin EXPORT CutterTargets)\nif (WIN32)\n    foreach(_lib ${RZ_LIBS} ${RZ_EXTRA_LIBS})\n        install(FILES \"${RIZIN_INSTALL_DIR}/${_lib}-${Rizin_VERSION}.dll\" DESTINATION \"${CMAKE_INSTALL_BINDIR}\")\n    endforeach()\n    foreach(_exe ${RZ_BIN})\n        install(FILES \"${RIZIN_INSTALL_DIR}/${_exe}.exe\" DESTINATION \"${CMAKE_INSTALL_BINDIR}\")\n    endforeach()\n    install(DIRECTORY \"${RIZIN_INSTALL_DIR}/share\" DESTINATION \".\")\n    install(DIRECTORY \"${RIZIN_INSTALL_DIR}/include\" DESTINATION \".\"\n        COMPONENT Devel)\n    install(DIRECTORY \"${RIZIN_INSTALL_DIR}/lib\" DESTINATION \".\"\n        COMPONENT Devel\n        PATTERN \"*.pdb\" EXCLUDE)\nelse ()\n    install(DIRECTORY \"${RIZIN_INSTALL_DIR}/\" DESTINATION \".\" USE_SOURCE_PERMISSIONS\n            PATTERN \"rz-test\" EXCLUDE)\nendif()\n"
  },
  {
    "path": "cmake/CutterConfig.cmake.in",
    "content": "@PACKAGE_INIT@\n\nset(Cutter_RIZIN_BUNDLED @CUTTER_USE_BUNDLED_RIZIN@)\n\ninclude(CMakeFindDependencyMacro)\nfind_dependency(@QT_PREFIX@ COMPONENTS @QT_COMPONENTS@)\nfind_dependency(Rizin COMPONENTS Core)\n\n# Make a best guess for a user location from where plugins can be loaded.\n# This can be used in Cutter plugins like\n# set(CUTTER_INSTALL_PLUGDIR \"${Cutter_USER_PLUGINDIR}\" CACHE STRING \"Directory to install Cutter plugin into\")\n# see https://doc.qt.io/qt-5/qstandardpaths.html under AppDataLocation\nif(APPLE)\n    set(Cutter_USER_PLUGINDIR \"$ENV{HOME}/Library/Application Support/rizin/cutter/plugins/native\")\nelseif(WIN32)\n    file(TO_CMAKE_PATH \"$ENV{APPDATA}\" Cutter_USER_PLUGINDIR)\n    set(Cutter_USER_PLUGINDIR \"${Cutter_USER_PLUGINDIR}/rizin/cutter/plugins/native\")\nelse()\n    set(Cutter_USER_PLUGINDIR \"$ENV{HOME}/.local/share/rizin/cutter/plugins/native\")\nendif()\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/CutterTargets.cmake\")\n"
  },
  {
    "path": "cmake/CutterInstallDirs.cmake",
    "content": "set(CUTTER_DIR_NAME \"rizin/cutter\")\nif(WIN32)\n    set(CMAKE_INSTALL_BINDIR \".\" CACHE PATH \"Executable install directory\")\n    set(CMAKE_INSTALL_INCLUDEDIR \"include\" CACHE PATH \"Include install directory\")\n    set(CMAKE_INSTALL_LIBDIR \"lib\" CACHE PATH \"Library install directory\")\n    set(CMAKE_INSTALL_DATAROOTDIR \"./\" CACHE PATH \"Resource installation directory\")\n    set(CUTTER_INSTALL_DATADIR \"${CMAKE_INSTALL_DATAROOTDIR}\" CACHE PATH \"Resource installation directory\")\nelseif(APPLE)\n    if (CUTTER_ENABLE_PACKAGING)\n        set(CMAKE_INSTALL_INCLUDEDIR \"include\" CACHE PATH \"Include install directory\")\n        set(CMAKE_INSTALL_LIBDIR \"lib\" CACHE PATH \"Library install directory\")\n        set(CMAKE_INSTALL_DATAROOTDIR \"./\" CACHE PATH \"Resource installation directory\")\n        set(CMAKE_INSTALL_BINDIR \"../MacOS\" CACHE PATH \"Executable install directory\") # BUNDLE step sets prefix to Resources\n        set(CUTTER_INSTALL_DATADIR \"${CMAKE_INSTALL_DATAROOTDIR}\" CACHE PATH \"Resource installation directory\")\n    else()\n        include(GNUInstallDirs)\n        set(CUTTER_INSTALL_DATADIR \"${CMAKE_INSTALL_DATAROOTDIR}/${CUTTER_DIR_NAME}\" CACHE PATH \"Resource installation directory\")\n    endif()\nelse()\n    include(GNUInstallDirs)\n    set(CUTTER_INSTALL_DATADIR \"${CMAKE_INSTALL_DATAROOTDIR}/${CUTTER_DIR_NAME}\" CACHE PATH \"Resource installation directory\")\nendif()\nset(CUTTER_INSTALL_CONFIGDIR \"${CMAKE_INSTALL_LIBDIR}/cmake/Cutter\" CACHE PATH \"CMake file install location\")\n"
  },
  {
    "path": "cmake/DisallowInSource.cmake",
    "content": "function(DisallowInSource)\n\tget_filename_component(SRC_DIR_REALPATH \"${CMAKE_SOURCE_DIR}\" REALPATH)\n\tget_filename_component(BINARY_DIR_REALPATH \"${CMAKE_BINARY_DIR}\" REALPATH)\n\tif(SRC_DIR_REALPATH STREQUAL BINARY_DIR_REALPATH)\n\t\tmessage(FATAL_ERROR \" In-source builds are not allowed.\n Please create a directory and run cmake from there:\n mkdir build && cd build && cmake ..\n This process created the file CMakeCache.txt and the directory CMakeFiles in ${CMAKE_SOURCE_DIR}.\n Please delete them manually!\")\n\tendif()\nendfunction()\n\nDisallowInSource()"
  },
  {
    "path": "cmake/FindGraphviz.cmake",
    "content": "set (_module Graphviz)\n\nfind_package(PkgConfig)\nif (PkgConfig_FOUND)\n    if (NOT (CMAKE_VERSION VERSION_LESS \"3.12.0\"))\n        pkg_check_modules(GVC IMPORTED_TARGET GLOBAL libgvc)\n    elseif (NOT (CMAKE_VERSION VERSION_LESS \"3.11.0\"))\n        pkg_check_modules(GVC IMPORTED_TARGET libgvc)\n    else()\n        pkg_check_modules(GVC libgvc)\n    endif()\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(${_module}\n        FOUND_VAR ${_module}_FOUND\n        REQUIRED_VARS GVC_INCLUDE_DIRS)\n\nif (${GVC_FOUND})\n    if (CMAKE_VERSION VERSION_LESS \"3.11.0\")\n        add_library(${_module}::GVC INTERFACE IMPORTED)\n        set_target_properties(${_module}::GVC PROPERTIES\n                INTERFACE_INCLUDE_DIRECTORIES \"${GVC_INCLUDE_DIRS}\")\n        set_target_properties(${_module}::GVC PROPERTIES\n                INTERFACE_LINK_LIBRARIES \"${GVC_LIBRARIES}\")\n    else()\n       add_library(${_module}::GVC ALIAS PkgConfig::GVC)\n    endif()\nendif()\n"
  },
  {
    "path": "cmake/FindPySide2.cmake",
    "content": "\nset(_module PySide2)\n\nfind_package(${_module} ${${_module}_FIND_VERSION} CONFIG QUIET)\nset(_lib_target ${_module}::pyside2)\n\nif(NOT ${_module}_FOUND)\n    include(PythonInfo)\n    find_python_site_packages(PYTHON_SITE_PACKAGES)\n    get_python_extension_suffix(PYTHON_EXTENSION_SUFFIX)\n\n    find_library(PYSIDE_LIBRARY\n            NAMES\n                \"pyside2${PYTHON_EXTENSION_SUFFIX}\"\n                \"pyside2${PYTHON_EXTENSION_SUFFIX}.${${_module}_FIND_VERSION_MAJOR}.${${_module}_FIND_VERSION_MINOR}\"\n            PATH_SUFFIXES \"${PYTHON_SITE_PACKAGES}/PySide2\")\n\n    find_path(PYSIDE_INCLUDE_DIR\n            pyside.h\n            PATH_SUFFIXES \"${PYTHON_SITE_PACKAGES}/PySide2/include\")\n\n    find_path(PYSIDE_TYPESYSTEMS\n            typesystem_core.xml\n            PATH_SUFFIXES \"${PYTHON_SITE_PACKAGES}/PySide2/typesystems\")\nendif()\n\nif(TARGET ${_lib_target})\n    get_target_property(_is_imported ${_lib_target} IMPORTED)\n    if(_is_imported)\n        get_target_property(_imported_location ${_lib_target} IMPORTED_LOCATION)\n        if(NOT _imported_location)\n            message(STATUS \"Target ${_lib_target} does not specify its IMPORTED_LOCATION! Trying to find it ourselves...\")\n            set(_find_args)\n            if(${_module}_CONFIG)\n                get_filename_component(_pyside2_lib_dir \"${${_module}_CONFIG}/../../../\" ABSOLUTE)\n                set(_find_args PATHS \"${_pyside2_lib_dir}\")\n            endif()\n            find_library(PYSIDE_LIBRARY\n                    NAMES\n                        \"pyside2${PYTHON_CONFIG_SUFFIX}\"\n                        \"pyside2${PYTHON_CONFIG_SUFFIX}.${${_module}_FIND_VERSION_MAJOR}.${${_module}_FIND_VERSION_MINOR}\"\n                    ${_find_args})\n            if(NOT PYSIDE_LIBRARY)\n                set(_message_type WARNING)\n                if(${_module}_FIND_REQUIRED)\n                    set(_message_type FATAL_ERROR)\n                endif()\n                message(${_message_type} \"Failed to manually find library for ${_module}\")\n                return()\n            endif()\n            message(STATUS \"IMPORTED_LOCATION for ${_lib_target} found: ${PYSIDE_LIBRARY}\")\n            set_target_properties(${_lib_target} PROPERTIES IMPORTED_LOCATION \"${PYSIDE_LIBRARY}\")\n        endif()\n    endif()\nelse()\n    include(FindPackageHandleStandardArgs)\n    find_package_handle_standard_args(${_module}\n            FOUND_VAR ${_module}_FOUND\n            REQUIRED_VARS PYSIDE_LIBRARY PYSIDE_INCLUDE_DIR PYSIDE_TYPESYSTEMS\n            VERSION_VAR ${_module}_VERSION)\n\n    add_library(${_module}::pyside2 INTERFACE IMPORTED)\n    set_target_properties(${_module}::pyside2 PROPERTIES\n            INTERFACE_INCLUDE_DIRECTORIES \"${PYSIDE_INCLUDE_DIR}\"\n            INTERFACE_LINK_LIBRARIES \"${PYSIDE_LIBRARY}\")\nendif()\n\nmark_as_advanced(PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_BINARY)\n"
  },
  {
    "path": "cmake/FindPySide6.cmake",
    "content": "\nset(_module PySide6)\n\nfind_package(${_module} ${${_module}_FIND_VERSION} CONFIG QUIET)\nset(_lib_target ${_module}::pyside6)\n\nif(NOT ${_module}_FOUND)\n    include(PythonInfo)\n    find_python_site_packages(PYTHON_SITE_PACKAGES)\n    get_python_extension_suffix(PYTHON_EXTENSION_SUFFIX)\n\n    find_library(PYSIDE_LIBRARY\n            NAMES\n                \"pyside6${PYTHON_EXTENSION_SUFFIX}\"\n                \"pyside6${PYTHON_EXTENSION_SUFFIX}.${${_module}_FIND_VERSION_MAJOR}.${${_module}_FIND_VERSION_MINOR}\"\n            PATH_SUFFIXES \"${PYTHON_SITE_PACKAGES}/PySide6\")\n\n    find_path(PYSIDE_INCLUDE_DIR\n            pyside.h\n            PATH_SUFFIXES \"${PYTHON_SITE_PACKAGES}/PySide6/include\")\n\n    find_path(PYSIDE_TYPESYSTEMS\n            typesystem_core.xml\n            PATH_SUFFIXES \"${PYTHON_SITE_PACKAGES}/PySide6/typesystems\")\nendif()\n\nif(TARGET ${_lib_target})\n    get_target_property(_is_imported ${_lib_target} IMPORTED)\n    if(_is_imported)\n        get_target_property(_imported_location ${_lib_target} IMPORTED_LOCATION)\n        if(NOT _imported_location)\n            message(STATUS \"Target ${_lib_target} does not specify its IMPORTED_LOCATION! Trying to find it ourselves...\")\n            set(_find_args)\n            if(${_module}_CONFIG)\n                get_filename_component(_pyside6_lib_dir \"${${_module}_CONFIG}/../../../\" ABSOLUTE)\n                set(_find_args PATHS \"${_pyside6_lib_dir}\")\n            endif()\n            find_library(PYSIDE_LIBRARY\n                    NAMES\n                        \"pyside6${PYTHON_CONFIG_SUFFIX}\"\n                        \"pyside6${PYTHON_CONFIG_SUFFIX}.${${_module}_FIND_VERSION_MAJOR}.${${_module}_FIND_VERSION_MINOR}\"\n                    ${_find_args})\n            if(NOT PYSIDE_LIBRARY)\n                set(_message_type WARNING)\n                if(${_module}_FIND_REQUIRED)\n                    set(_message_type FATAL_ERROR)\n                endif()\n                message(${_message_type} \"Failed to manually find library for ${_module}\")\n                return()\n            endif()\n            message(STATUS \"IMPORTED_LOCATION for ${_lib_target} found: ${PYSIDE_LIBRARY}\")\n            set_target_properties(${_lib_target} PROPERTIES IMPORTED_LOCATION \"${PYSIDE_LIBRARY}\")\n        endif()\n    endif()\nelse()\n    include(FindPackageHandleStandardArgs)\n    find_package_handle_standard_args(${_module}\n            FOUND_VAR ${_module}_FOUND\n            REQUIRED_VARS PYSIDE_LIBRARY PYSIDE_INCLUDE_DIR PYSIDE_TYPESYSTEMS\n            VERSION_VAR ${_module}_VERSION)\n\n    add_library(${_module}::pyside6 INTERFACE IMPORTED)\n    set_target_properties(${_module}::pyside6 PROPERTIES\n            INTERFACE_INCLUDE_DIRECTORIES \"${PYSIDE_INCLUDE_DIR}\"\n            INTERFACE_LINK_LIBRARIES \"${PYSIDE_LIBRARY}\")\nendif()\n\nmark_as_advanced(PYSIDE_INCLUDE_DIR PYSIDE_LIBRARY PYSIDE_BINARY)\n"
  },
  {
    "path": "cmake/PythonInfo.cmake",
    "content": "\nfunction(find_python_site_packages VAR)\n    if(Python_SITELIB)\n        set(\"${VAR}\" \"${Python_SITELIB}\" PARENT_SCOPE)\n        return()\n    endif()\n\n    execute_process(\n            COMMAND \"${PYTHON_EXECUTABLE}\" -c \"if True:\n                from distutils import sysconfig\n                print(sysconfig.get_python_lib(prefix='', plat_specific=True))\"\n            OUTPUT_VARIABLE \"${VAR}\"\n            OUTPUT_STRIP_TRAILING_WHITESPACE)\n    set(\"${VAR}\" \"${${VAR}}\" PARENT_SCOPE)\nendfunction()\n\nfunction(get_python_extension_suffix VAR)\n    # from PySide2 CMakeLists.txt\n    # Result of imp.get_suffixes() depends on the platform, but generally looks something like:\n    # [('.cpython-34m-x86_64-linux-gnu.so', 'rb', 3), ('.cpython-34m.so', 'rb', 3),\n    # ('.abi3.so', 'rb', 3), ('.so', 'rb', 3), ('.py', 'r', 1), ('.pyc', 'rb', 2)]\n    # We pick the first most detailed one, strip of the file extension part.\n\n    execute_process(\n            COMMAND \"${PYTHON_EXECUTABLE}\" -c \"if True:\n               import importlib.machinery, re\n               first_suffix = importlib.machinery.EXTENSION_SUFFIXES[0]\n               res = re.search(r'^(.+)\\\\.', first_suffix)\n               if res:\n                   first_suffix = res.group(1)\n               else:\n                   first_suffix = ''\n               print(first_suffix)\n               \"\n            OUTPUT_VARIABLE \"${VAR}\"\n            OUTPUT_STRIP_TRAILING_WHITESPACE)\n    set(\"${VAR}\" \"${${VAR}}\" PARENT_SCOPE)\nendfunction()"
  },
  {
    "path": "cmake/Translations.cmake",
    "content": "# Languages with 0% progress or low progress and alternatives are disabled\nset(TS_FILES\n#   translations/am/cutter_am_ET.ts\n    translations/ar/cutter_ar_SA.ts\n    translations/bn/cutter_bn_BD.ts\n    translations/ca/cutter_ca_ES.ts\n    translations/de/cutter_de_DE.ts\n    translations/es-ES/cutter_es_ES.ts\n    translations/fa/cutter_fa_IR.ts\n    translations/fi/cutter_fi_FI.ts\n#   translations/fil/cutter_fil_PH.ts\n    translations/fr/cutter_fr_FR.ts\n    translations/he/cutter_he_IL.ts\n    translations/hi/cutter_hi_IN.ts\n    translations/it/cutter_it_IT.ts\n    translations/ja/cutter_ja_JP.ts\n#   translations/km/cutter_km_KH.ts\n    translations/ko/cutter_ko_KR.ts\n    translations/nl/cutter_nl_NL.ts\n#   translations/pa-IN/cutter_pa_IN.ts\n    translations/pl/cutter_pl_PL.ts\n#   translations/pt-BR/cutter_pt_BR.ts\n    translations/pt-PT/cutter_pt_PT.ts\n    translations/ro/cutter_ro_RO.ts\n    translations/ru/cutter_ru_RU.ts\n#   translations/sv-SE/cutter_sv_SE.ts\n#   translations/sw/cutter_sw_KE.ts\n#   translations/th/cutter_th_TH.ts\n    translations/tr/cutter_tr_TR.ts\n    translations/uk/cutter_uk_UA.ts\n#   translations/ur-IN/cutter_ur_IN.ts\n    translations/ur-PK/cutter_ur_PK.ts\n    translations/vi/cutter_vi_VN.ts\n    translations/zh-CN/cutter_zh_CN.ts\n    translations/zh-TW/cutter_zh_TW.ts\n)\n\nset_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/translations)\nif (CUTTER_QT EQUAL 6)\n    find_package(Qt6LinguistTools REQUIRED)\n    qt6_add_translation(qmFiles ${TS_FILES})\nelseif(CUTTER_QT EQUAL 5)\n    find_package(Qt5LinguistTools REQUIRED)\n    qt5_add_translation(qmFiles ${TS_FILES})\nendif()\nadd_custom_target(translations ALL DEPENDS ${qmFiles} SOURCES ${TS_FILES})\n\ninstall(FILES\n    ${qmFiles}\n    # For Linux it might be more correct to use ${MAKE_INSTALL_LOCALEDIR}, but that\n    # uses share/locale_name/software_name layout instead of share/software_name/locale_files.\n    DESTINATION ${CUTTER_INSTALL_DATADIR}/translations\n)\n\n"
  },
  {
    "path": "cmake/Utils.cmake",
    "content": "\n# Like option(), but the value can also be AUTO\nmacro(tri_option name desc default)\n    set(\"${name}\" \"${default}\" CACHE STRING \"${desc}\")\n    set_property(CACHE \"${name}\" PROPERTY STRINGS AUTO ON OFF)\nendmacro()"
  },
  {
    "path": "dist/CMakeLists.txt",
    "content": "set(CUTTER_DEPS \"${CMAKE_CURRENT_SOURCE_DIR}/../cutter-deps\")\nunset(RZ_GHIDRA_PREFIX_PATH)\n\n################################################\n# Windows\n################################################\n\nif(WIN32)\n\tset(CPACK_GENERATOR \"ZIP\")\n\tset(RIZIN_INSTALL_PLUGDIR \"lib/rizin/plugins\")\n\n\tif (CUTTER_PACKAGE_DEPENDENCIES)\n\t\tif (CUTTER_ENABLE_PYTHON)\n\t\t\tif (CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)\n\t\t\t\tset(CPACK_INSTALL_SCRIPTS ${CMAKE_CURRENT_SOURCE_DIR}/WindowsBundlePython.cmake)\n\t\t\tendif()\n\t\t\tfind_package (Python3 ${CUTTER_PYTHON_MIN} REQUIRED COMPONENTS)\n\t\t\tinstall(DIRECTORY ${CUTTER_DEPS}/pyside/lib/site-packages DESTINATION \"python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}\")\n\t\t\tinstall(FILES ${CUTTER_DEPS}/pyside/bin/shiboken6.abi3.dll ${CUTTER_DEPS}/pyside/bin/pyside6.abi3.dll DESTINATION .)\n\t\tendif()\n\t\tinstall(SCRIPT WindowsBundleQt.cmake)\n\tendif()\n\tif (CUTTER_PACKAGE_JSDEC AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)\n\t\tinstall(CODE \"\n\t\t\tset(ENV{RZ_PREFIX} \\\"\\${CMAKE_INSTALL_PREFIX}\\\")\n\t\t\tset(ENV{PATH} \\\"\\${CMAKE_INSTALL_PREFIX};\\$ENV{PATH}\\\")\n\t\t\texecute_process(COMMAND powershell \\\"${CMAKE_CURRENT_SOURCE_DIR}/bundle_jsdec.ps1\\\"\n\t\t\t\t\t\t\t\\\"\\${CMAKE_INSTALL_PREFIX}\\\"\n\t\t\t\t\t\t\t\\\"-DCUTTER_INSTALL_PLUGDIR=plugins/native\\\"\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package jsdec (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\n\tif (CUTTER_PACKAGE_RZ_LIBSWIFT AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)\n\t\tinstall(CODE \"\n\t\t\tset(ENV{RZ_PREFIX} \\\"\\${CMAKE_INSTALL_PREFIX}\\\")\n\t\t\tset(ENV{PATH} \\\"\\${CMAKE_INSTALL_PREFIX};\\$ENV{PATH}\\\")\n\t\t\texecute_process(COMMAND powershell \\\"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_libswift.ps1\\\" \\\"\\${CMAKE_INSTALL_PREFIX}\\\"\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package rz-libswift (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\n\tif (CUTTER_PACKAGE_RZ_LIBYARA AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)\n\t\tinstall(CODE \"\n\t\t\tset(ENV{RZ_PREFIX} \\\"\\${CMAKE_INSTALL_PREFIX}\\\")\n\t\t\tset(ENV{PATH} \\\"\\${CMAKE_INSTALL_PREFIX};\\$ENV{PATH}\\\")\n\t\t\texecute_process(COMMAND powershell \n\t\t\t\t\t\t\t\\\"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_libyara.ps1\\\"\n\t\t\t\t\t\t\t\\\"\\${CMAKE_INSTALL_PREFIX}\\\"\n\t\t\t\t\t\t\t\\\"-DCUTTER_INSTALL_PLUGDIR=plugins/native\\\"\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package rz_libyara (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\n\tif (CUTTER_PACKAGE_RZ_SILHOUETTE AND CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)\n\t\tinstall(CODE \"\n\t\t\tset(ENV{RZ_PREFIX} \\\"\\${CMAKE_INSTALL_PREFIX}\\\")\n\t\t\tset(ENV{PATH} \\\"\\${CMAKE_INSTALL_PREFIX};\\$ENV{PATH}\\\")\n\t\t\texecute_process(COMMAND powershell \\\"${CMAKE_CURRENT_SOURCE_DIR}/bundle_rz_silhouette.ps1\\\" \\\"\\${CMAKE_INSTALL_PREFIX}\\\"\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package rz-silhouette (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\nendif()\n\n################################################\n# macOS\n################################################\n\nif(APPLE)\n\tset(CPACK_GENERATOR \"Bundle\")\n\tset(CPACK_BUNDLE_NAME \"Cutter\")\n\tset(ICON \"Cutter\")\n\tconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in\" \"${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist\")\n\tset(CPACK_BUNDLE_PLIST \"${CMAKE_CURRENT_BINARY_DIR}/macos/Info.plist\")\n\tset(CPACK_BUNDLE_ICON \"${CMAKE_CURRENT_SOURCE_DIR}/macos/cutter.icns\")\n\tset(CPACK_DMG_BACKGROUND_IMAGE \"${CMAKE_CURRENT_SOURCE_DIR}/macos/cutter_mac_app.png\")\n\tset(CPACK_DMG_DS_STORE \"${CMAKE_CURRENT_SOURCE_DIR}/macos/DS_Store_ForDMGBackground\")\n\tset(CPACK_DMG_VOLUME_NAME \"Cutter\")\n\tset(CPACK_BUNDLE_APPLE_ENTITLEMENTS \"${CMAKE_CURRENT_SOURCE_DIR}/macos/Entitlements.plist\")\n\tset(CPACK_APPLE_BUNDLE_ID \"re.rizin.cutter\")\n\n\tfind_program(MACDEPLOYQT_PATH macdeployqt HINTS \"${Qt5_DIR}/../../../bin\")\n\tif(NOT MACDEPLOYQT_PATH)\n\t\tmessage(FATAL_ERROR \"Failed to find macdeployqt\")\n\tendif()\n\n\tunset(ADJUST_RIZIN_LIBS)\n\tforeach(_lib ${RZ_LIBS})\n\t\tlist(APPEND ADJUST_RIZIN_LIBS \"${RIZIN_INSTALL_DIR}/lib/lib${_lib}.dylib\")\n\tendforeach()\n\tforeach(_lib ${RZ_EXTRA_LIBS})\n\t\tlist(APPEND ADJUST_RIZIN_LIBS \"${RIZIN_INSTALL_DIR}/lib/lib${_lib}.dylib\")\n\tendforeach()\n\n\tif(CUTTER_PACKAGE_DEPENDENCIES AND CUTTER_ENABLE_PYTHON)\n\t\tset(EMBED_PYTHON_SH \"${CMAKE_CURRENT_SOURCE_DIR}/appbundle_embed_python.sh\")\n\t\tset(PYTHON_FRAMEWORK_DIR \"${CUTTER_DEPS}/python/Library/Frameworks/Python.framework\")\n\t\tset(PYSIDE_PREFIX \"${CUTTER_DEPS}/pyside\")\n\t\tconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/MacOSBundlePython.cmake.in\"\n\t\t\t\"${CMAKE_CURRENT_BINARY_DIR}/MacOSBundlePython.cmake\" @ONLY)\n\t\tinstall(SCRIPT \"${CMAKE_CURRENT_BINARY_DIR}/MacOSBundlePython.cmake\")\n\tendif()\n\n\tconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/MacOSSetupBundle.cmake.in\"\n\t\t\"${CMAKE_CURRENT_BINARY_DIR}/MacOSSetupBundle.cmake\" @ONLY)\n\tinstall(SCRIPT \"${CMAKE_CURRENT_BINARY_DIR}/MacOSSetupBundle.cmake\")\n\n\tget_filename_component(QT_PREFIX \"${MACDEPLOYQT_PATH}/../..\" ABSOLUTE)\n\tlist(APPEND RZ_GHIDRA_PREFIX_PATH \"${QT_PREFIX}\")\n\tset(RIZIN_INSTALL_PLUGDIR \"lib/rizin/plugins\") # escaped backslash on purpose, should be evaluated later\nendif()\n\n################################################\n# macOS + Linux\n################################################\n\nif(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS AND (NOT WIN32))\n\tif (CUTTER_PACKAGE_JSDEC)\n\t\tif(APPLE)\n\t\t\tset (JSDEC_PLUGIN_OPTIONS \"-DCUTTER_INSTALL_PLUGDIR=plugins/native\")\n\t\telse()\n\t\t\tset (JSDEC_PLUGIN_OPTIONS \"\")\n\t\tendif()\n\t\tinstall(CODE \"\n\t\t\texecute_process(COMMAND \\\"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/jsdec.sh\\\"\n\t\t\t\t\t\t\t\\\"\\${CMAKE_INSTALL_PREFIX}\\\" \\\"${JSDEC_PLUGIN_OPTIONS}\\\"\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package jsdec (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\n\tif (CUTTER_PACKAGE_RZ_LIBSWIFT)\n\t\tinstall(CODE \"\n\t\t\texecute_process(COMMAND \\\"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-libswift.sh\\\"\n\t\t\t\t\t\t\t--pkg-config-path=\\${CMAKE_INSTALL_PREFIX}/lib/pkgconfig --prefix=\\${CMAKE_INSTALL_PREFIX}\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package rz-libswift (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\n\tif (CUTTER_PACKAGE_RZ_LIBYARA)\n\t\tif(APPLE)\n\t\t\tset (YARA_PLUGIN_OPTIONS \"-DCUTTER_INSTALL_PLUGDIR=plugins/native\")\n\t\telse()\n\t\t\tset (YARA_PLUGIN_OPTIONS \"\")\n\t\tendif()\n\t\tif (CUTTER_QT EQUAL 6)\n\t\t\tset (YARA_PLUGIN_OPTIONS \"${YARA_PLUGIN_OPTIONS} -DCUTTER_QT6=ON\")\n\t\tendif()\n\t\tinstall(CODE \"\n\t\t\texecute_process(COMMAND\n\t\t\t\t\t\t\t\\\"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-libyara.sh\\\"\n\t\t\t\t\t\t\t\\\"\\${CMAKE_INSTALL_PREFIX}\\\" \\\"${YARA_PLUGIN_OPTIONS}\\\"\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package rz-libyara (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\n\tif (CUTTER_PACKAGE_RZ_SILHOUETTE)\n\t\tinstall(CODE \"\n\t\t\texecute_process(COMMAND\n\t\t\t\t\t\t\t\\\"${CMAKE_CURRENT_SOURCE_DIR}/../scripts/rz-silhouette.sh\\\"\n\t\t\t\t\t\t\t\\\"\\${CMAKE_INSTALL_PREFIX}\\\" \\\"${YARA_PLUGIN_OPTIONS}\\\"\n\t\t\t\t\t\t\tWORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\t\t\t\t\t\t\tRESULT_VARIABLE SCRIPT_RESULT)\n\t\t\tif (SCRIPT_RESULT)\n\t\t\t\tmessage(FATAL_ERROR \\\"Failed to package rz-silhouette (returned \\${SCRIPT_RESULT})\\\")\n\t\t\tendif()\n\t\t\")\n\tendif()\nendif()\n\n################################################\n# rz-ghidra\n################################################\n\nif(CUTTER_PACKAGE_RZ_GHIDRA)\n\tif(CUTTER_ENABLE_DEPENDENCY_DOWNLOADS)\n\t\t# Currently using external project only for downloading source\n\t\t# It neeeds to link against compiled cutter so for now build it using custom install script.\n\t\t# That way rz-ghidra build process is the same as with any other external plugin built against\n\t\t# installed Cutter.\n\t\tExternalProject_Add(rz-ghidra\n\t\t\tGIT_REPOSITORY https://github.com/rizinorg/rz-ghidra\n\t\t\t#GIT_TAG v0.3.0\n\t\t\t#GIT_TAG c7a50a2e7c0a95cd52b167c9ee0fa1805223f08e\n\t\t\tGIT_TAG dev\n\t\t\t#GIT_SHALLOW ON # disable this line when using commit hash\n\t\t\tCONFIGURE_COMMAND \"\"\n\t\t\tBUILD_COMMAND \"\"\n\t\t\tINSTALL_COMMAND \"\"\n\t\t)\n\tendif()\n\tif (WIN32 OR APPLE)\n\t\tset (GHIDRA_OPTIONS \"\n\t\t\t\\\"-DRIZIN_INSTALL_PLUGDIR=\\${CMAKE_INSTALL_PREFIX}/${RIZIN_INSTALL_PLUGDIR}\\\"\n\t\t\t-DCUTTER_INSTALL_PLUGDIR=plugins/native\")\n\telse()\n\t\tset (GHIDRA_OPTIONS \"-DCUTTER_INSTALL_PLUGDIR=\\${CMAKE_INSTALL_PREFIX}/share/rizin/cutter/plugins/native\")\n\tendif()\n\tinstall(CODE \"\n\t\texecute_process(\n\t\t\tWORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rz-ghidra-prefix/src/rz-ghidra-build\n\t\t\tRESULT_VARIABLE SCRIPT_RESULT\n\t\t\tCOMMAND \\${CMAKE_COMMAND}\n\t\t\t\t\t\t\t../rz-ghidra\n\t\t\t\t\t\t\t-DCMAKE_BUILD_TYPE=Release\n\t\t\t\t\t\t\t\\\"-DCMAKE_PREFIX_PATH=\\${CMAKE_INSTALL_PREFIX};\\${CMAKE_INSTALL_PREFIX}/include/librz;\\${CMAKE_INSTALL_PREFIX}/include/librz/sdb;${RZ_GHIDRA_PREFIX_PATH}\\\"\n\t\t\t\t\t\t\t-DCMAKE_INSTALL_PREFIX=\\${CMAKE_INSTALL_PREFIX}\n\t\t\t\t\t\t\t${GHIDRA_OPTIONS}\n\t\t\t\t\t\t\t-DBUILD_CUTTER_PLUGIN=ON\n\t\t\t\t\t\t\t-DBUILD_SLEIGH_PLUGIN=OFF\n\t\t\t\t\t\t\t-G Ninja\n\t\t\t)\n\t\tif (SCRIPT_RESULT)\n\t\t\tmessage(FATAL_ERROR \\\"Failed to configure rz-ghidra (returned \\${SCRIPT_RESULT})\\\")\n\t\tendif()\n\t\texecute_process(WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rz-ghidra-prefix/src/rz-ghidra-build\n\t\t\tRESULT_VARIABLE SCRIPT_RESULT\n\t\t\tCOMMAND \\${CMAKE_COMMAND} --build . --target install\n\t\t\t)\n\t\tif (SCRIPT_RESULT)\n\t\t\tmessage(FATAL_ERROR \\\"Failed to install rz-ghidra plugin (returned \\${SCRIPT_RESULT})\\\")\n\t\tendif()\n\t\")\nendif()\n\ninclude(CPack)\n"
  },
  {
    "path": "dist/MacOSBundlePython.cmake.in",
    "content": "\nset(EMBED_PYTHON_SH \"@EMBED_PYTHON_SH@\")\nset(PYTHON_FRAMEWORK_DIR \"@PYTHON_FRAMEWORK_DIR@\")\nset(PYSIDE_PREFIX \"@PYSIDE_PREFIX@\")\n\nget_filename_component(BUNDLE_PATH \"${CMAKE_INSTALL_PREFIX}/../..\" ABSOLUTE)\n\nset(ENV{PKG_CONFIG_PATH} \"${PYSIDE_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}\")\n\n# TODO: instead of calling a shellscript, implement the whole thing in cmake\nexecute_process(COMMAND\n\t\"${EMBED_PYTHON_SH}\"\n\t\"${PYTHON_FRAMEWORK_DIR}\"\n\t\"${BUNDLE_PATH}\"\n\t\"${BUNDLE_PATH}/Contents/MacOS/cutter\"\n    RESULT_VARIABLE SCRIPT_RESULT)\nif(SCRIPT_RESULT)\n    message(FATAL_ERROR \"Failed to bundle python\")\nendif()\n\n"
  },
  {
    "path": "dist/MacOSSetupBundle.cmake.in",
    "content": "include(BundleUtilities)\n\nset(MACDEPLOYQT_PATH \"@MACDEPLOYQT_PATH@\")\nset(INFO_PLIST_PATH \"@CPACK_BUNDLE_PLIST@\")\nset(ADJUST_RIZIN_LIBS \"@ADJUST_RIZIN_LIBS@\")\nset(CUTTER_PACKAGE_DEPENDENCIES \"@CUTTER_PACKAGE_DEPENDENCIES@\")\nset(CUTTER_ENABLE_PYTHON \"@CUTTER_ENABLE_PYTHON@\")\nset(RZ_BIN \"@RZ_BIN@\")\n\n\nmacro(run_or_die)\n\texecute_process(${ARGV} RESULT_VARIABLE PROC_RESULT)\n\tif(PROC_RESULT)\n\t\tmessage(FATAL_ERROR \"Failed to run ${ARGV}\")\n\tendif()\nendmacro()\n\nget_filename_component(BUNDLE_PATH \"${CMAKE_INSTALL_PREFIX}/../..\" ABSOLUTE)\nset(EXECUTABLE_DIR \"${BUNDLE_PATH}/Contents/MacOS\")\nset(FRAMEWORK_DIR \"${BUNDLE_PATH}/Contents/Frameworks\")\n\n# Copying the Info.plist will be done later again by CPack but we need it a bit earlier\n# so macdeployqt has enough info.\nfile(COPY \"${INFO_PLIST_PATH}\" DESTINATION \"${BUNDLE_PATH}/Contents\")\n\n# replace absolute path from build directory in rizin pkgconfig files with relative ones\nfile(GLOB RZ_PCFILES \"${CMAKE_INSTALL_PREFIX}/lib/pkgconfig/rz_*.pc\")\nforeach (_pcfile ${RZ_PCFILES})\n\tfile(READ \"${_pcfile}\" _text)\n\tstring(REGEX REPLACE \"^prefix=[^\\n]*\\n\" \"prefix=\\${pcfiledir}/../..\\n\" _text \"${_text}\")\n\tfile(WRITE \"${_pcfile}\" \"${_text}\")\nendforeach()\n# macdeployqt would put the rz libraries into Contents/Frameworks by default, but we want to have them\n# only in the prefix, so we fix the paths manually.\nforeach(_lib ${ADJUST_RIZIN_LIBS})\n\tget_filename_component(_lib \"${_lib}\" REALPATH)\n\tget_filename_component(_name \"${_lib}\" NAME)\n\t# Cutter reference to lib\n\trun_or_die(COMMAND install_name_tool\n\t\t-change \"${_lib}\" \"@rpath/${_name}\"\n\t\t\"${EXECUTABLE_DIR}/cutter\")\n\t# lib LC_ID_DYLIB\n\trun_or_die(COMMAND install_name_tool\n\t\t\t-id \"@rpath/${_name}\"\t\n\t\t\t\"${CMAKE_INSTALL_PREFIX}/lib/${_name}\")\n\t# Fix every lib for every lib too\n\tforeach(_lib2 ${ADJUST_RIZIN_LIBS})\n\tget_filename_component(_lib2 \"${_lib2}\" REALPATH)\n\t\tget_filename_component(_name2 \"${_lib2}\" NAME)\n\t\trun_or_die(COMMAND install_name_tool\n\t\t\t-change \"${_lib2}\" \"@rpath/${_name2}\"\n\t\t\t\"${CMAKE_INSTALL_PREFIX}/lib/${_name}\")\n\tendforeach()\nendforeach()\nforeach(_lib ${RZ_BIN})\n\tget_filename_component(_lib \"${_lib}\" REALPATH)\n\tget_filename_component(_name \"${_lib}\" NAME)\n\t# Fix every lib for every lib too\n\tforeach(_lib2 ${ADJUST_RIZIN_LIBS})\n\tget_filename_component(_lib2 \"${_lib2}\" REALPATH)\n\t\tget_filename_component(_name2 \"${_lib2}\" NAME)\n\t\trun_or_die(COMMAND install_name_tool\n\t\t\t-change \"${_lib2}\" \"@rpath/${_name2}\"\n\t\t\t\"${CMAKE_INSTALL_PREFIX}/bin/${_name}\")\n\tendforeach()\nendforeach()\n\n# Add rpaths because macdeployqt fails to do that properly\nrun_or_die(COMMAND install_name_tool\n\t-add_rpath \"@executable_path/../Frameworks\"\n\t\"${EXECUTABLE_DIR}/cutter\")\nrun_or_die(COMMAND install_name_tool\n\t-add_rpath \"@executable_path/../Resources/lib\"\n\t\"${EXECUTABLE_DIR}/cutter\")\nrun_or_die(COMMAND install_name_tool\n\t-add_rpath \"@executable_path/../Resources/lib/rizin/plugins\"\n\t\"${EXECUTABLE_DIR}/cutter\")\n\nset(MACDEPLOYQT_COMMAND \"${MACDEPLOYQT_PATH}\" \"${BUNDLE_PATH}\" \"-verbose=2\" \"-libpath=${CMAKE_INSTALL_PREFIX}/lib\")\nmessage(\"Running macdeployqt \\\"${MACDEPLOYQT_COMMAND}\\\"\")\nrun_or_die(COMMAND ${MACDEPLOYQT_COMMAND}) # First run\nif (CUTTER_PACKAGE_DEPENDENCIES AND CUTTER_ENABLE_PYTHON)\n\tfile(REAL_PATH \"${FRAMEWORK_DIR}/Python.framework/Python\" _python_lib_path)\n\tmessage(\"Python lib ${_python_lib_path}\")\n\tlist(APPEND MACDEPLOYQT_COMMAND \"-executable=${_python_lib_path}\")\nendif()\n# Qt plugins are not getting deployed on first macdeployqt run. Runing twice helps.\n# If python is added as additional executable in first run it also breaks plugin copying.\nrun_or_die(COMMAND ${MACDEPLOYQT_COMMAND}) # Second run\n\n# Clean up the mess that macdeployqt made (duplicate rz libs, we only want the ones in the prefix)\nforeach(_lib ${ADJUST_RIZIN_LIBS})\n\tget_filename_component(_lib \"${_lib}\" REALPATH)\n\tget_filename_component(_name \"${_lib}\" NAME)\n\tfile(REMOVE \"${BUNDLE_PATH}/Contents/Frameworks/${_name}\")\nendforeach()\n"
  },
  {
    "path": "dist/WindowsBundlePython.cmake",
    "content": "execute_process(COMMAND powershell \"${CMAKE_CURRENT_LIST_DIR}/bundle_python.ps1\" x64 \"${CMAKE_INSTALL_PREFIX}\"\n    WORKING_DIRECTORY ${CPACK_PACKAGE_DIRECTORY}\n    RESULT_VARIABLE SCRIPT_RESULT)\nif (SCRIPT_RESULT)\n    message(FATAL_ERROR \"Failed to bundle python\")\nendif()\n"
  },
  {
    "path": "dist/WindowsBundleQt.cmake",
    "content": "message(\"Running windeployqt\")\nfind_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS \"${_qt_bin_dir}\")\nif(NOT WINDEPLOYQT_EXECUTABLE)\n\tmessage(FATAL_ERROR \"Failed to find windeployqt\")\nendif()\nexecute_process(COMMAND \"${WINDEPLOYQT_EXECUTABLE}\" cutter.exe\n        --plugindir \"qtplugins\"\n        --no-translations # Cutter currently isn't loading Qt translation file\n    WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}\n    RESULT_VARIABLE SCRIPT_RESULT)\nif (SCRIPT_RESULT)\n    message(FATAL_ERROR \"Failed to bundle qt\")\nendif()\nfile(WRITE \"${CMAKE_INSTALL_PREFIX}/qt.conf\" \"[PATHS]\\nPlugins = qtplugins\")\n"
  },
  {
    "path": "dist/appbundle_embed_python.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\nif ! [[ $# -eq 3 ]]; then\n    echo \"Usage: $0 [Python.framework] [AppBundle.app] [AppBundle.app/Contents/MacOS/Executable]\"\n    exit 1\nfi\n\npython_version=python3.12\n\npy_framework=$1\nappbundle=$2\nexecutable=$3\n\necho \"Embedding $py_framework into $appbundle | $executable\"\n\nmkdir -p \"$appbundle/Contents/Frameworks\"\nif [ ! -d \"$appbundle/Contents/Frameworks/Python.framework\" ]\nthen\n    cp -a -n \"$py_framework\" \"$appbundle/Contents/Frameworks/\"\n    echo \"Cleaning up embedded Python Framework\"\n    cd \"$appbundle/Contents/Frameworks/Python.framework\"\n    find . | grep -E \"(__pycache__|\\.pyc|\\.pyo$)\" | xargs rm -rf\n    rm -r Versions/Current/Resources/Python.app \"Versions/Current/lib/$python_version/test\" \"Versions/Current/lib/$python_version/idlelib\" \"Versions/Current/lib/$python_version/curses\" \"Versions/Current/lib/$python_version/lib2to3\" || echo \"Couldn't remove something\"\nelse\n    echo \"Python.framework already exists, skipping copying\"\n    cd \"$appbundle/Contents/Frameworks/Python.framework\"\nfi\n\necho \"Making executable $executable point to embedded Framework\"\ninstall_name_tool -change `otool -L \"$executable\" | sed -n \"s/^[[:blank:]]*\\([^[:blank:]]*Python\\) (.*$/\\1/p\"` @executable_path/../Frameworks/Python.framework/Versions/Current/Python \"$executable\" \n\necho \"Checking if PySide is available\"\n\npyside_prefix=$(pkg-config --variable=prefix pyside6)\nif [ $? -ne 0 ]; then\n\techo \"PySide is not available, ignoring.\"\n\texit 0\nfi\n\necho \"PySide is at $pyside_prefix\"\n\nif [ ! -d \"Versions/Current/lib/$python_version/site-packages/PySide6\" ]\nthen\n    cp -va \"$pyside_prefix/lib/$python_version/\" \"Versions/Current/lib/$python_version\"\n    cd .. # $appbundle/Contents/Frameworks\n    cp -va \"$pyside_prefix/lib/\"*.dylib .\nelse\n    echo \"site-packages/Pyside6 exists, skipping copying\"\nfi\n"
  },
  {
    "path": "dist/bundle_jsdec.ps1",
    "content": "$dist = $args[0]\n$python = Split-Path((Get-Command python.exe).Path)\n$plugin_path = \"$dist\\plugins\\native\\\"\n$pathdll = \"$plugin_path\\jsdec_cutter.dll\"\n\nif (-not (Test-Path -Path 'jsdec' -PathType Container)) {\n    git clone https://github.com/rizinorg/jsdec.git --depth 1 --branch \"dev\"\n}\ncd jsdec\n$jsdecdir = (Get-Item .).FullName\n\n& meson.exe setup --buildtype=release -Dbuild_type=cutter \"$jsdecdir\\build_lib\"\nninja -C \"$jsdecdir\\build_lib\"\n\n# cmake is silly and expects .lib but meson generates the static lib as .a\nCopy-Item \"$jsdecdir\\build_lib\\libjsdec.a\" -Destination \"$jsdecdir\\build_lib\\jsdec.lib\"\n\nmkdir build_plugin\ncd build_plugin\ncmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DJSDEC_BUILD_DIR=\"$jsdecdir\\build_lib\" -DCMAKE_INSTALL_PREFIX=\"$dist\" -DCUTTER_INSTALL_PLUGDIR=\"plugins\\native\" $cmake_opts \"$jsdecdir\\cutter-plugin\"\nninja install\n\n$ErrorActionPreference = 'Stop'\n\nif(![System.IO.File]::Exists($pathdll)) {\n    echo \"files: $plugin_path\"\n    ls \"$plugin_path\"\n    throw (New-Object System.IO.FileNotFoundException(\"File not found: $pathdll\", $pathdll))\n}\n"
  },
  {
    "path": "dist/bundle_python.ps1",
    "content": "$arch = $args[0]\n$dist = $args[1]\n\n$py_version = (python --version).Split()[1]\n$py_base = \"python\" + $py_version.Split('.')[0] + $py_version.Split('.')[1]\n$py_platform = If ($arch -eq \"x64\") {\"amd64\"} Else {\"win32\"}\n$py_url = \"https://www.python.org/ftp/python/${py_version}/python-${py_version}-embed-${py_platform}.zip\"\n\nRemove-Item .\\python_embed -Recurse -ErrorAction SilentlyContinue\n$ErrorActionPreference = 'Stop'\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; wget ${py_url} -OutFile python_embed.zip; Expand-Archive .\\python_embed.zip -DestinationPath .\\python_embed\nNew-Item -ItemType directory -Force -Path $dist\\$py_base\nCopy-Item .\\python_embed\\${py_base}.zip -Destination $dist\\$py_base\nCopy-Item .\\python_embed\\*.pyd -Destination $dist\\$py_base\nCopy-Item .\\python_embed\\sqlite3.dll -Destination $dist\\$py_base\nCopy-Item .\\python_embed\\python*.dll -Destination $dist\n[System.IO.File]::WriteAllLines(\"${dist}\\${py_base}._pth\", \"${py_base}`r`n${py_base}\\${py_base}.zip`r`n${py_base}\\site-packages\")\n"
  },
  {
    "path": "dist/bundle_rz_libswift.ps1",
    "content": "$dist = $args[0]\n$python = Split-Path((Get-Command python.exe).Path)\n\nif (-not (Test-Path -Path 'libswift' -PathType Container)) {\n    git clone https://github.com/rizinorg/rz-libswift.git --depth 1 libswift\n}\ncd libswift\n& meson.exe --buildtype=release --prefix=$dist build\nninja -C build install\n$pathdll = \"$dist\\lib\\rizin\\plugins\\swift.dll\"\nif(![System.IO.File]::Exists($pathdll)) {\n    type build/meson-logs/meson-log.txt\n    ls \"$dist\\lib\\rizin\\plugins\\\"\n    throw (New-Object System.IO.FileNotFoundException(\"File not found: $pathdll\", $pathdll))\n}\nRemove-Item -Recurse -Force \"$dist\\lib\\rizin\\plugins\\swift.lib\"\n"
  },
  {
    "path": "dist/bundle_rz_libyara.ps1",
    "content": "$dist = $args[0]\n$cmake_opts = $args[1]\n$python = Split-Path((Get-Command python.exe).Path)\n\nif (-not (Test-Path -Path 'rz_libyara' -PathType Container)) {\n    git clone https://github.com/rizinorg/rz-libyara.git --depth 1 --branch main rz_libyara\n    git -C rz_libyara submodule init\n    git -C rz_libyara submodule update\n}\ncd rz_libyara\n& meson.exe --buildtype=release --prefix=$dist -Duse_sys_yara=disabled -Denable_openssl=false build\nninja -C build install\n$pathdll = \"$dist\\lib\\rizin\\plugins\\rz_yara.dll\"\nif(![System.IO.File]::Exists($pathdll)) {\n    type build/meson-logs/meson-log.txt\n    ls \"$dist\\lib\\rizin\\plugins\\\"\n    throw (New-Object System.IO.FileNotFoundException(\"File not found: $pathdll\", $pathdll))\n}\nRemove-Item -Recurse -Force \"$dist\\lib\\rizin\\plugins\\rz_yara.lib\"\n\ncd cutter-plugin\nmkdir build\ncd build\ncmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DRIZIN_INSTALL_PLUGDIR=\"../build\" -DCMAKE_INSTALL_PREFIX=\"$dist\" $cmake_opts ..\nninja\nninja install\n\n$ErrorActionPreference = 'Stop'\n\n$plugin_path = \"$dist\\plugins\\native\\\"\n$pathdll = \"$plugin_path\\cutter_yara_plugin.dll\"\n\nif(![System.IO.File]::Exists($pathdll)) {\n    echo \"files: $plugin_path\"\n    ls \"$plugin_path\"\n    throw (New-Object System.IO.FileNotFoundException(\"File not found: $pathdll\", $pathdll))\n}\n"
  },
  {
    "path": "dist/bundle_rz_silhouette.ps1",
    "content": "$dist = $args[0]\n$cmake_opts = $args[1]\n$python = Split-Path((Get-Command python.exe).Path)\n\nif (-not (Test-Path -Path 'rz-silhouette' -PathType Container)) {\n    git clone https://github.com/rizinorg/rz-silhouette.git --depth 1 rz-silhouette\n}\ncd rz-silhouette\n& meson.exe --buildtype=release --prefix=$dist build\nninja -C build install\n$pathdll = \"$dist\\lib\\rizin\\plugins\\rz_silhouette.dll\"\nif(![System.IO.File]::Exists($pathdll)) {\n    type build/meson-logs/meson-log.txt\n    ls \"$dist\\lib\\rizin\\plugins\\\"\n    throw (New-Object System.IO.FileNotFoundException(\"File not found: $pathdll\", $pathdll))\n}\nRemove-Item -Recurse -Force \"$dist\\lib\\rizin\\plugins\\rz_silhouette.lib\"\n"
  },
  {
    "path": "dist/macos/Entitlements.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>com.apple.security.cs.debugger</key>\n    <true/>\n    <key>com.apple.security.cs.disable-library-validation</key>\n    <true/>\n    <key>com.apple.security.cs.allow-jit</key>\n    <true/>\n    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>\n    <true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "dist/macos/Info.plist.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDisplayName</key>\n\t<string>Cutter</string>\n\t<key>CFBundleExecutable</key>\n\t<string>cutter</string>\n\t<key>CFBundleVersion</key>\n\t<string>@CUTTER_VERSION_FULL@</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>@CUTTER_VERSION_FULL@</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>Licensed under the GPLv3 by the Cutter developers.</string>\n\t<key>CFBundleIconFile</key>\n\t<string>@ICON@</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>re.rizin.cutter</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n\t<key>NSSupportsAutomaticGraphicsSwitching</key>\n\t<true/>\n\t<key>CFBundleDocumentTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>Data</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Viewer</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Owner</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>NSDocumentClass</key>\n\t\t\t<string>FileDataDocument</string>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>Other</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Viewer</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Alternate</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>public.executable</string>\n\t\t\t\t<string>public.text</string>\n\t\t\t\t<string>public.archive</string>\n\t\t\t\t<string>public.disk-image</string>\n\t\t\t\t<string>public.image</string>\n\t\t\t\t<string>public.audio</string>\n\t\t\t\t<string>public.movie</string>\n\t\t\t\t<string>com.adobe.pdf</string>\n\t\t\t</array>\n\t\t\t<key>NSDocumentClass</key>\n\t\t\t<string>FileDataDocument</string>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t<array>\n\t\t\t\t<string>*</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>AllTypes</string>\n\t\t\t<key>CFBundleTypeOSTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>****</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Viewer</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Alternate</string>\n\t\t\t<key>NSDocumentClass</key>\n\t\t\t<string>FileDataDocument</string>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "FROM alpine:latest AS builder\nLABEL maintainer \"Philipp Schmied <banana@bananamafia.dev>\"\n\n# Prevent build fails because of interactive scripts when compiling\nENV DEBIAN_FRONTEND noninteractive\n\n# Install dependencies required for building Cutter\nRUN apk add --no-cache \\\n    bash \\\n    build-base \\\n    cmake \\\n    curl \\\n    git \\\n    libuuid \\\n    linux-headers \\\n    make \\\n    meson \\\n    pkgconfig \\\n    python3 \\\n    python3-dev \\\n    qt5-qtbase \\\n    qt5-qtsvg-dev \\\n    qt5-qttools-dev \\\n    shadow \\\n    su-exec \\\n    unzip \\\n    wget\n\n# Clone and build\nRUN git clone --recurse-submodules https://github.com/rizinorg/cutter.git /opt/cutter && \\\n    mkdir -p /opt/cutter/build && \\\n    cd /opt/cutter/build && \\\n    cmake .. && \\\n    cmake --build . -j $(grep -c ^processor /proc/cpuinfo)\n\nFROM alpine:latest AS runner\n\n# Get the compiled Cutter from the builder\nCOPY --from=builder /opt/cutter /opt/cutter\n\n# Add the dependencies we need for running\nRUN apk add --no-cache \\\n    bash \\\n    cabextract \\\n    libuuid \\\n    qt5-qtbase \\\n    qt5-qtsvg-dev \\\n    shadow \\\n    su-exec\n\n# Prepare user and files to mount configurations later on\nRUN useradd cutter && \\\n    mkdir /var/sharedFolder && \\\n    mkdir -p /home/cutter/.config/rizin && \\\n    touch /home/cutter/.rizinrc && \\\n    chown -R cutter:cutter /var/sharedFolder && \\\n    chown -R cutter:cutter /home/cutter/\n\nWORKDIR /home/cutter\nADD entrypoint.sh /usr/local/bin/entrypoint.sh\n\nENTRYPOINT [\"/usr/local/bin/entrypoint.sh\"]\n"
  },
  {
    "path": "docker/Makefile",
    "content": "SHELL := /bin/bash\n\n# The directory of this file\nDIR := $(shell echo $(shell cd \"$(shell  dirname \"${BASH_SOURCE[0]}\" )\" && pwd ))\n\n# The local users UID/GID\nLUID := $(shell id -u)\nLGID := $(shell id -g)\n\n# To mount a specific binary using BINARY=/absolute/path/to/binary\nifdef BINARY\n    MOUNTFLAGS += -v $(BINARY):/home/cutter/$(shell basename $(BINARY)):ro\n    RUNFLAGS += /home/cutter/$(shell basename $(BINARY))\nendif\n\nVERSION ?= latest\nIMAGE_NAME ?= rizin/cutter\nCONTAINER_NAME ?= cutter\n\n# This will output the help for each task\n# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html\n.PHONY: help\n\nhelp: ## This help\n\t@awk 'BEGIN {FS = \":.*?## \"} /^[a-zA-Z_-]+:.*?## / {printf \"\\033[36m%-30s\\033[0m %s\\n\", $$1, $$2}' $(MAKEFILE_LIST)\n\n.DEFAULT_GOAL := help\n\n# Build the container\nbuild: ## Build the container\n\tsudo docker build --rm -t $(IMAGE_NAME) .\n\nbuild-nc: ## Build the container without caching\n\tsudo docker build --rm --no-cache -t $(IMAGE_NAME) .\n\nrun: ## Run container\n\tXSOCK=/tmp/.X11-unix && \\\n\tXAUTH=$(shell mktemp /tmp/cutter_tmp.XXX.xauth) && \\\n\txauth nlist $$DISPLAY | sed -e 's/^..../ffff/' | xauth -f $$XAUTH nmerge - && \\\n\tchmod 644 $$XAUTH && \\\n\ttouch $(DIR)/rizinrc && \\\n\tmkdir -p $(DIR)/cutter-config && \\\n\tmkdir -p $(DIR)/sharedFolder && \\\n\tsudo docker run \\\n\t\t-it \\\n\t\t--name $(CONTAINER_NAME) \\\n\t\t--cap-add=SYS_PTRACE \\\n\t\t-e DISPLAY=$$DISPLAY \\\n\t\t-e XAUTHORITY=$$XAUTH \\\n\t\t-e LOCAL_USER_ID=$(LUID) \\\n\t\t-e LOCAL_GROUP_ID=$(LGID) \\\n\t\t-v $$XSOCK:$$XSOCK:ro \\\n\t\t-v $$XAUTH:$$XAUTH \\\n\t\t$(MOUNTFLAGS) \\\n\t\t-v $(DIR)/sharedFolder:/var/sharedFolder \\\n\t\t-v $(DIR)/rizinrc:/home/cutter/.rizinrc \\\n\t\t-v $(DIR)/cutter-config:/home/cutter/.config/rizin \\\n\t\t$(IMAGE_NAME):$(VERSION) $(RUNFLAGS) && \\\n\trm $$XAUTH\n\nget: ## Get the latest Cutter image\n\tsudo docker pull $(IMAGE_NAME):$(VERSION)\n\nstop: ## Stop a running container\n\tsudo docker stop $(CONTAINER_NAME)\n\nremove: ## Remove a (running) container\n\tsudo docker rm -f $(CONTAINER_NAME)\n\nremove-image-force: ## Remove the latest image (forced)\n\tsudo docker rmi -f $(IMAGE_NAME):$(VERSION)\n\n"
  },
  {
    "path": "docker/README.md",
    "content": "# Docker Configuration for Cutter\n\nThese files provide an easy way to deploy *Cutter* in a Docker container. After additional configuration you may want to apply to the `Makefile`, execute `make run`. By default, the *Cutter* image on [Docker Hub](https://hub.docker.com/r/rizin/cutter/) will be used along with additional UID, capability, X and mount settings:\n\n- Xauthority settings which avoid using potentially insecure `xhost` directives. The settings have been adapted from [this post](https://stackoverflow.com/questions/16296753/can-you-run-gui-apps-in-a-docker-container/25280523#25280523).\n- Mount directives to mount a shared folder and rizin configuration files.\n- The UID and GID of the user executing `make run` will also be used for the internal container user to avoid permission problems when sharing files.\n\n## Mounting and Using a Specific Binary\n\nThe `Makefile` allows mounting a single binary file as read-only, which will also be used as an input for *Cutter*. To use this feature, execute `make run BINARY=/absolute/path/to/binary`.\n\n## Additional Notes\n\n- The internal container user doesn't use superuser privileges and is called `cutter`.\n- To check for more options of the `Makefile`, execute `make`.\n"
  },
  {
    "path": "docker/entrypoint.sh",
    "content": "#!/bin/bash\nUSERNAME=\"cutter\"\n\necho \"Cutter: Starting with UID:GID $LOCAL_USER_ID:$LOCAL_GROUP_ID\"\nusermod -u $LOCAL_USER_ID $USERNAME\nusermod -g $LOCAL_GROUP_ID $USERNAME\nexport HOME=/home/$USERNAME\n\nexec su-exec $USERNAME \"/opt/cutter/build/cutter\" $@\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "/doxygen-out\n/build\n/source/api\n/source/_build\n"
  },
  {
    "path": "docs/Doxyfile",
    "content": "# Doxyfile 1.8.15\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"Cutter\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         =\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = ./doxygen-out\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all generated output in the proper direction.\n# Possible values are: None, LTR, RTL and Context.\n# The default value is: None.\n\nOUTPUT_TEXT_DIRECTION  = None\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines (in the resulting output). You can put ^^ in the value part of an\n# alias to insert a newline as if a physical newline was in the original file.\n# When you need a literal { or } or , in the value part of an alias you have to\n# escape them by means of a backslash (\\), this can lead to conflicts with the\n# commands \\{ and \\} for these it is advised to use the version @{ and @} or use\n# a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat\n# .inc files as Fortran files (default is PHP), and .f files as C (default is\n# Fortran), use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 0.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 0\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = NO\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO, these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = NO\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong or incomplete\n# parameter documentation, but not about the absence of documentation. If\n# EXTRACT_ALL is set to YES then this flag will automatically be disabled.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = ../src ../src/core ../src/common ../src/widgets ../src/dialogs ../src/menus ../src/plugins\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: https://www.gnu.org/software/libiconv/) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,\n# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.idl \\\n                         *.ddl \\\n                         *.odl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.cs \\\n                         *.d \\\n                         *.php \\\n                         *.php4 \\\n                         *.php5 \\\n                         *.phtml \\\n                         *.inc \\\n                         *.m \\\n                         *.markdown \\\n                         *.md \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.pyw \\\n                         *.f90 \\\n                         *.f95 \\\n                         *.f03 \\\n                         *.f08 \\\n                         *.f \\\n                         *.for \\\n                         *.tcl \\\n                         *.vhd \\\n                         *.vhdl \\\n                         *.ucf \\\n                         *.qsf \\\n                         *.ice\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = NO\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             = doxygen-images/graph_grid_layout\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via Javascript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have Javascript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: https://developer.apple.com/xcode/), introduced with OSX\n# 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the master .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-\n# folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANSPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment.\n# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: https://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: \\makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = \\makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = YES\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of 'which perl').\n# The default file (with absolute path) is: /usr/bin/perl.\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see:\n# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# http://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSOURCEDIR     = source\nBUILDDIR      = build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\tdoxygen\n\tpython3 apidoc.py\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\nquick:\n\t@$(SPHINXBUILD) -M html \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\nclean:\n\trm -fr doxygen-out\n\trm -fr source/api\n\trm -fr build\n\t@$(SPHINXBUILD) -M clean \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS)\n"
  },
  {
    "path": "docs/apidoc.py",
    "content": "# -*- coding: utf-8 -*-\nimport os\nimport sys\nimport errno\nimport xml.etree.ElementTree\n\n\nALLOWED_TYPES = ['class', 'interface', 'struct', 'union']\n\n\ndef write_file(name, text, destdir):\n    \"\"\"Write the output file for module/package <name>.\"\"\"\n    fname = os.path.join(destdir, '%s.%s' % (name, 'rst'))\n\n    if not os.path.exists(os.path.dirname(fname)):\n        try:\n            os.makedirs(os.path.dirname(fname))\n        except OSError as exc:  # Guard against race condition\n            if exc.errno != errno.EEXIST:\n                raise\n    try:\n        with open(fname, 'r') as target:\n            orig = target.read()\n            if orig == text:\n                return\n    except FileNotFoundError:\n        # Don't mind if it isn't there\n        pass\n\n    with open(fname, 'w') as target:\n        target.write(text)\n\n\ndef format_heading(level, text):\n    \"\"\"Create a heading of <level> [1, 2 or 3 supported].\"\"\"\n    underlining = ['=', '-', '~', ][level - 1] * len(text)\n    return '%s\\n%s\\n\\n' % (text, underlining)\n\n\ndef format_directive(package_type, package, project = None):\n    \"\"\"Create the breathe directive and add the options.\"\"\"\n    directive = '.. doxygen%s:: %s\\n' % (package_type, package)\n    if project:\n        directive += '   :project: %s\\n' % project\n    return directive\n\n\ndef create_package_file(package, package_type, package_id, package_folder, rootpath, destdir):\n    \"\"\"Build the text of the file and write the file.\"\"\"\n    text = format_heading(1, '%s' % (package))\n    text += format_directive(package_type, package)\n\n    xmlfile = os.path.join(rootpath, package_id + '.xml')\n    f = xml.etree.ElementTree.parse(os.path.join(xmlfile))\n\n    write_file(os.path.join(package_folder, package_id), text, destdir)\n\n\ndef create_modules_toc_file(key, value, destdir):\n    \"\"\"Create the module's index.\"\"\"\n    text = format_heading(1, '%s' % value)\n    text += '.. toctree::\\n'\n    text += '   :glob:\\n\\n'\n    text += '   %s/*\\n' % key\n\n    write_file('%slist' % key, text, destdir)\n\n\ndef get_compound_folder(rootpath, compound):\n    fxml = xml.etree.ElementTree.parse(os.path.join(rootpath, compound.get('refid')) + '.xml')\n    loc = fxml.getroot()[0].find('location')\n    dirname = os.path.basename(os.path.split(loc.get('file'))[0])\n    return dirname\n\n\ndef recurse_tree(rootpath, destdir):\n    \"\"\"\n    Look for every file in the directory tree and create the corresponding\n    ReST files.\n    \"\"\"\n    index = xml.etree.ElementTree.parse(os.path.join(rootpath, 'index.xml'))\n\n    for compound in index.getroot():\n        if compound.get('kind') not in ALLOWED_TYPES:\n            continue\n        create_package_file(compound.findtext('name'), compound.get('kind'),\n                            compound.get('refid'), get_compound_folder(rootpath, compound),\n                            rootpath, destdir)\n\ndef get_folders_tree(rootpath):\n    tmp = []\n\n    # Retrieve the subfolders indexes\n    for root, _, files in os.walk(rootpath):\n        for xmlfile in files:\n            if not xmlfile.startswith('dir_'):\n                continue\n            tmp.append(xmlfile)\n\n    # Iterate on them\n    dirs = []\n    for xmlfile in tmp:\n        data = xml.etree.ElementTree.parse(os.path.join(rootpath, xmlfile))\n        if not data:\n            continue\n        for compound in data.getroot():\n            name = compound.findtext('compoundname')\n            dirs.append(name)\n\n    return dirs\n\n\ndef main():\n    rootpath = './doxygen-out/xml'\n    destdir = './source/api'\n\n    if not os.path.exists(destdir):\n        os.makedirs(destdir)\n\n    dirs = sorted(get_folders_tree(rootpath))\n    source_root = dirs[0]\n    source_dirs = dirs[1:]\n    out_dirs = [os.path.basename(d) for d in dirs]\n\n    # TODO Handle only one level subfolders\n    for key in out_dirs:\n        ddir = os.path.join(destdir, key)\n        if not os.path.exists(ddir):\n            os.makedirs(ddir)\n        create_modules_toc_file(key, key.capitalize(), destdir)\n    recurse_tree(rootpath, destdir)\n\n\n# So program can be started with \"python -m breathe.apidoc ...\"\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "docs/cutter_theme/layout.html",
    "content": "{% extends 'basic/layout.html' %}\n{% block extrahead %}\n<link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css\" integrity=\"sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN\" crossorigin=\"anonymous\">\n<link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css\" integrity=\"sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk\" crossorigin=\"anonymous\">\n<script src=\"https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js\" integrity=\"sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo\" crossorigin=\"anonymous\"></script>\n<script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js\" integrity=\"sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI\" crossorigin=\"anonymous\"></script>\n{% endblock %}\n\n{# https://www.sphinx-doc.org/en/master/templating.html #}\n\n{% block relbar1 %}{% if pagename != 'index' %}{{ relbar() }}{% endif %}{% endblock %}\n\n{% block relbar2 %}\n{% endblock %}\n\n{% block header %}\n    {% if pagename == 'index' %}<div class=indexwrapper>{% endif %}\n{% endblock %}\n\n{% block sidebar1 %}\n{% endblock %}\n\n{% block sidebar2 %}\n {%- if render_sidebar %}\n  <div class=\"sphinxsidebar\" role=\"navigation\" aria-label=\"main navigation\">\n    <div class=\"sphinxsidebarwrapper\">\n      {%- block sidebarlogo %}\n      {%- if logo %}\n        <p class=\"logo\"><a href=\"https://cutter.re/\">\n          <img class=\"logo\" src=\"{{ pathto('_static/' + logo, 1) }}\" alt=\"Logo\"/>\n        </a></p>\n      {%- endif %}\n      {%- endblock %}\n      <a href=\"{{ pathto(master_doc) }}\">{{ project }} documentation</a>\n      {%- if sidebars != None %}\n        {#- new style sidebar: explicitly include/exclude templates #}\n        {%- for sidebartemplate in sidebars %}\n        {%- include sidebartemplate %}\n        {%- endfor %}\n      {%- else %}\n        {#- old style sidebars: using blocks -- should be deprecated #}\n      {%- endif %}\n    </div>\n  </div>\n {%- endif %}\n{% endblock %}\n\n{% block footer %}\n    {{ super() }}\n    {% if pagename == 'index' %}</div>{% endif %}\n{% endblock %}\n"
  },
  {
    "path": "docs/cutter_theme/static/admonitions.css",
    "content": "/* Admonition styles */\ndiv.admonition {\n    padding: 8px 35px 8px 0px;\n    margin-bottom: 20px;\n    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n    border: 1px solid ;\n    color: #3a87ad;\n    background-color: #d9edf7;\n    border-color: #bce8f1;\n\n    -webkit-border-radius: 4px;\n       -moz-border-radius: 4px;\n            border-radius: 4px;\n  }\n  \n  div.admonition p {\n    margin: 0.5em 1em 0.5em 1em;\n    padding: 0;\n  }\n  \n  div.admonition pre {\n    margin: 0.4em 1em 0.4em 1em;\n  }\n  \n  div.admonition p.admonition-title {\n    margin: 0;\n    padding: 0.1em 0 0.1em 0.5em;\n    font-weight: bold;\n  }\n  \n  div.admonition ul, div.admonition ol {\n    margin: 0.1em 0.5em 0.5em 3em;\n    padding: 0;\n  }\n  \n  /* -- danger, error -- */\n  div.danger,\n  div.error {\n    color: #b94a48;\n    background-color: #f2dede;\n    border-color: #eed3d7;\n  }\n  \n  \n  /* -- warning, caution, attention -- */\n  div.warning,\n  div.caution,\n  div.attention {\n    color: #c09853;\n    background-color: #fcf8e3;\n    border: 1px solid #fbeed5;\n  }\n  \n  /* -- note, important -- */\n  div.note,\n  div.important {\n    color: #468847;\n    background-color: #dff0d8;\n    border-color: #d6e9c6;\n  }\n  \n  /* -- hint, tip -- */\n  div.hint,\n  div.tip {\n    color: #3a87ad;\n    background-color: #d9edf7;\n    border-color: #bce8f1;\n  }\n  \n  div.danger p.admonition-title:before,\n  div.error p.admonition-title:before,\n  div.warning p.admonition-title:before,\n  div.caution p.admonition-title:before,\n  div.attention p.admonition-title:before,\n  div.important p.admonition-title:before,\n  div.note p.admonition-title:before,\n  div.hint p.admonition-title:before,\n  div.tip p.admonition-title:before\n  {\n    display: inline-block;\n    font-family: FontAwesome;\n    font-style: normal;\n    font-weight: normal;\n    line-height: 1;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n  }\n  \n  div.danger p.admonition-title:before,\n  div.error p.admonition-title:before {\n    content: \"\\f06a\\00a0\";\n  }\n  div.warning p.admonition-title:before,\n  div.caution p.admonition-title:before,\n  div.attention p.admonition-title:before {\n    content: \"\\f071\\00a0\";\n  }\n  div.important p.admonition-title:before,\n  div.note p.admonition-title:before {\n    content: \"\\f05a\\00a0\";\n  }\n  div.hint p.admonition-title:before,\n  div.tip p.admonition-title:before {\n    content: \"\\f0eb\\00a0\";\n  }\n  "
  },
  {
    "path": "docs/cutter_theme/static/cutter.css_t",
    "content": "/*\n * cutter.css_t\n * based on flasky.css_t by Armin Ronacher\n * ~~~~~~~~~~~~\n *\n */\n\n{% set page_width = '1300px' %}\n{% set sidebar_width = '220px' %}\n\n@import url('https://fonts.googleapis.com/css?family=Poppins&display=swap');\n@import url('https://fonts.googleapis.com/css?family=Inconsolata&display=swap');\n@import url('basic.css');\n\n/* -- page layout ---------------------------------------------------------- */\n\nbody {\n    font-family: \"Poppins\", sans-serif;\n    font-size: 16px;\n    background-color: white;\n    color: #0d1b33;\n    margin: 0;\n    padding: 0;\n}\n\ndiv.document {\n    width: {{ page_width }};\n    margin: 30px auto 0 auto;\n}\n\ndiv.documentwrapper {\n    float: left;\n    width: 100%;\n}\n\ndiv.bodywrapper {\n    margin: 0 0 0 {{ sidebar_width }};\n}\n\ndiv.sphinxsidebar {\n    width: {{ sidebar_width }};\n}\n\nhr {\n    border: 1px solid #b1b4b6;\n}\n\ndiv.body {\n    background-color: #fff;\n    color: #3e4349;\n    padding: 0 30px 0 30px;\n}\n\nimg.floatingflask {\n    padding: 0 0 10px 10px;\n    float: right;\n}\n\ndiv.footer {\n    width: {{ page_width }};\n    margin: 20px auto 30px auto;\n    font-size: 14px;\n    color: #888;\n    text-align: right;\n}\n\ndiv.footer a {\n    color: #888;\n}\n\ndiv.sphinxsidebar a {\n    color: #444;\n    text-decoration: none;\n    border-bottom: 1px solid #999;\n}\n\ndiv.sphinxsidebar a:hover {\n    color: #216ae6;\n    border-bottom: 1px solid #999;\n}\n\ndiv.sphinxsidebar {\n    font-size: 14px;\n    line-height: 1.5;\n}\n\ndiv.sphinxsidebarwrapper {\n    padding: 18px 10px;\n}\n\ndiv.sphinxsidebarwrapper p.logo {\n    padding: 0 0 20px 0;\n    margin: 0;\n    text-align: center;\n}\n\ndiv.sphinxsidebar h3,\ndiv.sphinxsidebar h4 {\n    font-family: Content-font, Roboto, sans-serif;\n    color: #444;\n    font-size: 18px;\n    font-weight: normal;\n    margin: 0 0 5px 0;\n    padding: 0;\n}\n\ndiv.sphinxsidebar h4 {\n    font-size: 20px;\n}\n\ndiv.sphinxsidebar h3 a {\n    color: #444;\n}\n\ndiv.sphinxsidebar p.logo a,\ndiv.sphinxsidebar h3 a,\ndiv.sphinxsidebar p.logo a:hover,\ndiv.sphinxsidebar h3 a:hover {\n    border: none;\n}\n\ndiv.sphinxsidebar p {\n    color: #555;\n    margin: 10px 0;\n}\n\ndiv.sphinxsidebar ul {\n    margin: 10px 0;\n    padding: 0;\n    color: #000;\n}\n\ndiv.sphinxsidebar input {\n    border: 1px solid #ccc;\n    font-family: Content-font, Roboto, sans-serif;\n    font-size: 1em;\n}\n\n/* -- body styles ---------------------------------------------------------- */\n\na {\n    color: #004b6b;\n    text-decoration: underline;\n}\n\na:hover {\n    color: #6d4100;\n    text-decoration: underline;\n}\n\ndiv.body h1,\ndiv.body h2,\ndiv.body h3,\ndiv.body h4,\ndiv.body h5,\ndiv.body h6 {\n    color: #041e49;\n    font-family: Content-font, Roboto, sans-serif;\n    font-weight: 400;\n    margin: 30px 0 10px 0;\n    padding: 0;\n}\n\n{% if theme_index_logo %}\ndiv.indexwrapper h1 {\n    text-indent: -999999px;\n    background: url({{ theme_index_logo }}) no-repeat center center;\n    height: {{ theme_index_logo_height }};\n}\n{% endif %}\ndiv.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }\ndiv.body h2 { font-size: 180%; }\ndiv.body h3 { font-size: 150%; }\ndiv.body h4 { font-size: 130%; }\ndiv.body h5 { font-size: 100%; }\ndiv.body h6 { font-size: 100%; }\n\na.headerlink {\n    color: #216ae6;\n    padding: 0 4px;\n    text-decoration: none;\n}\n\na.headerlink:hover {\n    color: #041e49;\n}\n\ndiv.body p, div.body dd, div.body li {\n    line-height: 1.4;\n}\n\ndt:target, .highlight {\n    background: #faf3e8;\n}\n\ndiv.note {\n    background-color: #eee;\n    border: 1px solid #ccc;\n}\n\ndiv.seealso {\n    background-color: #ffc;\n    border: 1px solid #ff6;\n}\n\ndiv.topic {\n    background-color: #eee;\n}\n\nimg.screenshot {\n}\n\nimg.screenshot {\n    -moz-box-shadow: 2px 2px 4px #eee;\n    -webkit-box-shadow: 2px 2px 4px #eee;\n    box-shadow: 2px 2px 4px #eee;\n}\n\ntable.docutils {\n    border: 1px solid #888;\n    -moz-box-shadow: 2px 2px 4px #eee;\n    -webkit-box-shadow: 2px 2px 4px #eee;\n    box-shadow: 2px 2px 4px #eee;\n}\n\ntable.docutils td, table.docutils th {\n    border: 1px solid #888;\n    padding: 0.25em 0.7em;\n}\n\ntable.field-list, table.footnote {\n    border: none;\n    -moz-box-shadow: none;\n    -webkit-box-shadow: none;\n    box-shadow: none;\n}\n\ntable.footnote {\n    margin: 15px 0;\n    width: 100%;\n    border: 1px solid #eee;\n    background: #fdfdfd;\n    font-size: 0.9em;\n}\n\ntable.footnote + table.footnote {\n    margin-top: -15px;\n    border-top: none;\n}\n\ntable.field-list th {\n    padding: 0 0.8em 0 0;\n}\n\ntable.field-list td {\n    padding: 0;\n}\n\ntable.footnote td.label {\n    width: 0;\n    padding: 0.3em 0 0.3em 0.5em;\n}\n\ntable.footnote td {\n    padding: 0.3em 0.5em;\n}\n\ndl {\n    margin: 0;\n    padding: 0;\n}\n\ndl dd {\n    margin-left: 30px;\n}\n\nblockquote {\n    margin: 0 0 0 30px;\n    padding: 0;\n}\n\nul, ol {\n    margin: 10px 0 10px 30px;\n    padding: 0;\n}\n\npre {\n    background: #fafafa;\n    padding: 7px 30px;\n    margin: 15px -30px;\n    line-height: 1.3;\n    font-family: 'Inconsolata', monospace;\n}\n\nspan.pre {\n    color: #e83e8c;\n    word-break: break-word;\n    font-family: SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;\n}\n\ndl pre, blockquote pre, li pre {\n    margin-left: -60px;\n    padding-left: 60px;\n}\n\ndl dl pre {\n    margin-left: -90px;\n    padding-left: 90px;\n}\n\na.reference {\n    color: #216ae6;\n    text-decoration: none;\n}\n\na.reference:hover {\n    color: #216ae6;\n    border-bottom: 1px solid;\n}\n\na.footnote-reference {\n    color: #216ae6;\n    text-decoration: none;\n    font-size: 0.7em;\n    vertical-align: top;\n    border-bottom: 1px solid;\n}\n\na.footnote-reference:hover {\n    color: #216ae6;\n    border-bottom: 1px solid;\n}\n\n/* Notes box */\n.admonition {\n    border-radius: 5px;\n}\n\n.admonition.note {\n    color: #0c5460;\n    background-color: #d1ecf1;\n    border-color: #bee5eb;\n}\n\n/* API Reference */\n\n.rst-content dl:not(.docutils) dt {\n}\n\ncode.descname {\n    font-size: 14px;\n}\n\n.sig-paren {\n    font-size: 14px;\n}\n\n@media screen and (max-width: 1050px) {\n    body {\n        margin: 0;\n        padding: 20px 30px;\n    }\n\n    div.documentwrapper {\n        float: none;\n        background: white;\n        margin: 0;\n    }\n\n    div.sphinxsidebar {\n        display: block;\n        float: none;\n        width: 102.5%;\n        margin: 50px -30px 0;\n        padding: 10px 30px;\n        background: #333;\n        color: white;\n    }\n\n    div.sphinxsidebarwrapper {\n        padding: 0;\n    }\n\n    div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,\n    div.sphinxsidebar h3 a {\n        color: white;\n    }\n\n    div.sphinxsidebar a {\n        color: #aaa;\n    }\n\n    div.sphinxsidebar p.logo {\n        display: none;\n    }\n\n    div.document {\n        width: 100%;\n        margin: 0;\n    }\n\n    div.related {\n        display: block;\n        margin: 0;\n        padding: 10px 0 20px 0;\n    }\n\n    div.related ul,\n    div.related ul li {\n        margin: 0;\n        padding: 0;\n    }\n\n    div.bodywrapper {\n        margin: 0;\n    }\n\n    div.body {\n        min-height: 0;\n        padding: 0;\n    }\n\n    .rtd_doc_footer {\n        display: none;\n    }\n\n    div.footer {\n        width: auto;\n        margin: 0 -30px -20px;\n        padding: 10px 30px 20px;\n        background: #333;\n    }\n\n    ul {\n        margin-left: 0;\n    }\n}\n/* Kbd Style */\n\nkbd {\n    font-family: Consolas, \"Lucida Console\", monospace;\n    display: inline-block;\n    border-radius: 3px;\n    padding: 0px 4px;\n    box-shadow: 1px 1px 1px #777;\n    margin: 2px;\n    font-size: small;\n    vertical-align: text-bottom;\n    background: #eee;\n    font-weight: 500;\n    color: #555;\n    cursor: pointer;\n    font-variant: small-caps;\n    font-weight: 600;\n    letter-spacing: 0.5px;\n    letter-spacing: 1px;\n    -webkit-touch-callout: none;\n    -webkit-user-select: none;\n    -khtml-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n}\n\nkbd:hover,\nkbd:hover * {\n    color: black;\n    box-shadow: 1px 1px 1px #333;\n}\n\nkbd:active,\nkbd:active * {\n    color: black;\n    box-shadow: 1px 1px 0px #ddd inset;\n}\n\nkbd kbd {\n    padding: 0px;\n    margin: 0 1px;\n    box-shadow: 0px 0px 0px black;\n    vertical-align: baseline;\n    background: none;\n}\n\nkbd kbd:hover {\n    box-shadow: 0px 0px 0px black;\n}\n\nkbd:active kbd {\n    box-shadow: 0px 0px 0px black;\n    background: none;\n}\n"
  },
  {
    "path": "docs/cutter_theme/static/cutter.js",
    "content": "\n// Make sure that external links are opened in new tabs\n$(document).ready(function() {\n    $(\"a[href^='http']\").attr('target','_blank');\n});"
  },
  {
    "path": "docs/cutter_theme/theme.conf",
    "content": "[theme]\ninherit = basic\nstylesheet = cutter.css\npygments_style = flasky\n\n[options]\nindex_logo =\nindex_logo_height = 120px\ntouch_icon =\ngithub_fork =\ngithub_ribbon_color = darkblue_121621\n"
  },
  {
    "path": "docs/source/api.rst",
    "content": ".. _api:\n\nAPI Reference\n==============\n\n.. toctree::\n   :glob:\n\n   api/*\n"
  },
  {
    "path": "docs/source/building.rst",
    "content": "Building\n========\n\n.. note::\n\n If you just want to use the latest Release version of Cutter, please note\n that we provide pre-compiled binaries for Windows, Linux, and macOS on\n our `release page <https://github.com/rizinorg/cutter/releases/latest>`_ and\n `CI page <https://nightly.link/rizinorg/cutter/workflows/ccpp/dev>`_ for latest development builds.\n\nThis page describes how to do a basic build from the command line. If you are planning to modify Cutter it is recommended to also read our :doc:`development environment setup</contributing/code/ide-setup>`.\n\nGetting the Source\n------------------\n\nMake sure you've ``git`` installed in your system (`Installation guide <https://git-scm.com/book/en/v2/Getting-Started-Installing-Git>`_) and do the following:\n\n.. code-block:: sh\n\n   git clone --recurse-submodules https://github.com/rizinorg/cutter\n\n\nThis will clone the Cutter source and its dependencies(rizin, etc.)\nunder **cutter** and you should see the following dir structure:\n\n.. code-block:: sh\n\n    cutter/-|\n            |-docs/     # Cutter Documentation\n            |-rizin/    # Rizin submodule\n            |-src/      # Cutter Source Code\n\nFollowing sections assume that **cutter** is your working dir. (if not, do ``cd cutter``)\n\nBuilding on Linux\n-----------------\n\nRequirements\n~~~~~~~~~~~~\n\nOn Linux, you will need:\n\n* build-essential\n* cmake\n* meson\n* libzip-dev\n* libzlib-dev\n* qt6\n* qt6-svg\n* pkgconf\n* curl*\n* python-setuptools*\n* KSyntaxHighlighter**\n* graphviz**\n* shiboken6**\n* pyside6**\n\n `*` Recommended while building with ``make``/``Cmake``.\n\n `**` Optional. If present, these add extra features to Cutter. See `CMake Building Options`_.\n\nOn Debian-based Linux distributions, all of these essential packages can be installed with this single command:\n\n::\n\n   sudo apt install build-essential cmake meson pkgconf libzip-dev zlib1g-dev qt6-base-dev qt6-tools-dev qt6-tools-dev-tools libqt6svg6-dev libqt6core5compat6-dev libqt6svgwidgets6 qt6-l10n-tools\n\nDepending on your configuration you'll might also need the following:\n\n::\n\n  # When building with CUTTER_ENABLE_GRAPHVIZ (Default)\n  sudo apt install libgraphviz-dev\n\n.. note::\n For Ubuntu 20.04 and lower (or in any case you get an error ``Meson version is x but project requires >=y``), ``meson`` should be installed with ``pip install --upgrade --user meson``.\n\nOn Arch-based Linux distributions:\n\n::\n\n   sudo pacman -Syu --needed base-devel cmake meson qt6-base qt6-svg qt6-tools\n\n   # When building with CUTTER_ENABLE_KSYNTAXHIGHLIGHTING (Default)\n   sudo pacman -Syu --needed syntax-highlighting\n   # When building with CUTTER_ENABLE_GRAPHVIZ (Default)\n   sudo pacman -Syu --needed graphviz\n   # When building with CUTTER_ENABLE_PYTHON and CUTTER_ENABLE_PYTHON_BINDINGS\n   sudo pacman -Syu --needed pyside6 shiboken6\n\nOn dnf/yum based distributions:\n\n::\n\n   sudo dnf install -y gcc gcc-c++ make cmake meson qt6-qtbase-devel qt6-qtsvg-devel qt6-qttools-devel qt6-qt5compat-devel\n   # Optional packages\n   sudo dnf install -y graphviz-devel kf6-syntax-highlighting-devel python3-devel shiboken6 python3-pyside6-devel clang\n\n\nOn older Linux systems not supported by QT6 you can use Qt 5.15. Use of Qt5 on operating systems other than Linux is untested.\n\nBuilding Steps\n~~~~~~~~~~~~~~\n\nThe recommended way to build Cutter on Linux is by using CMake. Simply invoke CMake to build Cutter and its dependency Rizin.\n\n.. code:: sh\n\n   mkdir build && cd build\n   cmake ..\n   cmake --build .\n\nIf your operating system has a newer version of CMake (> v3.12) you can use this cleaner solution:\n\n.. code:: sh\n\n   cmake -B build\n   cmake --build build\n\nIf you want to use Cutter with another version of Rizin you can set ``-DCUTTER_USE_BUNDLED_RIZIN=OFF``. Note that using a version of Rizin which isn't the version Cutter is using can cause issues and the compilation might fail.\n\n.. note::\n\n   If you are interested in building Cutter with support for Python plugins,\n   Syntax Highlighting and more, please look at the full list of `CMake Building Options`_.\n\n\nAfter the build process is complete, you should have the ``Cutter`` executable in the **build** dir.\nYou can now execute Cutter like this:\n\n.. code:: sh\n\n   ./build/cutter\n\n\nMaking Linux distribution specific packages\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nWhen making a distribution specific package, the default install target should give you a good starting point.\nIt uses CMake built-in functionality and `GNUInstallDirs <https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html?highlight=gnu%20directories>`_ for\ninstalling the executable, desktop file, headers and other files required for plugin compilation. See CMake documentation for adjusting installed file locations and properties.\nIt shouldn't be necessary to manually copy files from plain build.\n\nIt is recommended to build and package rizin as a separate package so that it can be used with or without Cutter. Doing that will also give more control over the way rizin dependencies are handled. We are trying to maintain\ncompatibility with latest rizin release at the time of Cutter release and making a new Cutter release when new rizin version is released.\n\nIf you are packaging Cutter, users will appreciate it if you also package `rz-ghidra <https://github.com/rizinorg/rz-ghidra>`_ and `jsdec <https://github.com/rizinorg/jsdec>`_ decompilers as optional packages.\nIt should be possible to compile Cutter plugins against proper Cutter installation without having direct access to Cutter source code.\n\nIf the names \"Cutter\" or \"cutter\" conflict with other packages or their content, \"rz-cutter\" can be used.\n\n:Configuration for packaging:\n\n* ``-DCMAKE_BUILD_TYPE=Release`` turn on release optimizations, unless your distro has more specific guidelines for common compiler options.\n* ``CUTTER_USE_BUNDLED_RIZIN=OFF`` turn off use of rizin from submodule to use previously packaged rizin. Note that keeping it on doesn't install rizin in a way suitable for linux packaging without doing additional manual steps making packaging process more complex. Bundled rizin will also likely conflict with standalone rizin package.\n* Correct install prefix. By default CMake will install to /usr/local suitable for user builds. Change it according to your distro packaging guidelines.\n* ``CUTTER_ENABLE_PYTHON`` and  ``CUTTER_ENABLE_PYTHON_BINDINGS`` it is recommended to turn on for complete user experience. May require manual path specification on distros with multiple python versions.\n* ``CUTTER_ENABLE_GRAPHVIZ`` and ``CUTTER_ENABLE_KSYNTAXHIGHLIGHTING`` optional but nice to have since they are available on most distros.\n* ``CUTTER_EXTRA_PLUGIN_DIRS`` use it to specify additional plugin search locations if distro packaging guidelines require you placing them in locations Cutter doesn't use by default.\n* ``CUTTER_VERSION_SUFFIX`` can be used to differentiate multiple builds based on same upstream Cutter version, distro specific package build number and similar.\n* ``CUTTER_INCLUDE_GIT_HASH=OFF`` By default cutter includes git commit hash in the full version string. If you are building from source tarrball or other source which isn't original git repository you might want to disable this.\n\nBuilding on Windows\n-------------------\n\nRequirements\n~~~~~~~~~~~~\n\nCutter works on Windows 10 or newer.\nTo compile Cutter it is necessary to have the following installed:\n\n* A version of `Visual Studio <https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=16>`_ (2019 or newer)\n* `CMake <https://cmake.org/download/>`_\n* `Qt 6 <https://www.qt.io/download-qt-installer>`_  See :doc:`development environment setup</contributing/code/ide-setup>` for alternative ways of obtaining Qt that don't require Qt account.\n* `Meson <https://mesonbuild.com/Getting-meson.html#installing-meson-with-pip>`_\n* `Ninja <https://github.com/ninja-build/ninja/releases/latest>`_\n\nBuilding Steps\n~~~~~~~~~~~~~~~\n\nTo build Cutter on Windows machines using CMake,\nyou will have to make sure that the executables are available\nin your ``%PATH%`` environment variable.\n\nYou can check if the binaries are available by opening PowerShell and \nexecuting the following commands.\n\n.. code:: powershell\n\n   ninja --version\n   meson --version\n   cmake --version\n\nIf they are not available, you can use PowerShell to add them to your PATH one by one:\n\n.. code:: powershell\n\n   $Env:Path += \";C:\\enter\\path\\here\"\n\n\nNote that the paths below may vary depending on your version of Qt and Visual Studio.\n\n.. code:: powershell\n\n   # Add the following directory to your PATH\n   $Env:Path += \";C:\\Qt\\6.7.2\\msvc2019_64\\bin\"\n\n   # Build Cutter\n   cmake -B build\n   cmake --build build\n\n\nAfter the compilation completes, the ``cutter.exe`` binary will be available in ``.\\build\\Debug\\cutter.exe``.\n\n\n\nBuilding on macOS\n-------------------\n\nRequirements\n~~~~~~~~~~~~\n\n* XCode\n* CMake\n* Qt\n* meson\n* ninja\n\n\nFor basic build all dependencies except XCode can be installed using homebrew:\n\n::\n\n   brew install cmake qt6 meson ninja\n\n\nRecommended Way for dev builds\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code:: batch\n\n   mkdir build\n   cd build\n   cmake .. -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/qt6\n   make\n\n--------------\n\nCMake Building Options\n----------------------\n\nNote that there are some major building options available:\n\n* ``CUTTER_USE_BUNDLED_RIZIN`` automatically compile Rizin from submodule (Enabled by default).\n* ``CUTTER_ENABLE_PYTHON`` compile with Python support, required for Python plugins.\n* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken, required for Python plugins!\n* ``CUTTER_ENABLE_KSYNTAXHIGHLIGHTING`` use KSyntaxHighlighting for code highlighting.\n* ``CUTTER_ENABLE_GRAPHVIZ`` enable Graphviz for graph layouts.\n* ``CUTTER_EXTRA_PLUGIN_DIRS`` List of addition plugin locations. Useful when preparing package for Linux distros that have strict package layout rules.\n* ``CUTTER_QT`` Qt major version to use. Defaults to 6. Allowed values: 5, 6. \n\nCutter binary release options, not needed for most users and might not work easily outside CI environment: \n\n* ``CUTTER_ENABLE_DEPENDENCY_DOWNLOADS`` Enable downloading of dependencies. Setting to OFF doesn't affect any downloads done by Rizin build. This option is used for preparing Cutter binary release packges. Turned off by default.\n* ``CUTTER_PACKAGE_DEPENDENCIES`` During install step include the third party dependencies. This option is used for preparing Cutter binary release packages. \n\nFor full list of Cutter specific build options and their description see CMakeCache.txt after configuring the project or use a graphical CMake configurator if your IDE provides one.\n\nThese options can be enabled or disabled from the command line arguments passed to CMake.\nFor example, to build Cutter with support for Python plugins, you can run this command:\n\n::\n\n   cmake -B build -DCUTTER_ENABLE_PYTHON=ON -DCUTTER_ENABLE_PYTHON_BINDINGS=ON\n\nOr if one wants to explicitly disable an option:\n\n::\n\n   cmake -B build -DCUTTER_ENABLE_PYTHON=OFF\n\n\n\n\n--------------\n\nTroubleshooting\n---------------\n\n* **Cmake can't find Qt**\n\n    Cmake: qt development package not found\n\nDepending on how Qt installed (Distribution packages or using the Qt\ninstaller application), CMake may not be able to find it by itself if it\nis not in a common place. If that is the case, double-check that the\ncorrect Qt version is installed. Locate its prefix (a directory\ncontaining bin/, lib/, include/, etc.) and specify it to CMake using\n``CMAKE_PREFIX_PATH`` in the above process, e.g.:\n\n::\n\n   rm CMakeCache.txt # the cache may be polluted with unwanted libraries found before\n   cmake -DCMAKE_PREFIX_PATH=/opt/Qt/5.9.1/gcc_64 ..\n\n* **Rizin's librz_*.so cannot be found when running Cutter**\n\n   ./cutter: error while loading shared libraries: librz_lang.so: cannot open shared object file: No such file or directory\n\nThe exact Rizin .so file that cannot be found may vary. On some systems, the linker by default uses RUNPATH instead of RPATH which is incompatible with the way Rizin is currently compiled. It results in some of the Rizin libraries not being found when running cutter. You can verify if this is the problem by running `ldd ./cutter`. If all the Rizin libraries are missing you have a different problem.\nThe workaround is to either add the `--disable-new-dtags` linker flag when compiling Cutter or add the Rizin installation path to LD_LIBRARY_PATH environment variable.\n\n::\n\n   cmake -DCMAKE_EXE_LINKER_FLAGS=\"-Wl,--disable-new-dtags\"  ..\n\n* **rz_*.h: No such file or directory**\n\n    Eg: rz_util/rz_annotated_code.h: No such file or directory\n\nIf you face an error where some header file starting with ``rz_`` is missing, you should check the **rizin** submodule and\nmake sure it is in sync with upstream **Cutter** repo. Simply run:\n\n::\n\n   git submodule update --init --recursive\n\n* **rz_core development package not found**\n\nIf you installed Rizin and still encounter this error, it could be that your\n``PATH`` environment variable is set improperly (doesn’t contain\n``/usr/local/bin``). You can fix this by adding the Rizin installation dir to\nyour ``PATH`` variable.\n\nmacOS specific solutions:\n\nOn macOS, that can also be, for example, due to ``Qt Creator.app``\nbeing copied over to ``/Applications``. To fix this, append\n``:/usr/local/bin`` to the ``PATH`` variable within the *Build\nEnvironment* section in Qt Creator. See the screenshot below should you\nencounter any problems.\n\nYou can also try:\n\n-  ``PKG_CONFIG_PATH=$HOME/bin/prefix/rizin/lib/pkgconfig cmake ...``\n\n.. image:: images/cutter_path_settings.png\n\nYou can also install Rizin into ``/usr/lib/pkgconfig/`` and then\nadd a variable ``PKG_CONFIG_PATH`` with the value ``/usr/lib/pkgconfig/``.\n\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Configuration file for the Sphinx documentation builder.\n#\n# This file does only contain a selection of the most common options. For a\n# full list see the documentation:\n# http://www.sphinx-doc.org/en/master/config\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\n# import os\n# import sys\n# sys.path.insert(0, os.path.abspath('.'))\n\n\n# -- Project information -----------------------------------------------------\n\nproject = 'Cutter'\ncopyright = '2020, The Cutter Developers'\nauthor = 'The Cutter Developers'\n\n# The short X.Y version\nversion = '2.4'\n# The full version, including a2lpha/beta/rc tags\nrelease = '2.4.0'\n\n\n# -- General configuration ---------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'breathe',\n    'recommonmark',\n    'sphinx.ext.autosectionlabel'\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = '.rst'\nsource_suffix = {\n    '.rst': 'restructuredtext',\n    '.md': 'markdown',\n}\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = ['_build']\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'rainbow_dash'\n\n\nautosectionlabel_prefix_document = True\nautosectionlabel_maxdepth = 2\n\n# -- Options for Breathe -----------------------------------------------------\n\nbreathe_projects = { 'cutter': '../doxygen-out/xml' }\nbreathe_default_project = 'cutter'\nbreathe_default_members = ('members', 'undoc-members')\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'cutter_theme'\nhtml_theme_path = ['..']\nhtml_logo = '../../src/img/cutter.ico'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\n# html_static_path = ['static']\n\nhtml_js_files = [\n    'cutter.js',\n]\n\nhtml_css_files = [\n    'cutter.css',\n    'admonitions.css'\n]\n\n# Custom sidebar templates, must be a dictionary that maps document names\n# to template names.\n#\n# The default sidebars (for documents that don't match any pattern) are\n# defined by theme itself.  Builtin themes are using these templates by\n# Default: html_sidebars = {\n#   '**': ['globaltoc.html', 'sourcelink.html', 'searchbox.html'],\n#   'using/windows': ['windowssidebar.html', 'searchbox.html'],\n# }\nhtml_sidebars = {\n   '**': ['globaltoc.html', 'searchbox.html'],\n}\n\n# -- Options for HTMLHelp output ---------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'CutterDoc'\n\n\n# -- Options for LaTeX output ------------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'Cutter.tex', 'Cutter Documentation',\n     'The Cutter Developers', 'manual'),\n]\n\n\n# -- Options for manual page output ------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'cutter', 'Cutter Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output ----------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'Cutter', 'Cutter Documentation',\n     author, 'Cutter', 'One line description of project.',\n     'Miscellaneous'),\n]\n\n\n# -- Options for Epub output -------------------------------------------------\n\n# Bibliographic Dublin Core info.\nepub_title = project\n\n# The unique identifier of the text. This can be a ISBN number\n# or the project homepage.\n#\n# epub_identifier = ''\n\n# A unique identification for the text.\n#\n# epub_uid = ''\n\n# A list of files that should not be packed into the epub file.\nepub_exclude_files = ['search.html']\n"
  },
  {
    "path": "docs/source/contributing/code/development-guidelines.rst",
    "content": "\nCutter Development Guidelines\n===============================\n\n.. note::\n   New to Cutter development? Check out our :doc:`tutorial for new developers <getting-started>`.\n\n\nCommon Usage\n--------------\n\nCutterCore Class\n~~~~~~~~~~~~~~~~\n\nThis is the main class where every link with Rizin is made. It is *unique*\nacross the whole process. To access it, simply call ``Core()``.\n\nExample:\n\n.. code:: cpp\n\n   Core()->getOffset();\n\n\nSeek the Current File\n~~~~~~~~~~~~~~~~~~~~~\n\nTo modify Rizin seek use ``CutterCore::seek(const RVA offset)``. This\nis important because it will emit a\n``CutterCore::seekChanged(RVA offset)`` signal. Never ever call\n``cmd(\"s offset\")``;\n\nExample:\n\n.. code:: cpp\n\n   Core()->seek(0x00C0FFEE);\n\n.. note::\n\n Cutter also supports a silent seek which doesn't trigger the ``seekChanged`` event and doesn't add new entries to the seek history.\n\n\nCreating a Widget\n~~~~~~~~~~~~~~~~~\n\nMake sure to connect the ``CutterCore::seekChanged(RVA offset)`` signal\nso your widget refreshes its output when Rizin seek is modified\n(switching to another function, etc.).\n\nCoding Style\n------------\n\nIn general, we follow a slightly customized version of `the official Qt guidelines <https://wiki.qt.io/Qt_Coding_Style>`__ \nto format the code. Before sending a pull request, you will need to use `clang-format <https://clang.llvm.org/docs/ClangFormat.html>`__ (version 8 or newer)\nto format the code. The command line for formatting the code according\nto the style is:\n\n.. code:: bash\n\n   clang-format -style=file -i src/filename.cpp\n\nIf your changes were done on many files across the codebase, you can use this oneliner to tun ``clang-format`` on the entire 'src' directory:\n\n.. code:: bash\n\n   find ./src -regex '.*\\.\\(cpp\\|h\\)' -exec clang-format -style=file -i {} \\;\n\nIn contrast to the official guidelines of Qt, in Cutter we always use curly braces in conditional statements, even if the body of a conditional statement contains only one line.\n\n.. code:: cpp\n\n   // Wrong\n   if (address.isEmpty())\n      return false;\n   \n   // Correct\n   if (address.isEmpty()) {\n      return false;\n   }\n   \n   // Wrong\n   for (int i = 0; i < 10; ++i)\n      qDebug(\"%i\", i);\n   \n   // Correct\n   for (int i = 0; i < 10; ++i) {\n      qDebug(\"%i\", i);\n   }\n\n\nIncludes\n~~~~~~~~\n\nStrive to include only **required** definitions inside header files.\nThis will avoid triggering additional unnecessary compilations.\n\nIf you only need to know that a class exists but don't need the prototype,\nyou can declare the class like this:\n\n.. code:: cpp\n\n   class MyClassThatExists;\n\n   /** ... **/\n\n   private:\n       MyClassThatExists *classInstance;\n\nAnd then include the class header inside your .cpp so you can use that class.\n\nIf you need something in the source file (.cpp) that is not a class member,\nthen add the include in the source file.\n\nThe includes must be ordered from local to global. That is, first include\nany local header file (with double quotes like `#include \"common/Helpers.h\"`.\nThen, after an empty newline, include Qt definitions like\n`#include <QShortcut>`.\nFinally, include the standard C++ headers you need.\n\nIncludes must be sorted by alphabetical order.\n\nDocstrings\n~~~~~~~~~~\n\nOur API reference is generated using Doxygen, so when it comes to\nfunction documentation, please use the following format:\n\n.. code:: cpp\n\n   /**\n    * @brief Add a new param to the accumulator\n    */\n   virtual void accumulate(RefreshDeferrerParams params) =0;\n\nLoops\n~~~~~\n\nWe use the C++11 foreach loop style, which means any “foreach” loop should\nlook like:\n\n.. code:: cpp\n\n   for (QJsonValue value : importsArray) {\n       doSomething(value);\n   }\n\nnullptr\n~~~~~~~\n\nPlease do not use ``0`` nor ``Q_NULLPTR``, only use ``nullptr``.\n\nExample:\n\n.. code:: cpp\n\n   QObject *object = nullptr;\n\nConnecting Qt Signals\n~~~~~~~~~~~~~~~~~~~~~\n\nUse one of the following methods for connecting signals to slots:\n\n.. code:: cpp\n\n   // typically you will make connection in the constructor to a member of current class\n   connect(this->ui->button1, &QPushButton::clicked,\n           this, &MyObject::buttonClicked); // Good\n\n   // you can also connect directly other object slots\n   connect(checkbox, &QCheckBox::toggled, widget, &QWidget::setEnabled); // Good\n\n   // use lambda for passing extra arguments\n   connect(button1, &QPushButton::clicked, this, [this](){ foo(getBar()); }); // Good\n\nThis syntax performs compile-time type checks and allows the use of lambda\nfunctions. Other approaches for connecting signals can silently break at runtime.\n\nDon't use the older macro based syntax or automatic name based connections.\n\n.. code:: cpp\n\n   // SIGNAL and SLOT macros\n   connect(sender, SIGNAL(clicked), this, SLOT(buttonClicked)); // BAD\n\n   // automatic name based connection\n   slot:\n      void on_actionNew_triggered(); // BAD\n\n   // 3 argument connect without receiver object\n   connect(sender, &SomeObject::signal, [this](){ this->foo(getBar()); }); // BAD\n\n\nGeneral Coding Advices\n----------------------\n\nFunctions Documentation\n~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can find the class documentation in the API Reference menu item.\n\nUpdating the Git Submodules\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nGit submodules play a major part in Cutter. This, because Cutter is powered\nby Rizin, its parent project, and it tries to stay up-to-date with its\nrecent version, which allows us to implement new features, and enjoy bug\nfixes and performance improvements on Rizin. Often, we need to update\nthe Rizin submodule or the others, to push their most recent\nversion to Cutter.\n\nYou can view the list of all the submodules from the cutter root folder with:\n\n.. code:: sh\n\n   git config --file .gitmodules --get-regexp path | awk '{ print $2 }'\n\nTo update all the submodules at once, run these commands from the\ncutter root folder:\n\n.. code:: sh\n\n   git submodule foreach git pull origin master\n   git add submodule_name_1 submodule_name_2\n   git commit -m \"Update submodules\"\n\nMore likely, you'll only need to update the *rizin* submodule.\nIn order to update one submodule individually, use the following code:\n\n.. code:: sh\n\n   cd rizin\n   git checkout dev && git pull\n   cd ..\n   git add rizin\n   git commit -m \"Update rizin submodule\"\n\n\nUseful Resources (Qt Development)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n* `Signals & Slots <https://doc.qt.io/qt-5/signalsandslots.html>`__\n* `Model/View Programming <https://doc.qt.io/qt-5/model-view-programming.html>`__ - read this if you are going to work with a list or table-like widgets\n* `QAction <https://doc.qt.io/qt-5/qaction.html#details>`__\n"
  },
  {
    "path": "docs/source/contributing/code/getting-started.rst",
    "content": "Contributing code to Cutter\n===========================\n\nThank you so much for your interest in contributing to Cutter! Contributors like you are a treasured resource for Cutter and their contributions have made the project what it is. And so, we appreciate everyone who gives the community the gift of their time <3.\n\nThe following section in our documentation is *not* intended to be comprehensive but rather a quick guide to walk you through the basic flow of contributing to Cutter. For more thorough documentation, follow the links on this page and read our Development Guidelines.\n\nClone and compile Cutter\n------------------------\nThe first step before starting to add code to Cutter is to build it on your environment. Whether it is Linux, Windows, or macOS, we have you covered and detailed the instructions to compile Cutter on our :doc:`/building`. Once you are done and Cutter is built successfully, test that it works correctly and continue to the next steps.\n\n.. tip::\n  If you are facing issues with building Cutter on your environment, make sure you didn't miss anything in the documentation. Specifically, check the :ref:`Building:Troubleshooting` section.\n\n.. tip::\n  If you need help configuring your development environment, make sure to read our :doc:`instructions, recommendations and tips <ide-setup>` for setting up a Cutter development environment for popular IDEs.\n\n\n\nFind something to work on\n-------------------------\nSome will reach this page with a clear idea of what issue they want to fix or what feature they wish to implement. But some would simply want to help Cutter getting better while doing open-source, without having a specific thing in mind. If you already have something in mind - great! Move forward to the next section. If you don't have anything specific, stick with us a little bit more.\n\nThe issues and the feature-requests of Cutter are listed and tracked on our `Github Issues <https://github.com/rizinorg/cutter/issues>`_ page. Don't get scared by the number of issues open, we will learn how to filter them to find those which fit you best.\n\n.. tip::\n  **Fix your pet peeve!** Anything specific that annoys you about Cutter? Fantastic! This can be a great place to start.\n\n\nOrganizing issues\n*****************\nIn order to organize the hundreds of Issues opened on Cutter, we use several features of Github for project management. Get yourself familiar with the following features, it will help you filter the issues.\n\nLabels\n^^^^^^\nTagging issues and pull requests with labels allows us to quickly search for them later. We use labels to describe the type of the issue, the feature it belongs to, the difficulty, and even its priority. We recommend you to start from issues labeled as `good-first-issue <https://github.com/rizinorg/cutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22>`_. These issues were tagged by us as suitable for new contributors.\n\nProjects\n^^^^^^^^\nWe use `project boards <https://github.com/rizinorg/cutter/projects>`_ to gather together tasks and issues for specific features or problems. For example, we have dedicated project boards for Debugger, Hexdump widget, Decompiler widget, and High DPI support. If you are interested to take part in developing a major feature, or you want to get a broader look at a progress of a specific part in Cutter, this can be a good place to visit.\n\nMilestones\n^^^^^^^^^^\n\nWhen starting working on a new release, we gather together the bug and feature-requests that we consider of high-priority into a `Milestone <https://github.com/rizinorg/cutter/milestones>`_. If you want to work on a feature or fix a bug that is needed and prioritized for the next release, then check out the open Milestone,.\n\n\nAssigning Issues\n^^^^^^^^^^^^^^^^\n\nBefore choosing an issue to work on, make sure it is not assigned to anyone. If it is, you can comment and ask if this person is intended to work on the issue. Some assigned issues are abandoned by their assignee and can be picked up by other contributors.\n\n\n\nGet familiar with the current situation\n---------------------------------------\n\nRun Cutter and get familiar with how the functionality you want to modify works currently. For example, if it is a dialog, make sure you understand its role and how our users are using it. If it is a bug, try to repeat it using the instructions from the bug report.\n\n.. tip::\n  If you are inexperienced with binary analysis tasks, you can always ask other developers in the team about the feature and how it should be used. The team and the community will be happy to help and instruct you with everything you need to know. \n\nFind the source files implementing the feature you want to work on. By doing this, practice searching code in your editor, this is an important skill. We suggest searching for files with the name of the feature. For example, if you are interested in improving the \"Sections\" widget, you can find the source code of this feature in `SectionsWidget.cpp` and `SectionsWidget.h`. Alternatively, you can try and look for specific unique strings that exist in the dialog, widget, or feature you want to improve. Usually, when searching the entire code base for these strings, you'll land on a file related to this feature, whether it is a ``.cpp`` file or a ``.ui`` file. From there it will be easy to navigate your way to the right place.\n\n\nWork on your feature or bug-fix \n-------------------------------\n\nIf you are experienced with such tasks, go ahead - we leave this in your hands. Otherwise, we recommend you come up with a plan for things that need to be done to solve this bug or implement this feature. Discuss your plan in the relevant issue on GitHub.\n\n.. important::\n   Before starting coding, make sure to get yourself familiar with our comprehensive documentation for our `coding style, conventions, and guidelines <development-guidelines.html>`_.\n\n\nIf you don't know how to implement something, check if any of the existing code implements similar behavior in the same widget or similar widgets. If you do copy an existing code consider why it did things the way it did, the same factors might not apply in your case or the old code was poorly written from the first place.\n\nOpen a Pull request\n-------------------\n\nWhen you are done, and the additions and modifications to the code are in place, commit your changes, and get your code reviewed by opening a new Pull Request. Please remember to follow the Pull Request template.\n\nIn the Pull Request template you will be required to add a \"Test plan\". For example, if you performed GUI changes, demonstrate it by posting a screenshot. Make a list of steps to be taken by the reviewers to verify that the changes are working as expected. This is also a good point to consider any potential edge-cases or different kinds of inputs if you didn't already do it while writing the code. Perform the steps you described when making the PR even if they seem trivial and you did them during development, it helps to catch any mistakes done during the final cleanup and making sure you didn't forget anything.\n\n\nRepeat\n------\n\n**Thank you!** You've made your very first contribution, and Cutter is better for it. But don't stop now. Go back to the first steps, as there is plenty more to do. A mentor or other developers might suggest a new issue for you to work on.\n\n\nMore Information\n----------------\n\n.. important::\n    We're always in the process of improving the information on this page for newcomers to the Cutter. Please help us by suggesting improvements and tell us about the information that this page lacks.\n\n"
  },
  {
    "path": "docs/source/contributing/code/ide-setup.rst",
    "content": "Development environment setup\n=============================\n\nThis page contains recommendations and tips on how to better setup different IDEs for Cutter development.\n\n\nGeneral advice\n--------------\nEveryone has their own preferences for their favorite IDE or code editor.\nThere are no strict requirements for using a specific one for Cutter development.\nAny IDE with good CMake integration should work well.\n\nFor most development builds, unless you are working on packaging issues, it is recommended to build Cutter with the bundled version of Rizin. It is the default configuration and the easiest way to ensure that a compatible Rizin version is used and helps you deal with different versions of Rizin when working with multiple Cutter branches. In case you want to build Cutter with a different version of Rizin installed on your system, you can set `CUTTER_USE_BUNDLED_RIZIN=OFF`. On Linux, in case you have multiple Rizin versions without ``CUTTER_USE_BUNDLED_RIZIN``, the ``PKG_CONFIG_PATH`` environment variable can be used to select the desired Rizin installation.\n\nWhile `Qt Creator`_ has a builtin visual form and widget editor, not having it in other IDEs is not a major problem. It is also available as a standalone tool called Qt Designer and you can configure the file associations so that ``.ui`` files are opened using it. Depending on the ``.ui`` file and changes you want to make, it is sometimes easier to perform them by editing the ``.ui`` file as a text file. Essentially, ``.ui`` files are XML files. Most code editors should have some support for XML highlighting and possibly block folding.\n\nThe following instructions and recommendations assume that you have already download Cutter source and obtained required dependencies as described in :doc:`/building`.\n\nLinux\n-----\n\nOn a rolling-release distribution or a somewhat recent version of traditional distributions like Ubuntu 18.04, it should be possible to get all the dependencies from the official repository. There might be some problems with PySide2 and Shiboken2 but it can be easily disabled and isn't necessary for most work on Cutter. Don't try to install PySide using pip.\n\nWindows\n-------\n\nAssuming you have a sufficiently powerful computer, a nice way of getting and configuring Qt for Cutter is to use `vcpkg <https://github.com/Microsoft/vcpkg>`_.\nFor a quick test, the exact versions of libraries used by Cutter release packages can be obtained from `cutter-deps <https://github.com/rizinorg/cutter-deps/releases>`_ but they don't contain debug\nversions of libraries so they are not suitable for more serious Cutter development on Windows.\n\nQt Creator\n----------\nQT Creator is an open-source IDE made by the same developers as Qt.\n\nPros and Cons\n~~~~~~~~~~~~~\n\n- builtin help for Qt API\n- builtin .ui file editor (Qt Designer - visual form editor)\n- builtin helper for displaying Qt types in the debugger\n- Viewing source files that are not directly part of the project (Rizin source code) is somewhat inconvenient.\n- The simplest way of installing on non-Linux operating systems require login with Qt account\n\nProject setup\n~~~~~~~~~~~~~\nThe following instructions were made based on version 4.12.4 of Qt Creator. The steps might slightly differ between the versions.\n\n- Go to :menuselection:`File --> Open File or Project..` and select :file:`cutter/CMakeList.txt`\n- Select kit and press :guilabel:`Configure Project`\n\nFormatting using clang-format\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nTo configure ``clang-format`` for formatting a file you will need to use the built-in Beautifier plugin. `Follow the instructions <https://doc.qt.io/qtcreator/creator-beautifier.html>`_ on Qt Creator's website to enable the plugin and configure it to run ``clang-format`` when saving a file. In the clang-format options page choose \"Use predefined style: File\".\n\nChanging CMake configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nClick on the \"Projects\" button on the left side of the screen and then select \"Build\". All the project CMake options are listed and can be edited there in a graphical editor.\n\nEditing Qt .ui files\n~~~~~~~~~~~~~~~~~~~~\nDouble-clicking a ``.ui`` file in a file list opens it inside a visual editor. If you want to make changes that are easier to do by editing ``.ui`` file as text - right-click the file and select :menuselection:`Open With --> Plain Text Editor`. Switching from visual form editor back to code editor mode will open the ``.ui`` file in read-only mode with the following warning \"This file can only be edited in Design mode\". To edit use the same steps as described before.\n\nVS Code\n-------\n`VS Code <https://github.com/Microsoft/vscode>`_ is an open-source code editor made by Microsoft.\n\nPros and Cons\n~~~~~~~~~~~~~\n\n- A large number of plugins\n- A good fallback mechanism for files that are not directly part of a project.\n\nRecommended plugins\n~~~~~~~~~~~~~~~~~~~\n- `C/C++ <https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools>`_ - The official C++ support plugin made by Microsoft\n- `CMake Tools <https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools>`_ - Provides CMake project integration. Originally developed by vector-of-bool and currently maintained by Microsoft.\n- `CMake <https://marketplace.visualstudio.com/items?itemName=twxs.cmake>`_ - CMake language support when editing CMake files. Does not replace the previous CMake plugin. They provide non-overlapping functionality and should be used together.\n\nProject setup\n~~~~~~~~~~~~~\n- :menuselection:`File --> Open Folder...` and select the folder in which you cloned Cutter\n- Install the recommended plugins.\n- Once the `CMake Tools` plugin is installed, in the corner you will see a popup asking you \"Would you like to configure project 'cutter'? Source: CMake Tools (Extension)\". Click Yes.\n- In the kit selection popup, choose :guilabel:`[Unspecified]` unless you have more specific needs.\n- If you initially dismissed the configuration window or didn't have the plugins installed yet - open command-palette using :kbd:`Ctrl-Shift-P` and select :guilabel:`Cmake: Configure`\n\nChanging CMake configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nAfter the first configuration :kbd:`Ctrl-Shift-P`/:guilabel:`CMake: Edit CMake Cache` opens a text editor with all CMake options. Cutter specific ones mostly start with \"CUTTER\".\n\n.. _vscode-debug-setup:\n\nFormatting using clang-format\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nThe C/C++ extension we recommended earlier supports source code formatting using clang-format which is included with the extension. Use :kbd:`Ctrl-Shift-I` to format the document or :kbd:`Ctrl-K Ctrl-F` to only format the selection. We recommend to configure auto-formatting via the settings. `Follow the instructions <https://code.visualstudio.com/docs/cpp/cpp-ide#_code-formatting>`_ on VS Code's website.\n\nBuilding, Running, Debugging\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nBuild and running commands are available in the status bar at the bottom and in the Command Palette menu (:kbd:`Ctrl-Shift-P`) named ``CMake: Build F7``, ``CMake: Run Without Debugging Shift+F5``, and ``CMake Debug Ctrl + F5``.\nShortcuts can be viewed in the :kbd:`Ctrl-Shift-P` menu. They don't match default VS Code ones since those depend on :file:`tasks.json``.\n\nRunning and debugging launches the executable without any arguments. Command-line arguments can be passed to the debug\nexecutable by creating a ``.vscode/launch.json`` configuration. Read the `documentation <https://code.visualstudio.com/docs/cpp/launch-json-reference>`_  for more information. Instead of creating :file:`launch.json` manually it can be created from template: :kbd:`Ctrl-Shift-P`/:menuselection:`Debug: Select and Start Debugging --> Add configuration.. --> C,C++: (gdb) Launch`.\n\nTo setup gdb pretty printers for Qt types on Linux, download the scripts from `Kdevelop <https://github.com/KDE/kdevelop/tree/master/plugins/gdb/printers>`_. In the :file:`~/.gdbinit` file add the following code:\n\n\n.. code-block:: python\n\n    python\n    import sys\n\n    sys.path.insert(0, '/path/to/folder/with/pretty_printer_scripts')\n    from qt import register_qt_printers\n    register_qt_printers (None)\n\n    end\n    set print pretty 1\n\n\nCLion\n-----\n`CLion <https://www.jetbrains.com/clion/>`_ is a C and C++ IDE from the popular software development tool maker - JetBrains.\n\n\nPros and Cons\n~~~~~~~~~~~~~\n\n- Medium amount of plugins, many first-party plugins made by JetBrains for their IntelliJ based IDE family\n- There is no free version\n- Takes some time to analyze the files after opening a project. Switching between .cpp and corresponding .h file may for the first time may take a few seconds.\n\nProject setup\n~~~~~~~~~~~~~\n- Go to :menuselection:`File --> Open` and select the folder in which you cloned Cutter\n- Open :file:`cutter/CMakeLists.txt` using the project file list on the left side of the screen\n- A yellow bar with a message :guilabel:`CMake project is not loaded` should appear, click :guilabel:`Load CMake project`\n\nChanging CMake configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nGo to :menuselection:`File --> Settings --> Build,Execution,Deployment --> CMake`. CMake options are specified the same way as on command-line ``-DOPTION_NAME=VALUE``.\n\nFormatting using clang-format\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nClion provides you with an easy way to format files with ``clang-format``. Follow the `documentation <https://www.jetbrains.com/help/clion/clangformat-as-alternative-formatter.html>`_ on their website to learn how to enable formatting with ``clang-format``.\n\nBuilding, Running, Debugging\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nFollow the `Clion documentation <https://www.jetbrains.com/help/clion/qt-tutorial.html#debug-renderers>`_ for how to configure Qt type debugger renderers. If you are using the MSVC toolchain\nit can use :file:`qt5.natvis`. In rest of the cases you can use ``.gdbinit`` or ``..ldbinit`` based approach similar to one described for :ref:`VSCode setup<vscode-debug-setup>`\n\nEditing Qt .ui files\n~~~~~~~~~~~~~~~~~~~~\nDefault CLion behavior for opening .ui files is `somewhat buggy <https://youtrack.jetbrains.com/issue/CPP-17197>`_. Double-clicking the file does nothing, but it can be opened by dragging it to the text editor side.\nThis can be somewhat improved by changing `file association <https://www.jetbrains.com/help/clion/creating-and-registering-file-types.html>`_. Open :menuselection:`File --> Settings --> Editor --> File Types` and change type association of \\*.ui files from :guilabel:`Qt UI Designer Form` to either \"XML\" or :guilabel:`Files Opened in Associated Applications`.\nThe first one will open it within CLion as an XML file and the second will use the operating system configuration.\n\nVisual Studio\n-------------\nVisual Studio Community edition is available for free and can be used for contributing to open source projects.\n\nIt is recommended to use the latest Visual Studio version 2019 because it has the best CMake integration.\nOlder VS versions can be used but CMake integration isn't as good. With those, it might be better to generate Visual Studio\nproject from CMake project using the command-line or :command:`cmake-gui` and opening the generated Visual Studio project instead of opening the\nCMake project directly.\n\nVisual Studio supports many different languages and use-cases. Full installation takes a lot of space. To keep the size minimal during installation\nselect only component called \"Desktop development with C++\". Don't worry too much about missing something.\nAdditional components can be later added or removed through the VS installer which also serves as an updater and package manager for Visual Studio components.\n\nPros and Cons\n~~~~~~~~~~~~~\n- good debugger\n- medium amount of plugins\n- completely closed source\n- Windows only\n\nProject setup\n~~~~~~~~~~~~~\n- Open folder in which you cloned Cutter source using Visual Studio\n- Open CMake settings configurator using either :menuselection:`Project --> CMake Settings` or by clicking :guilabel:`Open the CMake Settings Editor` in the overview page.\n- Check options that you want Cutter to be built with, or leave it as-is for the default options.\n- If you are using vcpkg, Visual Studio should detect it automatically. The list of CMake options in the configurator should have some referring to vcpkg. If they are not there, specify the path to vcpkg toolchain file in the :guilabel:`CMake toolchain file` field.\n- If you are not using vcpkg, configure the path to Qt as mentioned in :ref:`windows CMake instructions<building:Building on Windows>`. You can specify the CMake flag in :guilabel:`CMake command arguments:` field.\n- To Ensure that VS debugger can display Qt types in a readable way, it is recommended to install `Qt Visual Studio Tools <https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019>`_ plugin. It will create a :file:`Documents/Visual Studio 2019/Visualizers/qt6.natvis` file. Once :file:`qt6.natvis` has been created you can uninstall the plugin.\n\nChanging CMake configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOpen :menuselection:`Project --> CMake Settings`. CMake options can be modified either in graphical table editor, as a command-line flag or by switching to the JSON view.\n\nFormatting using clang-format\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nVisual Studio supports ``clang-format`` by default so you should not do anything special. It will simple use the existing ``_clang-format`` file from Cutter's root directory. If you wish to configure how and when Visual Studio will use ``clang-format``, you can do this from :menuselection:`Tools --> Options --> Text Editor --> C/C++ --> Formatting`.\n\nEditing Qt .ui files and Qt integration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nBy default Visual Studio will open ``.ui`` files as XML text documents. You can configure to open it using Qt Designer by right-clicking and selecting :guilabel:`Open With...`.\n\nThere is a  Qt plugin for Visual Studio from Qt. It isn't very useful for Cutter development since it is aimed more at helping with Qt integration into Visual Studio projects.\nIt doesn't do much for CMake based projects. The biggest benefit is that it automatically installs :file:`qt6.natvis` file for more readable displaying of Qt types in the debugger.\n"
  },
  {
    "path": "docs/source/contributing/code/release-procedure.rst",
    "content": "Release Procedure\n=================\n\n1. Update translations submodule `<https://github.com/rizinorg/cutter-translations>`_\n    1. The latest archive from Crowdin should already be in the repository, if not make sure to merge any automated Pull Request from Crowdin (e.g. https://github.com/rizinorg/cutter-translations/pull/9)\n    2. Update submodule in cutter\n2. Merge the current state of dev into stable. This can happen even earlier in order to feature-freeze the release while keeping development on dev alive. The rizin submodule on stable should point to a commit of stable in rizin and dev to a commit in dev.\n3. Lock rzghidra and rzdec versions downloaded by packaging scripts. Specify a tag or commit hash.\n4. Update version\n    #. appveyor.yml\n    #. docs/sourc/conf.py\n    #. docs/source/index.rst\n    #. CMakeLists.txt\n    #. Cutter.appdata.xml\n    #. To be safe, search the code base for the previous version number.\n5. Create a tag for the release candidate. For example, for the `v1.11.0` release you'd do something like this:\n    #. ``git tag v1.11.0-rc1``\n    #. ``git tag push origin v1.11.0-rc1``\n6. Create a GitHub release, mark it as pre-release save draft, set the tag to v1.11.0-rc1\n7. Wait for packages to build\n8. On all operating systems do the `Basic testing procedure`_ to ensure nothing badly broken.\n9. If any major problems are found, open an issue and fix them in dev and cherry pick into release branch. If the amount of changes is sufficiently large repeat from step 3. increasing rc number by one.\n10. Update version to 1.11.0\n11. Create tag\n12. Create release\n    * Fill the release notes in the Release description. Preparing release notes can begin earlier. Compare current dev branch against previous release to find all the changes. Choose the most important ones. Don't duplicate the commit log. Release notes should be a summary for people who don't want to read whole commit log. Group related changes together under titles such as \"New features\", \"Bug Fixes\", \"Decompiler\", \"Rizin\" and similar.\n13. Prepare announcement tweets and messages to send in the Telegram group, reddit, and others.\n14. Close milestone if there was one\n\n\n\nBugfix Release\n--------------\nThe process for bugfix release is similar no normal release procedure described above with some differences.\n\n* Cherry pick required bugfixes from dev into the stable.\n* Increase the third version number x.y.n into x.y.(n+1) .\n\n\nBasic testing procedure\n-----------------------\n\nThis isn't intended as exhaustive testing process, just some simple steps to make sure nothing is badly broken.\nIf it makes sense repeat the step multiple times at different offsets and click around increase the chance of noticing common problems that doesn't happen 100% of time.\n\n* Open a simple executable like ``/bin/ls`` or ``calc.exe``\n* Make sure that the upgraded layout isn't completely broken\n* The Disassembly widget shows proper disassembly.\n* Bundled plugins work\n   * Open decompiler and select ghidra, it shows some C code at least for some functions\n   * Open rzdec in decompiler widget, make sure it shows code\n* Test that sample python plugin works\n* Try debugger\n   * Insert breakpoint in main\n   * Start debugging\n   * Go to main using function widget, make sure relocation was done correctly and you see code instead of unmapped memory and breakpoint is where you placed\n   * Click continue until you hit breakpoint in main\n* Delete cutter settings file, and test that clean start works and layout isn't broken\n"
  },
  {
    "path": "docs/source/contributing/code.rst",
    "content": "Developer Documentation\n=======================\n\n.. note::\n   New to Cutter development? Check out our :doc:`tutorial for new developers <code/getting-started>`.\n\n.. toctree::\n   :maxdepth: 2\n   :glob:\n\n   Getting Started <code/getting-started>\n   code/development-guidelines\n   code/ide-setup\n   code/*\n"
  },
  {
    "path": "docs/source/contributing/docs/building-docs.rst",
    "content": "Building docs\n=======================\n\nThis page explains the steps that are needed to build Cutter's documentation.\n\nRequirements\n------------\n\nYou will need:\n\n* doxygen\n* python3\n* pip3\n\n  * sphinx\n  * breathe\n  * recommonmark\n\nOn Debian-based Linux distributions, the packages above can be installed with the following command.\n\n.. code:: sh\n\n   sudo apt install make doxygen python3-pip doxygen\n   sudo pip3 install sphinx breathe recommonmark\n\nThen, you can build the documentation with the following commands:\n\n.. code:: sh\n\n   cd cutter/docs/\n   make html\n\n.. tip::\n\n   If you do not need API documentation, type ``make quick`` instead of ``make html``.\n   \nYou can find the generated html files at ``cutter/docs/build``. Open ``cutter/docs/build/html/index.html`` with your browser to visit the index file of your local copy of the documentation.\n"
  },
  {
    "path": "docs/source/contributing/docs/getting-started.rst",
    "content": "Contributing documentation to Cutter\n====================================\n\nIf you reached this place you were probably looking to help and improve Cutter's documentation - Fantastic, welcome aboard!\n\nAs many other young projects, Cutter has a problem - it lacks comprehensive documentation and written content. Thankfully, contributing to Cutter's documentation doesn’t require you to have a deep knowledge about the internals of the project. Writing, reviewing, and editing the documentation are great ways to learn about Cutter.\n\nDon’t like the way something reads? Discover some information that wasn’t documented? Your pull request will be gleefully embraced.\n\n.. note::\n   The documentation of Cutter is written using `Sphinx and reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`_. The syntax is quite straight forward and very similar to Markdown. In the future, we hope to move completely to Markdown as it is more popular.\n\n\nHow can you help?\n-----------------\n\nThe following sections suggest ways you can contribute to Cutter's documentation. The list isn't complete as the possibilities are limitless.\n\nThe source for this documentation is available in the `docs directory <https://github.com/rizinorg/cutter/tree/dev/docs>`_ on Cutter's repository. This source can be generated according to the steps described in the :ref:`building docs page<contributing/docs/building-docs:Building docs>`. When the docs are updated, they are generated and pushed directly to the website at <https://cutter.re/docs>. The source for the website and blog are available on the `cutter.re's repository <https://github.com/rizinorg/cutter.re>`_ and are served from the ``gh-pages`` branch.\n\n.. tip::\n  Document what you wished to see. If you are a user of Cutter, try to think what things you would want to see documented when you started using the project. Sometimes, the best contributions are coming from your own needs.\n\nUser documentation\n^^^^^^^^^^^^^^^^^^\n\nThe documentation for users describes how to use Cutter and what the different features in Cutter do. The possibilities for contributing are endless since there are so many features that weren't even described on our docs yet. To contribute to our user documentation, all you need to do is pick a subject, widget, or functionality and document it. Describe the different visual components, how to use them, and what this feature even does.\n\nCommon Errors and Troubleshooting\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAs our community gets bigger, more and more people are using, installing or building Cutter on different setups. Naturally, different environments might cause different issues. The :ref:`common issues<user-docs/common-issues:Common Issues>` section in our docs aims to go over the most popular issues that you might face while using Cutter and the :ref:`building errors troubleshooting<building:Troubleshooting>` section goes over common errors that you might face while building Cutter from sources. These docs sections also explain what causes these issues and how to solve them.\n\nIf you know of such a common issue, whether you faced it yourself or noticed it on our community chats, it will be very helpful if you could document it and possible solutions for it.\n\n\nDevelopers documentation\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe documentation for Cutter developers aims to instruct new contributors on how to get started with developing to Cutter's codebase. It also describes best practices and development guidelines for all developers in the project. Think you have something to add to the developers' docs? We would love you to contribute.\n\nAPI documentation\n^^^^^^^^^^^^^^^^^\n\nThe documentation for Cutter's API is an important reference for Cutter developers as well as Plugin developers. It thoroughly describes for developers how some functions and features work. While some of the functions are thoroughly documented, most of them aren't. Any help with improving these documentations will be blessed.\n\n\nBlog posts\n^^^^^^^^^^\n\nWe would love to see people writing and sharing their experiences with Cutter. Whether you are solving CTF challenges with Cutter, analyzing malware, finding vulnerabilities, or working on personal projects - we would love to see these published. If you have a personal blog, we would be proud to share and retweet your Cutter-related publications on our profiles, bringing you more engagement and followers. If you don't have a personal blog, we would love to host your publication on our community blog over at https://cutter.re/blog.\n"
  },
  {
    "path": "docs/source/contributing/docs.rst",
    "content": "Contributing Documentation\n==========================\n\n.. toctree::\n   :maxdepth: 2\n   \n   Getting Started <docs/getting-started>\n   docs/building-docs\n"
  },
  {
    "path": "docs/source/contributing/plugins/getting-started.rst",
    "content": "Getting Started with Python Plugins\n===================================\n\nThis article provides a step-by-step guide on how to write a simple Python plugin for Cutter.\n\nCreate a python file, called ``myplugin.py`` for example, and add the following contents:\n\n.. code-block:: python\n\n   import cutter\n\n   class MyCutterPlugin(cutter.CutterPlugin):\n       name = \"My Plugin\"\n       description = \"This plugin does awesome things!\"\n       version = \"1.0\"\n       author = \"1337 h4x0r\"\n\n       def setupPlugin(self):\n           pass\n\n       def setupInterface(self, main):\n           pass\n\n       def terminate(self):\n           pass\n\n   def create_cutter_plugin():\n       return MyCutterPlugin()\n\nThis is the most basic code that makes up a plugin.\nPython plugins in Cutter are regular Python modules that are imported automatically on startup.\nIn order to load the plugin, Cutter will call the function ``create_cutter_plugin()`` located\nin the root of the module and expects it to return an instance of ``cutter.CutterPlugin``.\nNormally, you shouldn't have to do anything else in this function.\n\n.. note::\n   The Cutter API is exposed through the ``cutter`` module.\n   This consists mostly of direct bindings of the original C++ classes, generated with Shiboken6.\n   For more detail about this API, see the Cutter C++ code or :ref:`api`.\n\nThe ``CutterPlugin`` subclass contains some meta-info and two callback methods:\n\n* ``setupPlugin()`` is called right after the plugin is loaded and can be used to initialize the plugin itself.\n* ``setupInterface()`` is called with the instance of MainWindow as an argument and should create and register any UI components.\n* ``terminate()`` is called on shutdown and should clean up any resources used by the plugin.\n\nCopy this file into the ``python`` subdirectory located under the plugins directory of Cutter and start the application.\nYou should see an entry for your plugin in the list under Edit -> Preferences -> Plugins.\nHere, the absolute path to the plugins directory is shown too if you are unsure where to put your plugin:\n\n.. image:: preferences-plugins.png\n\n.. note::\n   As mentioned, plugins are Python modules. This means, instead of only a single .py file, you can also\n   use a directory containing multiple python files and an ``__init__.py`` file that defines or imports the\n   ``create_cutter_plugin()`` function.\n\n.. note::\n   If you are working on a Unix-like system, instead of copying, you can also symlink your plugin into the plugins\n   directory, which lets you store the plugin somewhere else without having to copy the files over and over again.\n\n\nCreating a Widget\n-----------------\n\nNext, we are going to add a simple dock widget. Extend the code as follows:\n\n.. code-block:: python\n\n   import cutter\n\n   from PySide6.QtWidgets import QAction, QLabel\n\n   class MyDockWidget(cutter.CutterDockWidget):\n       def __init__(self, parent, action):\n           super(MyDockWidget, self).__init__(parent, action)\n           self.setObjectName(\"MyDockWidget\")\n           self.setWindowTitle(\"My cool DockWidget\")\n\n           label = QLabel(self)\n           self.setWidget(label)\n           label.setText(\"Hello World\")\n\n   class MyCutterPlugin(cutter.CutterPlugin):\n       # ...\n\n       def setupInterface(self, main):\n           action = QAction(\"My Plugin\", main)\n           action.setCheckable(True)\n           widget = MyDockWidget(main, action)\n           main.addPluginDockWidget(widget, action)\n\n   # ...\n\nWe are subclassing ``cutter.CutterDockWidget``, which is the base class for all dock widgets in Cutter,\nand adding a label to it.\n\n.. note::\n   You can access the whole Qt6 API from Python, which is exposed by PySide6. For more information about this, refer to the\n   Documentation of `Qt <https://doc.qt.io/qt-6/reference-overview.html>`_ and `PySide6 <https://wiki.qt.io/Qt_for_Python>`_.\n\n.. note::\n   Main cutter packages are now using QT6, but Qt5 builds are still provided for compatibilty with older OS versions. If you\n   want your plugin to support both, Qt usage process is a bit more complicated.\n\nIn our ``setupInterface()`` method, we create an instance of our dock widget and an action to be\nadded to the menu for showing and hiding the widget.\nMainWindow provides a helper method called ``addPluginDockWidget()`` to easily register these.\n\nWhen running Cutter now, you should see the widget:\n\n.. image:: mydockwidget.png\n\n... as well as the action:\n\n.. image:: mydockwidget-action.png\n\n\nFetching Data\n-------------\n\nNext, we want to show some actual data from the binary in our widget.\nAs an example, we will display the instruction and instruction size at the current position.\nExtend the code as follows:\n\n.. code-block:: python\n\n   # ...\n\n   class MyDockWidget(cutter.CutterDockWidget):\n       def __init__(self, parent, action):\n           # ...\n\n           label = QLabel(self)\n           self.setWidget(label)\n\n           disasm = cutter.cmd(\"pd 1\").strip()\n\n           instruction = cutter.cmdj(\"pdj 1\")\n           size = instruction[0][\"size\"]\n\n           label.setText(\"Current disassembly:\\n{}\\nwith size {}\".format(disasm, size))\n\n   # ...\n\nWe can access the data by calling Rizin commands and utilizing their output.\nThis is done by using the two functions ``cmd()`` and ``cmdj()``, which behave just as they\ndo in `rz-pipe <https://book.rizin.re/scripting/rz-pipe.html>`_.\n\nMany commands in Rizin can be suffixed with a ``j`` to return JSON output.\n``cmdj()`` will automatically deserialize the JSON into python dicts and lists, so the\ninformation can be easily accessed.\n\n.. warning::\n   When fetching data that is not meant to be used only as readable text, **always** use the JSON variant of a command!\n   Regular command output is not meant to be parsed and is subject to change at any time, which will break your code.\n\nIn our case, we use the two commands ``pd`` (Print Disassembly) and ``pdj`` (Print Disassembly as JSON)\nwith a parameter of 1 to fetch a single line of disassembly.\n\n.. note::\n   To try out commands, you can use the Console widget in Cutter. Almost all commands support a ``?`` suffix, like in\n   ``pd?``, to show help and available sub-commands.\n   To get a general overview, enter a single ``?``.\n\nThe result will look like the following:\n\n.. image:: disasm-static.png\n\nOf course, since we only fetch the info once during the creation of the widget, the content never updates.\nWe are going to change that in the next section.\n\n\nReacting to Events\n------------------\n\nWe want to update the content of our widget on every seek.\nThis can be done like the following:\n\n.. code-block:: python\n\n   # ...\n\n   from PySide6.QtCore import QObject, SIGNAL\n\n   # ...\n\n   class MyDockWidget(cutter.CutterDockWidget):\n       def __init__(self, parent, action):\n           # ...\n\n           self._label = QLabel(self)\n           self.setWidget(self._label)\n\n           QObject.connect(cutter.core(), SIGNAL(\"seekChanged(RVA)\"), self.update_contents)\n\n       def update_contents(self):\n           disasm = cutter.cmd(\"pd 1\").strip()\n\n           instruction = cutter.cmdj(\"pdj 1\")\n           size = instruction[0][\"size\"]\n\n           self._label.setText(\"Current disassembly:\\n{}\\nwith size {}\".format(disasm, size))\n\n\nFirst, we move the update code to a separate method.\nThen we call ``cutter.core()``, which returns the global instance of ``CutterCore``.\nThis class provides the Qt signal ``seekChanged(RVA)``, which is emitted every time the current seek changes.\nWe can simply connect this signal to our method and our widget will update as we expect it to:\n\n.. image:: disasm-dynamic.png\n\nFor more information about Qt signals and slots, refer to `<https://doc.qt.io/qt-6/signalsandslots.html>`_.\n\nFull Code\n---------\n\n.. code-block:: python\n\n   import cutter\n\n   from PySide6.QtCore import QObject, SIGNAL\n   from PySide6.QtWidgets import QLabel\n   from PySide6.QtGui import QAction\n\n   class MyDockWidget(cutter.CutterDockWidget):\n       def __init__(self, parent, action):\n           super(MyDockWidget, self).__init__(parent, action)\n           self.setObjectName(\"MyDockWidget\")\n           self.setWindowTitle(\"My cool DockWidget\")\n\n           self._label = QLabel(self)\n           self.setWidget(self._label)\n\n           QObject.connect(cutter.core(), SIGNAL(\"seekChanged(RVA)\"), self.update_contents)\n\n       def update_contents(self):\n           disasm = cutter.cmd(\"pd 1\").strip()\n\n           instruction = cutter.cmdj(\"pdj 1\")\n           size = instruction[0][\"size\"]\n\n           self._label.setText(\"Current disassembly:\\n{}\\nwith size {}\".format(disasm, size))\n\n\n   class MyCutterPlugin(cutter.CutterPlugin):\n       name = \"My Plugin\"\n       description = \"This plugin does awesome things!\"\n       version = \"1.0\"\n       author = \"1337 h4x0r\"\n\n       def setupPlugin(self):\n           pass\n\n       def setupInterface(self, main):\n           action = QAction(\"My Plugin\", main)\n           action.setCheckable(True)\n           widget = MyDockWidget(main, action)\n           main.addPluginDockWidget(widget, action)\n\n       def terminate(self):\n           pass\n\n   def create_cutter_plugin():\n       return MyCutterPlugin()\n"
  },
  {
    "path": "docs/source/contributing/translations/getting-started.rst",
    "content": "Translate Cutter\n==================\n\nHelp Cutter by adding translations to the project!\n\nCutter is a global project with users from all around the world. We believe that Cutter should be as accessible as possible, and want our users to feel comfortable while using its interface. Providing our community an interface with their own language makes the experience of using Cutter better. Thus, Cutter supports a translation and localization mechanism powered by the `Crowdin <https://crowdin.com/project/cutter>`_ platform. We invite you to contribute and add translations to the project.\n\n\n.. important::\n  We currently support more than 15 languages. If you need to add a language that isn't available yet, ask any developer from the team and they will happily assist you.\n"
  },
  {
    "path": "docs/source/contributing.rst",
    "content": "Contributing to Cutter\n=======================\n\nSo you like Cutter and want to get involved? Great! This part of the documentation will help and guide you through everything you need to know when contributing to Cutter. Welcome, we're delighted to see you!\n\n.. tip::\n   **Need help?** Our community strives to be friendly, open, and accessible for new contributors. If you have any difficulties getting involved or finding answers to you questions, please `come and ask your questions on our Telegram or IRC groups <https://cutter.re/#community>`_.\n\n   We know that set up to work on Cutter and finding issues that are a good fit for your skills can be a challenge. We're always looking for ways to improve this process: making Cutter more open, accessible, and easier to participate with. If you're having any trouble following this documentation or hit a barrier you can't get around, please contact us.\n\n.. rubric:: How do you want to help?\n\n.. raw:: html\n\n   <a class=\"btn btn-outline-primary btn-lg m-2\" role=\"button\" href=\"contributing/code/getting-started.html\">Code</a>\n   <a class=\"btn btn-outline-warning btn-lg m-2\" role=\"button\" href=\"contributing/docs/getting-started.html\">Documentation</a>\n   <a class=\"btn btn-outline-success btn-lg m-2\" role=\"button\" href=\"contributing/plugins/getting-started.html\">Plugins</a>\n   <a class=\"btn btn-outline-info btn-lg m-2\" role=\"button\" href=\"contributing/translations/getting-started.html\">Translations</a>\n\n---------\n\n.. toctree::\n   :maxdepth: 2\n   :titlesonly:\n\n   contributing/code\n   contributing/docs\n   contributing/translations/getting-started\n   contributing/plugins/getting-started\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "Cutter\n======\n\nCutter is a Qt and C++ GUI for Rizin. Its goal is to provide an advanced,\ncustomizable and FOSS reverse-engineering platform while keeping the\nuser experience in mind. Cutter was created by reverse engineers for\nreverse engineers.\n\n.. image:: images/screenshot.png\n\n\nGetting Cutter\n--------------\n\nCutter is available for all major platforms. You can\ndownload the latest release from\n`here <https://github.com/rizinorg/cutter/releases>`__.\n\n- **macOS**: Download the ``.dmg`` file and install it.\n- **Windows**: Download the ``.zip`` archive and extract it.\n- **Linux**: Download the ``.AppImage`` file and make it executable by doing:\n\n::\n\n    chmod +x Cutter*.AppImage\n\n\nBuilding from Sources\n---------------------\n\nTo build Cutter on your machine, please follow this guide: :doc:`Building from Source </building>`.\n\nNeed help?\n----------\n\nYou can contact the *Cutter* developers and community on:\n\n-  Mattermost: https://im.rizin.re\n-  Telegram: https://t.me/cutter_re\n-  #cutter on https://web.libera.chat\n-  Twitter: https://twitter.com/cutter_re\n\nWant to help the project?\n-------------------------\n\nIf you want to contribute to Cutter, take a look at our :doc:`Contribution Guidelines </contributing>` to learn how you can help improve the project!\n\n.. toctree::\n   :caption: Contents:\n   :maxdepth: 3\n\n   user-docs\n   contributing\n   Developer documentation <contributing/code>\n   building\n   plugins\n   api\n\n"
  },
  {
    "path": "docs/source/plugins.rst",
    "content": "Plugins\n=======\n\nCutter supports writing plugins in both C++ and Python.\nIf you are unsure of which language to choose, starting with Python is strongly suggested as\nit provides a faster and simpler workflow. You can find example plugins `here <https://github.com/rizinorg/cutter/tree/dev/src/plugins>`__.\n\nIf you plan to implement support for a new file format or architecture, Cutter plugins are not the correct approach.\nInstead, you will want to implement a Rizin plugin, which is documented `here <https://book.rizin.re/src/plugins/intro.html>`__.\n\n\nLoading and Overview\n----------------------\n\nPlugins are loaded from an OS-dependent user-level directory.\nTo get the location of this directory and a list of currently loaded plugins, navigate to Edit -> Preferences -> Plugins.\n\n.. image:: contributing/plugins/preferences-plugins.png\n\nThe plugins directory contains two subdirectories, ``native`` and ``python`` for C++ and Python plugins respectively,\nwhich will be created automatically by Cutter.\n\n.. note::\n   The support for Python plugins is only available if Cutter was built with the options ``CUTTER_ENABLE_PYTHON``\n   and ``CUTTER_ENABLE_PYTHON_BINDINGS`` enabled.\n   This is the case for all official builds from GitHub Releases starting with version 1.8.0.\n\n\nCreating Plugins\n------------------\n\n.. toctree::\n   :glob:\n\n   contributing/plugins/*\n"
  },
  {
    "path": "docs/source/user-docs/command-line.rst",
    "content": "Command-line Options\n====================\n\nSynopsis\n--------\n\n**Cutter** [*options*] [<*filename*> | --project <*project*>]\n\n\nOptions\n-------\n\n.. option:: <filename>\n\n   Filename to open. If not specified file selection dialog will be shown.\n\n.. option:: -h, --help\n\n   Displays help on command-line options.\n\n.. option:: --help-all\n\n   Displays help including Qt specific options.\n\n.. option:: -v, --version\n\n   Displays version information.\n\n.. option:: -A, --analysis <level>\n\n   When opening a file automatically perform analysis at a given level. Requires\n   :option:`<filename>` to be specified. Following levels are available:\n\n   **0**\n     No analysis.\n\n   **1**\n     aaa\n\n   **2**\n     aaaa (experimental)\n\n.. option:: -a, --arch <arch>\n\n   Sets a specific architecture name.\n\n.. option:: -b, --bits <bits>\n\n   Sets a specific architecture bits.\n\n.. option:: -c, --cpu <cpu>\n\n   Sets a specific CPU.\n\n.. option:: -o, --os <os>\n\n   Sets a specific operating system.\n\n.. option:: -e, --endian <big|little>\n\n   Sets the endianness (big or little).\n\n.. option:: -F, --format <name>\n\n   Force using a specific file format (bin plugin).\n\n.. option:: -B, --base <base address>\n\n   Load binary at a specific base address.\n\n.. option:: -m, --map <map address>\n\n   Map the binary at a specific address.\n\n.. option:: -i <file>\n\n   Run script file.\n\n.. option:: -p, --project <file>\n  \n   Load project file.\n\n.. option:: -w, --writemode\n\n   Open a file in write mode, instead of the default read-only mode.\n   When used together with -A/--analysis <level>, it will open a file directly\n   in write mode without any further dialog or confirmation.\n\n.. option:: -P, --phymode\n\n   Disables virtual addressing.\n\n.. option:: --pythonhome <PYTHONHOME>\n\n   PYTHONHOME to use for the embedded python interpreter.\n\n.. option:: --no-output-redirect\n\n   Disable output redirection. Some of the output in the console widget will not\n   be visible. Use this option when debugging a crash or freeze and output\n   redirection is causing some messages to be lost.\n\n.. option:: --no-plugins\n\n   Start cutter with all plugins disabled. Implies :option:`--no-cutter-plugins` and :option:`--no-rizin-plugins`.\n\n.. option:: --no-cutter-plugins\n\n   Start cutter with cutter plugins disabled.\n\n.. option:: --no-rizin-plugins\n\n   Start cutter with rizin plugins disabled.\n"
  },
  {
    "path": "docs/source/user-docs/common-issues.rst",
    "content": "Common Issues\n=============\n\nThis page lists common issues encountered by users.\n\nAppImage Crashes\n----------------\n\nIf the Linux AppImage binary crashes upon startup, make sure your\n``LD_LIBRARY_PATH`` environment variable is empty.\nFor a detailed explanation, see the issue `#579 <https://github.com/rizinorg/cutter/issues/579>`__\n\nKeyboard Layout Issue\n---------------------\n\nSome people report that they have keyboard issues. Usually it is because\nthe Xorg layout is wrong. You can check it with: ``setxkbmap -query``\nMost of the time using ``setxkbmap us`` solves the issue, but it might\nnot be enough and require a more advanced Xorg configuration.\n\nInitial Analysis takes a long time or Cutter UI freezes\n-------------------------------------------------------\n\nCutter and Rizin currently don't work very well with large and very large binaries.\nThe exact limits depend on the content of the binary, but roughly a few MB can be considered large\nand may take a few minutes to analyze. More than 100MB is very large,\nanalysis with default settings will likely take a very long time and it might occasionally\nfreeze the UI during usage.\n\nIf the analysis takes longer than 5-15 minutes it is recommended to retry it with different\nanalysis options. In the \"Load Options\" dialog, move the analysis slider to the right in order to reach\nthe \"Advanced Analysis\" view. This view will help you learn more about the options that can\nbe used to more selectively analyze only the relevant parts of code.\n"
  },
  {
    "path": "docs/source/user-docs/features.rst",
    "content": "Features\n========\nThis section in the user docs gives a more detailed information about different features in Cutter. These are the different\nutilities and views that can be used and controlled by you during your session. As more familiar and comfort you feel with the\ndifferent features in Cutter, the more efficient and effective you are during your reverse-engineering experience.\n\nDecompiler\n----------\nA decompiler is a program that will analyze binaries and attempt to create a high-level representation of the machine code in it. In other words, it tries to reconstruct the source code from which the binary was compiled in the first place.\n\nHere's an image that compares one of the decompiler Cutter supports with the Cutter's disassembler.\n\n.. image:: ../images/decompiler_vs_disassembly.png\n\n\nCutter provides an interface that supports plugins of multiple decompilers including Ghidra, RetDec and JSDec. The interface receives data from the decompiler and presenting the decompiler code in a context-sensitive decompiler widget. Check the `Decompilers <https://github.com/rizinorg/cutter-plugins#decompilers>`_ section on our Plugins repository to know more about the decompilers we support.\n\n\nOut of the decompiler plugins that we support, the one that's officially maintained by the developers of Cutter is RzGhidra. :doc:`Click here <menus/decompiler-context-menu>` to learn more about the functionalities we provide in the decompiler.\n"
  },
  {
    "path": "docs/source/user-docs/menus/breakpoints-widget-context-menu.rst",
    "content": "Breakpoints Widget Context Menu \n================================\n\n\nEdit a Breakpoint\n----------------------------------------\n**Description:** Open the Advanced Breakpoint Edit dialog to edit the selected breakpoint. In this dialog, you can define a software or hardware breakpoint, use conditionals, and more.    \n\n**Steps:** Right-click on an item in the Breakpoints widget and choose ``Edit``.  \n\nToggle Breakpoint State\n----------------------------------------\n**Description:** Enable or disable a breakpoint, depending on its current state. An active breakpoint will be disabled, and an inactive breakpoint will be enabled.  \n\n**Steps:** Right-click on an item in the Breakpoints widget and choose ``Toggle breakpoint``.   \n\n**Shortcut:** :kbd:`Space`  \n\nDelete Breakpoint\n----------------------------------------\n**Description:** These options will disable and remove the selected breakpoint from the program.  \n\n**Steps:** Right-click on an item in the Breakpoints widget and choose ``Delete breakpoint``  \n\n**Shortcut:** :kbd:`Del`"
  },
  {
    "path": "docs/source/user-docs/menus/debug-buttons-toolbar.rst",
    "content": "Debug Buttons Toolbar\n==============================\n\nContinue until Main \n----------------------------------------\n**Description:** Continue the execution of the program until the Main function is reached.  \n\n**Steps:**  Continue until main  \n\nContinue until Call\n----------------------------------------\n**Description:** Continue the execution of the program until a function call is reached.  \n\n**Steps:**  Continue until call  \n\nContinue until Syscall\n----------------------------------------\n**Description:** Continue the execution of the program until a Syscall is reached.  \n\n**Steps:**  Continue until syscall"
  },
  {
    "path": "docs/source/user-docs/menus/decompiler-context-menu.rst",
    "content": "Decompiler Context Menu \n==============================\nThe decompiler context menu is a context-sensitive menu that contains actions that are available for the position under the cursor.\n\nCopy\n----------------------------------------\n**Description:** If text is selected, copy the selected text to the clipboard. If a word is highlighted, copy that word. In all other cases, copy the line under the cursor.\n\n**Steps:**  Right-click on a selected text and choose ``Copy``\n\n**Shortcut:** :kbd:`Ctrl-C`  \n\nCopy Instruction Address\n----------------------------------------\n**Description:** Copy the address of the instruction mapped to the part of the code under the cursor.\n\n**Steps:**  Right-click on the portion of code for which you want the instruction's address and choose ``Copy instruction address (<address>)``\n\nCopy Address of Reference\n----------------------------------------\n**Description:** Copy the address of the reference under the cursor. References include functions, global variables, and constant variables with an address.\n\n**Steps:**  Right-click on a reference and choose ``Copy  address [of <name>] (<address>)``  \n\n**Shortcut:** :kbd:`Ctrl-Shift-C`\n\nShow the code in another widget\n----------------------------------------\n**Description:** Show the code under the cursor in another opened widget, or open a new one. If a non-decompiler widget is chosen, the address mapped to the portion of code under the cursor will be opened in that widget.\n\n**Steps:**  Right-click on an item and go to the :menuselection:`Show in` submenu. You can choose a widget or open a new widget from here.\n\nShow the selected item in another widget\n----------------------------------------\n**Description:** Show the selected item in another opened widget, or open a new one. Items include functions, global variables, and constant variables under the cursor.\n\n**Steps:**  Right-click on an item and go to the submenu :menuselection:`Show <item> in` or :menuselection:`<function name> (<address>)`. You can choose a widget or open a new widget from here.\n\nAdd and Edit Comment\n----------------------------------------\n**Description:** Add a comment for the line of code under the cursor or edit the comment under the cursor. The ``Edit comment`` option is only available for user-defined comments.\n\n**Steps:** Right-click and choose ``Add Comment`` or ``Edit Comment``.\n\n**Shortcut:** :kbd:`;`\n\nDelete a Comment\n----------------------------------------\n**Description:** Delete the comment under the cursor. If a comment doesn't exist under the cursor, delete the comment at the offset mapped to the portion of code under the cursor.\n\n**Steps:** Right-click on an instruction with a user-defined comment and choose ``Delete comment``  \n\nRename function\n----------------------------------------\n**Description:** Rename a function under the cursor. \n\n**Steps:** Right-click on a function name and choose ``Rename function <name>``  \n\n**Shortcut:** :kbd:`N`\n\nGive a name or rename global variables\n----------------------------------------\n**Description:** Give a name or rename the global variable under the cursor.\n\n**Steps:** Right-click the global variable and choose ``Add name to <address of global variable>`` or ``Rename <name>``.\n\n**Shortcut:** :kbd:`N`\n\nDelete the name of a global variable\n----------------------------------------\n**Description:** Delete the name of the global variable under the cursor.\n\n**Steps:** Right-click on a global variable and choose ``Remove <name>``.\n\nRename Function Variable\n----------------------------------------\n**Description:** Rename local variables and arguments in the decompiled function. Note that this option is available only for function variables defined in the disassembly.\n\n**Steps:** Right-click on a variable and choose ``Rename variable <name>``. \n\n**Shortcut:** :kbd:`N` \n\nEdit Local Variables and Arguments\n----------------------------------------\n**Description:** Rename or set the types of the function's variables and arguments. Note that this option is available only for function variables and arguments defined in the disassembly.\n\n**Steps:** Right-click on a variable and choose ``Edit variable <name>``.\n\n**Shortcut:** :kbd:`Y`\n\nShow Cross References\n----------------------------------------\n**Description:** Show X-Refs to the reference under the cursor. This option will open Cutter's X-Refs dialog in which you will be able to see a list of X-Refs from and to the address of the reference. You can also see a preview of each cross-reference to quickly inspect the different usages. Note that references refer to function names, global variables, and constant variables with an address.  \n\n**Steps:** Right-click on a reference and choose ``Show X-Refs``  \n\n**Shortcut:** :kbd:`X`\n\n\nDebug Context Menu\n=======================================\n\nAdd or Remove Breakpoint\n-------------------------\n**Description:** Add a breakpoint at the earliest offset in the line under the cursor. If you use the keyboard shortcut to remove a breakpoint, all the breakpoints defined in the line will be removed.\n\n**Steps:** Right-click on a line of code and choose :menuselection:`Breakpoint --> Add breakpoint`  or :menuselection:`Breakpoint --> Remove breakpoint`.\n\n**Shortcut:** :kbd:`F2`  \n\nAdvanced Breakpoint Dialog\n----------------------------------------\n**Description:** Open the advanced breakpoint dialog. This dialog lets you define not only a regular breakpoint in this address, but also a hardware breakpoint, a conditional breakpoint, and more.\n\n**Steps:** Right-click on a line of code and choose :menuselection:`Breakpoint --> Advanced breakpoint`. If multiple breakpoints are present in the line, you will be able choose the breakpoint you want to edit from the :menuselection:`Edit breakpoint` submenu.\n\n**Shortcut:** :kbd:`Ctrl-F2`\n\nContinue Until Line\n----------------------------------------\n**Description:** Continue the execution of the program until it reaches the offset in the selected line. The program is not guaranteed to ever reach this address and will keep running until exited or until reached another breakpoint. If other breakpoints hit before reaching this line, they will be triggered and pause the execution. *This option is only available on Debug or Emulation modes*.      \n\n**Steps:** While in Debug or Emulation modes, right-click on a line of code and choose :menuselection:`Debug --> Continue until line`.  \n\nSet Program Counter (PC)\n----------------------------------------\n**Description:** Set the Program Counter of the debugger to the current offset. For example, on an Intel 64bit program, Cutter will set the value of the RIP register to the current address.  *This option is only available on Debug or Emulation modes*.  \n\n**Steps:** While in Debug or Emulation modes, right-click on a line of code and choose :menuselection:`Debug --> Set PC`.\n"
  },
  {
    "path": "docs/source/user-docs/menus/disassembly-context-menu/debug-context-menu.rst",
    "content": "Debug Context Menu\n==============================\n\nContinue Until Line\n----------------------------------------\n**Description:** Continue the execution of the program until reached the selected offset. The program is not guaranteed to ever reach this address and will keep running until exited or until reached another breakpoint. If other breakpoints hit before reaching this line, they will be triggered and pause the execution. *This option is only available on Debug or Emulation modes*.      \n\n**Steps:** While in Debug or Emulation modes, right-click and address and choose ``Debug -> Continue until line``.  \n\nSet Program Counter (PC)\n----------------------------------------\n**Description:** Set the Program Counter of the debuggee to the current offset. For example, on an Intel 64bit program, Cutter will set the value of the RIP register to the current address.  *This option is only available on Debug or Emulation modes*.  \n\n**Steps:** While in Debug or Emulation modes, right-click and address and choose ``Debug -> Set PC``.\n"
  },
  {
    "path": "docs/source/user-docs/menus/disassembly-context-menu/manage-breakpoints-context-menu.rst",
    "content": "Manage Breakpoints Context Menu\n================================\n\nAdd or Remove a Breakpoint\n----------------------------------------\n**Description:** Add a breakpoint at the current address. If a breakpoint already exists, Cutter will remove it.  \n\n**Steps:** Right-click on an instruction and choose ``Breakpoint -> Add/remove breakpoint``  \n\n**Shortcut:** :kbd:`F2`  \n\nAdvanced Breakpoint Dialog\n----------------------------------------\n**Description:** Open the advanced breakpoint dialog. This dialog lets you define not only a regular breakpoint in this address, but also a Hardware breakpoint, a conditional breakpoint, and more.\n\n**Steps:** Right-click on an instruction and choose ``Breakpoint -> Advanced breakpoint``.  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`F2`"
  },
  {
    "path": "docs/source/user-docs/menus/disassembly-context-menu/patching.rst",
    "content": "Patching\n==============================\n\nEdit Instruction\n----------------------------------------\n**Description:** Edit the current instruction by typing a sequence of one or more instructions. Cutter will automatically fetch a preview of the bytes that are constructing the instruction.  \n\n**Steps:** Right-click on an instruction and choose ``Edit -> Instruction``  \n\nEdit Bytes\n----------------------------------------\n**Description:** Edit the bytes of the current instruction by typing a sequence of bytes. Cutter will automatically disassemble a preview of the instructions that are create by the typed bytes.  \n\n**Steps:** Right-click on an instruction and choose ``Edit -> Bytes``  \n\nNOP Instruction\n----------------------------------------\n**Description:** Fill the content of the instruction with NOP instructions. Cutter will fill the instructions with NOP as the length of the bytes constructing the instruction.   \n\n**Steps:** Right-click on an instruction and choose ``Edit -> Nop Instruction``  \n\nReverse Jump\n----------------------------------------\n**Description:** On conditional jumps, Cutter will detect the inverted conditional instruction and will replace it. For example, from ``je`` to ``jne``.  \n\n**Steps:** Right-click on an instruction and choose ``Edit -> Reverse Jump``"
  },
  {
    "path": "docs/source/user-docs/menus/disassembly-context-menu/set-as-code-data-string.rst",
    "content": "Set as Code\\Data\\String\n==============================\n\nSet as Code\n----------------------------------------\n**Description:** Set the current instruction to Code. This will force Cutter to display the current instruction as Code.  \n\n**Steps:** Set as... -> Code  \n\n**Shortcut:** :kbd:`C`    \n\nSet as String (auto-detect length) \n----------------------------------------\n**Description:** Set the current location to String. This will tell Cutter to treat the current address as a string and will auto-detect the length (e.g by looking for a string null-terminator).   \n\n**Steps:** Right click on an instruction and choose ``Set as... -> String... -> Auto-detect``  \n\n**Shortcut:** :kbd:`A`  \n\nRemove string definition\n----------------------------------------\n**Description:** Remove a defined string in this address. Cutter will then treat this location as a code.  \n\n**Steps:** Right click on an instruction and choose ``Set as... -> String... -> Remove``.  \n\nSet as String (Advance dialog)\n----------------------------------------\n**Description:** Set the current location to String. This will open a String definition dialog in which the user will supply the length and the type (ASCII, UTF8, ...) of the string. Cutter will then treat the current address as the defined string.  \n\n**Steps:** Right click on an instruction and choose ``Set as... -> String... -> Advanced``.  \n\nSet as data (bytes)\n----------------------------------------\n**Description:** Convert the instruction to data of Bytes.   \n\n**Steps:** Right click on an instruction and choose ``Set as... -> Data... -> Byte``  \n\nSet as data (Word)\n----------------------------------------\n**Description:** Convert the instruction to data of Words.  \n\n**Steps:** Right click on an instruction and choose ``Set as... -> Data... -> Word``  \n\nSet as data (Dword)\n----------------------------------------\n**Description:** Convert the instruction to data of Dwords.  \n\n**Steps:** Right click on an instruction and choose ``Set as... -> Data... -> Dword``  \n\nSet as data (Qword)\n----------------------------------------\n**Description:** Convert the instruction to data of Qwords.  \n\n**Steps:** Right click on an instruction and choose ``Set as... -> Data... -> Qword``  \n\nSet as data (Advanced)\n----------------------------------------\n**Description:** Open an advanced dialog to define the custom data type of the current instruction.  \n\n**Steps:** Right click on an instruction and choose ``Set as... -> Data... -> ...``  \n\n**Shortcut:** :kbd:`*`"
  },
  {
    "path": "docs/source/user-docs/menus/disassembly-context-menu/set-current-bits.rst",
    "content": "Set Current Bits\n==============================\n\nSet Current Bits to 16\n----------------------------------------\n**Description:** Set the current instruction to 16-bit.  \n\n**Steps:** Right-click on an instruction and choose ``Set current bits to... -> 16``  \n\nSet Current Bits to 32\n----------------------------------------\n**Description:** Set the current instruction to 32-bit.    \n\n**Steps:** Right-click on an instruction and choose ``Set current bits to... -> 32``  \n\nSet Current Bits to 64\n----------------------------------------\n**Description:** Set the current instruction to 64-bit.    \n\n**Steps:** Right-click on an instruction and choose ``Set current bits to... -> 64``"
  },
  {
    "path": "docs/source/user-docs/menus/disassembly-context-menu/set-immediate-base.rst",
    "content": "Set Immediate Base\n==============================\n\nSet Immediate Base to Binary\n----------------------------------------\n**Description:** Set the immediate value of the operand to a Binary representation.\n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> Binary``  \n\nSet Immediate Base to Octal\n----------------------------------------\n**Description:** Set the immediate value of the operand to an Octal representation.\n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> Octal``  \n\nSet Immediate Base to Decimal\n----------------------------------------\n**Description:** Set the immediate value of the operand to a Decimal representation.\n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> Decimal``  \n\nSet Immediate Base to Hexadecimal\n----------------------------------------\n**Description:** Set the immediate value of the operand to Hexadecimal.  \n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> Hexadecimal``  \n\nSet Immediate Base to Network Port\n----------------------------------------\n**Description:** Set the immediate value of the operand to a Network Port  \n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> Network Port``  \n\nSet Immediate Base to IP Address\n----------------------------------------\n**Description:** Set the immediate value of the operand to an IP Address  \n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> IP Address``  \n\nSet Immediate Base to Syscall\n----------------------------------------\n**Description:** Set the immediate value of the operand to Syscall  \n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> Syscall``  \n\nSet Immediate Base to String\n----------------------------------------\n**Description:** Set the immediate value of the operand to String   \n\n**Steps:** Right-click on an immediate base of instruction and choose ``Set Immediate Base to... -> String``"
  },
  {
    "path": "docs/source/user-docs/menus/disassembly-context-menu.rst",
    "content": "Disassembly Context Menu \n==============================\n\n.. toctree::\n   :maxdepth: 1\n   :glob:\n\n   disassembly-context-menu/*\n\nThe Disassembly context menu contains actions that operate with selected instruction in disassembly and graph widgets.\n\nCopy\n----------------------------------------\n**Description:** Copy the selected text.  \n\n**Steps:**  Right-click on a selected text and choose ``Copy``  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`C`  \n\nCopy Address\n----------------------------------------\n**Description:** Copy the address of the location under the cursor.  \n\n**Steps:**  Right-click on a location and choose ``Copy address``  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`C`  \n\nShow address in another widget\n----------------------------------------\n**Description:** Show the selected address or item in another opened widget, or open a new one.  \n\n**Steps:**  Right-click on an address or an item in instruction and choose the ``Show in`` sub-menu \n\nAdd Comment\n----------------------------------------\n**Description:** Add a comment in the current location.  \n\n**Steps:** Right-click an address and choose `Add Comment`.  \n\n**Shortcut:** :kbd:`;`  \n\nAdd Flag\n----------------------------------------\n**Description:** Add a flag to the selected item or location by bookmarking and giving it a name.  \n\n**Steps:** Right-click an address or item and choose ``Add Flag``. This will open the Flag dialog in which you can name the location.  \n\nRename\n----------------------------------------\n**Description:** Rename the flag, function or local variable at current location. If empty, remove the currently associated name.\n\n**Steps:** Right-click an address or item and choose ``Rename``  \n\n**Shortcut:** :kbd:`N`  \n\nEdit Function\n----------------------------------------\n**Description:** Open the Function edit dialog in which you can define the name of the function, its start address, stack size, calling convention, and more.  \n\n**Steps:**  Right-click on a location inside a function and choose ``Edit function``.  \n\n**Shortcut:** :kbd:`Shift` + :kbd:`P`  \n\nRe-type Local Variables\n----------------------------------------\n**Description:** Rename or set the types of the function's variables and arguments.  \n\n**Steps:** Right-click anywhere inside a function and then choose ``Re-type Local Variables``.\n\n**Shortcut:** :kbd:`Y`  \n\nDelete a Comment\n----------------------------------------\n**Description:** Delete the comment at the current address. This option only available for addresses with user-defined comments. \n\n**Steps:** Right-click on an instruction with a user-defined comment and choose ``Delete comment``  \n\nDelete a Flag\n----------------------------------------\n**Description:**   Delete the flag at the current location.\n\n**Steps:** Right-click on a location with a flag and choose ``Delete flag``.  \n\nUndefine a Function\n----------------------------------------\n**Description:** Undefine the current function. This will remove the function and its associated meta-data. You can always re-define the function, but every change that was made to the previously defined function (e.g variable renaming) would not be restored.  \n\n**Steps:**  Right-Click on the name of the function and choose ``Undefine function``.  \n\n**Shortcut:** :kbd:`U`  \n\nDefine a function\n----------------------------------------\n**Description:** Define a function starting from the current location. Cutter will automatically guess the size of the function. This can later be changed using the function editor.  \n\n**Steps:** Right-click on an instruction and choose ``Define function here``.  \n\n**Shortcut:** :kbd:`P`  \n\nSet Structure Offset\n----------------------------------------\n**Description:** Present the current value is an offset in a structure. \n\n**Steps:**  -> Structure offset  \n\nShow Cross References\n----------------------------------------\n**Description:** Show X-Refs from and to the specific location. This option will open Cutter's X-Refs dialog in which you will be able to see a list of X-Refs from and to the selected location, in addition to a preview of each cross-reference to quickly inspect the different usages.  \n\n**Steps:** Right-click on an instruction and choose ``Show X-Refs``  \n\n**Shortcut:** :kbd:`X`\n\n"
  },
  {
    "path": "docs/source/user-docs/menus/graph-widget-context-menu.rst",
    "content": "Graph Widget Context Menu \n==============================\n\n\nHighlight Block\n----------------------------------------\n**Description:** Open the Color Picker dialog to set a background color for the selected block in the Graph view.  \n\n**Steps:** Right-click on a node\\block in the Graph view and choose ``Highlight block``.  \n\nUnHighlight Block\n----------------------------------------\n**Description:** If a block in the Graph view is highlighted, this option will unhighlight it and restore the original color.  \n\n**Steps:** Right-click on a highlighted node\\block in the Graph view and choose ``Unhighlight block``.  \n\nHighlight Instruction\n----------------------------------------\n**Description:** Open the Color Picker dialog to set a background color for the selected instruction in the Graph view.    \n\n**Steps:** Right-click on an instruction in the Graph view and choose ``Highlight instruction``.  \n\nUnHighlight Instruction\n----------------------------------------\n**Description:** If an instruction in the Graph view is highlighted, this option will unhighlight it and restore the original color.   \n\n**Steps:** Right-click on a highlighted instruction in the Graph view and choose ``Unhighlight instruction``.  \n\nExport Graph\n----------------------------------------\n**Description:** Export the current graph to one of the following formats:\n - PNG Image\n - JPEG Image\n - SVG Image\n\nWhen Graphviz is installed, the following options are also available:\n - Graphviz PostScript File\n - Graphviz Dot File\n - Graphviz JSON File\n - Graphviz Gif Image\n - Graphviz PNG Image\n - Graphviz JPG Image\n - Graphviz SVG Image\n\n**Steps:** Right-click anywhere on the Graph view and choose ``Export Graph``.  \n\n\nGraph Layout Direction\n----------------------------------------\n**Description:** Graph layout direction can be either vertical top to bottom or horizontal left to right.\n\n\n**Steps:** Right-click anywhere on the Graph view  ``Layout -> Horizontal``.\n\nChoose Graph Layout\n----------------------------------------\n**Description:** Choose the layout to be used by Cutter to display the Graph. Cutter supports the following Graph layout algorithms:\n\n - Grid narrow  \n - Grid medium  \n - Grid wide  \n \nWhen Graphviz is installed, the following options are also available:\n\n - Graphviz polyline\n - Graphviz ortho\n\n**Steps:** Right-click anywhere on the Graph view and choose a layout from the ``Layout`` sub-menu."
  },
  {
    "path": "docs/source/user-docs/menus/hexdump-context-menu/patching.rst",
    "content": "Patching\n==============================\n\nWrite String\n----------------------------------------\n**Description:** Write ASCII string at the current location. If multiple bytes are selected, the string will be written from the start of the selection. Please note, this item won't write a null-terminated nor wide string. Please see the other items for these features.\n\n**Steps:** Right-click on a byte and select ``Edit -> Write string``  \n\nWrite Length and String\n----------------------------------------\n**Description:** Write a length prefix followed by ASCII string at the current location. If multiple bytes are selected, the string will be written from the start of the selection. Please note, although similar to BSTR in its concept, this item would not add a null terminator WCHAR at the end of the string.\n\n**Steps:** Right-click on a byte and select ``Edit -> Write length and string``  \n\n\nWrite Wide String\n----------------------------------------\n**Description:** Write null-terminated wide string at the current location. If multiple bytes are selected, the string will be written from the start of the selection.\n\n**Steps:** Right-click on a byte and select ``Edit -> Write wide string``  \n\n\nWrite Null-Terminated String\n----------------------------------------\n**Description:** Write a null-terminated ASCII string at the current location. If multiple bytes are selected, the string will be written from the start of the selection.\n\n**Steps:** Right-click on a byte and select ``Edit -> Write zero terminated string``  \n\n\nWrite Encoded\\\\Decoded Base64 String\n----------------------------------------\n**Description:** Write an encoded or decoded base64 string at the current location. If multiple bytes are selected, the string will be written from the start of the selection.\n\n**Steps:** Right-click on a byte and select ``Edit -> Write De\\Encoded Base64 string`` . On the dialog that will open choose whether you want to encode a string or decode one.\n\n\nWrite Zeroes\n----------------------------------------\n**Description:** Write null-bytes at the current location. The number of null-bytes is specified by the user. If multiple bytes are selected, the null-bytes will be written from the start of the selection.\n\n**Steps:** Right-click on a byte and select ``Edit -> Write zeroes``. On the dialog that will open, specify how many null-bytes you'd like to write.\n\n\nWrite Random Bytes\n----------------------------------------\n**Description:** Write random bytes at the current location. The number of bytes is specified by the user. If multiple bytes are selected, the null-bytes will be written from the start of the selection.\n\n**Steps:** Right-click on a byte and select ``Edit -> Write random bytes``. On the dialog that will open, specify how many bytes you'd like to write.\n\n\nDuplicate Bytes From Offset\n----------------------------------------\n**Description:** Duplicate N bytes from an offset to the current location. The number of bytes to duplicated and the offset of origin are specified by the user. If multiple bytes are selected, the bytes will be written from the start of the selection. A preview pane will display the bytes to be copied.\n\n**Steps:** Right-click on a byte and select ``Edit -> Duplicate from offset``. On the dialog that will open, specify the offset from which to copy, and how many bytes to copy.  \n\n\nIncrement/Decrement Bytes\n----------------------------------------\n**Description:** Increment or decrement Byte, Word, Dword or Qword at the current location. The value to add or subtract from the location is specified by the user. If multiple bytes are selected, the function will apply on the start of the selection.\n\n**Steps:** Right-click on a byte and select ``Edit -> Increment/Decrement``. On the dialog that will open, specify if you'd like to modify a Byte, Word, Dword or Qword, choose the value of the operation, and choose whether you want to increment or decrement."
  },
  {
    "path": "docs/source/user-docs/menus/hexdump-widget-context-menu.rst",
    "content": "Hexdump Widget Context Menu\n==============================\n.. toctree::\n   :maxdepth: 1\n   :glob:\n\n   hexdump-context-menu/*\n\nBytes Per Row\n----------------------------------------\n**Description:** Select how many bytes should be displayed by Cutter in each row in the Hexdump widget.\n\nAvailable options are:   \n - 1 byte\n - 2 bytes\n - 4 bytes\n - 8 bytes\n - 16 bytes\n - 32 bytes\n - Power of 2 (Auto)"
  },
  {
    "path": "docs/source/user-docs/menus/information-windows-menu.rst",
    "content": "Information Windows Menu\n==============================\n\nInfo sub-menu\n----------------------------------------\n**Description:** This is a sub-menu of the Windows menu. It contains a list of available widgets to get more information about the opened binary.\n\n**Steps:** Windows -> Info...  \n\nShow Classes\n----------------------------------------\n**Description:** List the classes that were detected in the binary.  \n\n**Steps:** Windows -> Info... -> Classes  \n\nShow Entry Points\n----------------------------------------\n**Description:** List the entry points of the binary  \n\n**Steps:** Windows -> Info... -> Entry Points  \n\nShow Exports\n----------------------------------------\n**Description:** Show a list of exported functions in the binary.  \n\n**Steps:** Windows -> Info... -> Exports   \n\n**Shortcut:** :kbd:`Shift` + :kbd:`E`\n\nShow Flags\n----------------------------------------\n**Description:** Show the Flags widget. Flags are conceptually similar to bookmarks. They associate a name with a given offset in a file. Function names, strings, and more are considered flags.  \n\n**Steps:** Windows -> Info... -> Flags  \n\nShow Headers\n----------------------------------------\n**Description:** Show the Headers widget, displaying information about the opened file headers.  \n\n**Steps:** Windows -> Info... -> Headers  \n\nShow Imports\n----------------------------------------\n**Description:** Show the Imports widget with a list of all the imported functions required by the binary.  \n\n**Steps:** Windows -> Info... -> Imports   \n\n**Shortcut:** :kbd:`Shift` + :kbd:`I`\n\nShow Relocations\n----------------------------------------\n**Description:** Show the Relocations widget, displaying a list of relocation information.  \n\n**Steps:** Windows -> Info... -> Relocs  \n\nShow Resources\n----------------------------------------\n**Description:** Show the resources widget, which contains a list of the resources inside the binary.  \n\n**Steps:** Windows -> Info... -> Resources  \n\nShow SDB Browser\n----------------------------------------\n**Description:** Show the SDB browser widget.  \n\n**Steps:** Windows -> Info... -> SDB Browser  \n\nShow Sections\n----------------------------------------\n**Description:** Show the Sections widget which contains a list of all the sections found in the binary. The view also contains a nice visualization of raw and virtual memory layout.  \n\n**Steps:** Windows -> Info... -> Sections  \n\nShow Segments\n----------------------------------------\n**Description:** Show the Segments widget with a list of the binary segments.  \n\n**Steps:** Windows -> Info... -> Segments  \n\nShow Symbols\n----------------------------------------\n**Description:** Show the Symbols widget, with a list of symbol information from the binary, such as function names, and more.  \n\n**Steps:** Windows -> Info... -> Symbols  \n\nShow VTables\n----------------------------------------\n**Description:** Show the VTables widget, which contains information about the Virtual Tables found in the binary.  \n\n**Steps:** Windows -> Info... -> VTables  \n\nShow Signatures\n----------------------------------------\n**Description:** Cutter supports the creation and the utilization of signatures. This widget lists all the signatures available to cutter.  \n\n**Steps:** Windows -> Info... -> Signatures"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/debug-menu.rst",
    "content": "Debug Menu\n==============================\n\n\nStart Debugging\n----------------------------------------\n**Description:** Start the debugging session of the current loaded binary.  \n\n**Steps:** Debug -> Start debug  \n\n**Shortcut:** :kbd:`F9`  \n\nStart Emulation\n----------------------------------------\n**Description:** Start an emulation session on the current loaded binary. Cutter supports emulation of different file formats. Unlike debugging, emulation isn't really executing the binary, but only emulating the instructions. This is very strong feature for analysis of self-contained functions or programs, to analyze cryptographic algorithms or to deobfuscate data. Emulation isn't limited by the running platform, so Linux files such as ELF can be emulated on Windows platforms, and DLL can be emulated on Linux.  \n\n**Steps:** Debug -> Start emulation  \n\nAttach to Process\n----------------------------------------\n**Description:** Attach Cutter's debugger to a running process, instead of spawning a new process.  \n\n**Steps:** Debug -> Attach to process  \n\nConnect to a Remote Debugger\n----------------------------------------\n**Description:** Connect Cutter to a remote debugger such as GDB ot WinDbg by providing IP and Port of the remote debugger..  \n\n**Steps:** Debug -> Connect to a remote debugger  \n\nStep Into\n----------------------------------------\n**Description:** Execute a single assembler instruction, stepping into functions and loops.  \n\n**Steps:** Debug -> Step  \n\n**Shortcut:** :kbd:`F7`  \n\nStep Over\n----------------------------------------\n**Description:** Execute a single assembler instruction, stepping over functions and procedures. The functions will not be skipped and will be executed by Cutter. The execution will pause when reaching the instruction after the function call.    \n\n**Steps:** Debug -> Step over  \n\n**Shortcut:** :kbd:`F8`  \n\nStep Out\n----------------------------------------\n**Description:** Execute the code and suspends execution when the current function returns  \n\n**Steps:** Debug -> Step out  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`F8`  \n\nContinue\n----------------------------------------\n**Description:** Continue the execution of the running program. The execution will stop when reached a breakpoint, when manually suspended by the user, or when the running program quits.   \n\n**Steps:** Debug -> Continue  \n\n**Shortcut:** :kbd:`F5`  \n\nContinue Until Call\n----------------------------------------\n**Description:** Continue the execution of the program until a function call is reached.  \n\n**Steps:** Debug -> Continue until call  \n\nContinue Until Syscall\n----------------------------------------\n**Description:** Continue the execution of the program until a Syscall is reached.  \n\n**Steps:** Debug -> Continue until syscall\n"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/debug-view-menu.rst",
    "content": "Debug -> View Menu\n==============================\n\nShow Backtrace\n----------------------------------------\n**Description:** Display the Backtrace window.  \n***Note:** This view only available on Debug mode.*\n\n\n**Steps:** Debug -> View -> Backtrace  \n\nShow Breakpoints\n----------------------------------------\n**Description:** Show the Breakpoints widget in which you can list, manage, add, remove and define breakpoints.  \n\n**Steps:** Debug -> View -> Breakpoints  \n\nShow Threads\n----------------------------------------\n**Description:** List the threads of the running application.  \n***Note:** This view only available on Debug mode.*\n\n\n**Steps:** Debug -> View -> Threads  \n\nShow Processes\n----------------------------------------\n**Description:** The processes widget shows all the child processes and allows the user to switch between them. This is useful feature for kernel debugging or for following forks.  \n***Note:** This view only available on Debug mode.*\n\n\n**Steps:** Debug -> View -> Processes  \n\nShow Memory Map\n----------------------------------------\n**Description:** Show the Memory Map widget which shows the mapped memory pages of the running process. Including mapped memory of loaded libraries and dynamically allocated maps.  \n***Note:** This view only available on Debug mode.*\n\n\n**Steps:** Debug -> View -> Memory map  \n\nShow Registers\n----------------------------------------\n**Description:** Show the Registers widget with information of the registers and their value of the running process. The widget allows not only to view registers and flags, but also to manipulate and modify them.  \n***Note:** This view only available on Debug mode.*\n\n\n**Steps:** Debug -> View -> Registers  \n\nShow Register References\n----------------------------------------\n**Description:** The Register References widget is an extended view of the Register widget which allows you to \"telescope\", dereference, the register values in multiple depths.  \n***Note:** This view only available on Debug mode.*\n\n\n**Steps:** Debug -> View -> Register References  \n\nShow Stack\n----------------------------------------\n**Description:** Show the Stack widget, listing the stack values and structure of the running process.  \n***Note:** This view only available on Debug mode.*\n\n\n**Steps:** Debug -> View -> Stack\n"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/edit-menu.rst",
    "content": "Edit Menu\n==============================\n\nShow Search Widget\n----------------------------------------\n**Description:** Show the Search widget  \n\n**Steps:** Edit -> Search  \n\nGo Back\n----------------------------------------\n**Description:** Seek backward to your previous location.  \n\n**Steps:** Edit -> Undo Seek  \n\n**Shortcut:** :kbd:`Alt` + :kbd:`Left`  \n\nRedo Seek\n----------------------------------------\n**Description:** Seek forward a location.   \n\n**Steps:** Edit -> Redo Seek  \n\n**Shortcut:** :kbd:`Alt` + :kbd:`Right`  \n\nPreferences\n----------------------------------------\n**Description:** Open the preferences dialog to access and define Cutter's configurations.  \n\n**Steps:** Edit -> Preferences"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/file-menu.rst",
    "content": "File Menu\n==============================\n\nNew Instance of Cutter\n----------------------------------------\n**Description:** Open a new instance of Cutter to start a new session. This option will open a new window of Cutter without exiting the current session.  \n\n**Steps:** File -> New Window  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`N`  \n\nMap a New File\n----------------------------------------\n**Description:** Cutter allows you to map the contents of other files into the same I/O space used to contain the loaded binary. The new contents can be placed at random or specific offsets.\nSpecifically, Cutter is able to open files and map portions of them at random or specific places in memory. It is the perfect basic tooling to reproduce an environment like a core file, a debug session, or a framework by also loading and mapping all the libraries and files the binary depends on.  \n\n**Steps:** File -> Map File  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`M`  \n\nImport a PDB File\n----------------------------------------\n**Description:** Cutter allows you to load additional debugging information by loading external PDB files. Unlike other platforms, Cutter does not rely on Windows API to parse a PDB files, thus they can be loaded on any other supported platform like Linux or macOS.    \n\n**Steps:** File -> Import PDB  \n\nEnable Write Mode\n-----------------------\n**Description:** This option will enable Write mode and allow you to patch and edit the binary on disk. **Please note** that when Write mode is enabled, each change you make to the binary in Cutter will be applied to the original file on disk. In order not to take unnecessary risks, consider using Cache mode instead.\n\n**Steps:** File -> Set mode -> Write mode\n\n\nEnable Cache Mode\n-----------------------\n**Description:** This option will enable Cache mode and allow you to patch and edit the binary **without** applying the changes to the file on disk. Unlike in Write mode, in Cache mode, the changes you make to the binary in Cutter will not be applied to the original file on disk unless you specifically committing them using the \"Commit changes\" menu item. This is safer than using Write mode because there is no risk to lose important data.\n\n**Steps:** File -> Set mode -> Cache mode\n\n\nEnable Read-Only Mode\n------------------------\n**Description:** This option is available when files are opened in Write or Cache modes. When Read-Only mode is enabled, no patches and editions to the file are allowed. This is the default mode for files in Cutter, unless specified otherwise by the user, by either enabling Write or Cache mode.\n\n**Steps:** File -> Set mode -> Read-Only mode\n\nCommit Changes from Cache\n----------------------------\n**Description:** Apply the changes performed in Cache mode to the file on disk. Cache mode will not apply the changes and patches made unless the user clicks \"Commit changes\". To automatically apply every change to the file on disk, use the less-safer Write mode.\n\n**Steps:** File -> Commit changes\n\n\nSave Project\n----------------------------------------\n**Description:** Save your session to a project. If no project file assigned to your session, the \"Save as...\" dialog will open.  \n\n**Steps:** File -> Save  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`S`  \n\nSave Project as...\n----------------------------------------\n**Description:** Save the current state of your session, including function names, comments, and more.\n**Note:** This feature is currently in `Beta<https://rizin.re/posts/introducing-projects/>`_.\n\n\n**Steps:** File -> Save As...  \n\nExport to Code\n----------------------------------------\n**Description:** Export the entire binary in different formats that later can be used in your favorite programming language. The feature supports many formats such as Python arrays, java, several C array formats, javascript, and more.   \n***Note:** This isn't a decompilation feature.*\n\n\n**Steps:** File -> Export as code  \n\nRun a Rizin Script\n----------------------------------------\n**Description:** Cutter allows you to execute Rizin scripts to automate task or transfer information. Rizin scripts are files that contain list of Rizin commands. The scripts can be created manually by you, or automatically generated by Rizin commands (which typically end with a star character. e.g. ``afl*``).   \n\nSuch a script can look like this:\n\n.. code-block::\n\n    ?e hello world\n    ?v 10+5\n    pdf @ main\n\n**Steps:** File -> Run Rizin script  \n\nQuit Cutter\n----------------------------------------\n**Description:** Quit and exit your current session of Cutter. On exit, you'll be asked whether you want to save your session in order to avoid losing data.   \n\n**Steps:** File -> Quit  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`Q`\n"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/help-menu.rst",
    "content": "Help Menu\n==============================\n\nAbout\n----------------------------------------\n**Description:**  Learn more about Cutter. View the current version of the application, the installed plugins, the authors, building options, the license, and more.  \n\n**Steps:** Help -> About  \n\nReport an Issue\n----------------------------------------\n**Description:** Quickly report an issue to Cutter's GitHub repository. Clicking this option will navigate your browser to the new-issue page in Cutter's GitHub repository. It will also automatically fill relevant information inside the issue template.    \n\n**Steps:** Help -> Report an issue\n\nDocumentation\n---------------------------------------\n**Description:** Clicking this option will open the user documentation of Cutter in your browser.\n\n**Steps:** Help -> Documentation\n"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/plugins-menu.rst",
    "content": "Plugins Menu\n==============================\n\nPlugins Sub-Menu\n----------------------------------------\n**Description:** This menu will contain the windows and views created by the loaded plugins. By default, this menu is empty unless plugins added their actions and items to the menu.    \n\n**Steps:** Windows -> Plugins\n"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/view-menu.rst",
    "content": "View Menu\n==============================\n\n\nRefresh Contents\n----------------------------------------\n**Description:** In some cases, not all the displayed information on Cutter's widgets will be up-to-date, for example - after defining a new function from the integrated Rizin console. By refreshing the contents, Cutter will fetch the most up to date information from the session and will update the different views.\n\n***Note:** In the future, Cutter will be aware to any underlying change and will update everything automatically. This is currently a work-in-progress.*\n\n\n**Steps:** View -> Refresh Contents  \n\nReset to Default Layout\n----------------------------------------\n**Description:** Reset the current :doc:`layout</user-docs/preferences/layout>` to the default layout provided by Cutter.\n\n**Steps:** View -> Reset to default layout\n\nReset to Default Settings\n----------------------------------------\n**Description:** Reset the current settings to the default settings defined by Cutter.  \n\n**Steps:** View -> Reset Settings  \n\nLock and Unlock Panels\n----------------------------------------\n**Description:** Allow or disable moving and closing of different widgets. Uncheck this option to prevent accidentally modifying current layout.\n\n**Steps:** View -> Unlock Panels  \n\nShow Tabs at the Top\n----------------------------------------\n**Description:** Toggle the position of the tab bar.  \n\n**Steps:** View -> Show Tabs at the Top  \n\nGrouped Dock Dragging\n----------------------------------------\n**Description:** When enabled, dragging a widget will also drag the widgets which are grouped to it. You can drag a specific widget from a group by dragging from the tab itself and not from the title bar. Disable this option to always drag individual widgets.   \n\n**Steps:** View -> Grouped dock dragging  \n\n\nZoom In\n----------------------------------------\n**Description:** Zoom-In inside different widgets such as Graph, Disassembly and Hexdump.     \n\n**Steps:** View -> Zoom -> Zoom In  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`+`  \n\nZoom Out\n----------------------------------------\n**Description:** Zoom-Out inside different widgets such as Graph, Disassembly and Hexdump.   \n\n**Steps:** View -> Zoom -> Zoom Out  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`-`  \n\nReset Zoom\n----------------------------------------\n**Description:** Reset the zoom to its default size.   \n\n**Steps:** View -> Zoom -> Reset  \n\n**Shortcut:** :kbd:`Ctrl` + :kbd:`=`\n\n\nManage Layouts\n----------------------------------------\n**Description:**  Rename and delete saved :doc:`layouts</user-docs/preferences/layout>`.\n\n**Steps:** View -> Manage layouts , select layout, choose command\n\nSave Layout\n----------------------------------------\n**Description:** Save the current :doc:`layout</user-docs/preferences/layout>` with a given name. A layout includes the set of currently opened widgets, their position, and some properties.\n\n**Steps:** View -> Save Layout , enter a layout name in the dialog.\n\nLayouts\n----------------------------------------\n**Description:** Load the settings from the selected :doc:`layout</user-docs/preferences/layout>` into the current layout. Loading a layout will not cause it to automatically be modified. To do that you must use the `Save layout`_ command.\n\n**Steps:** View -> Layouts ->  layout name\n"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar/windows-menu.rst",
    "content": "Windows Menu\n==============================\n\nShow Dashboard\n----------------------------------------\n**Description:** Show the Dashboard panel. Cutter's dashboard contains basic information about the binary. On the Dashboard you can find:\n - File name\n - Binary format (PE, ELF64, ...)  \n - Bits (16, 32, 64, ...)\n - Binary Architecture (x86, ARM, ...)\n - Access mode (Read, Write, Execute)\n - Size\n - Binary type\n - Written Language\n - Compiler detection\n - Stack Canaries\n - NX bit\n - Position independent code\n - Checksums (MD5, SHA1, ...)\n - Entropy\n - and more...\n\n\n**Steps:** Windows -> Dashboard  \n\nShow Functions\n----------------------------------------\n**Description:** Display the list of functions identified by Cutter. The list also contains information about each function such as name, address, size, and more. Some functions like Main, Entrypoint and external functions are highlighted with specific colors to make them easier to spot.   \n\n**Steps:** Windows -> Functions  \n\nShow Decompiler\n----------------------------------------\n**Description:** Cutter releases are shipped with two decompilers by default - [rz-ghidra](https://github.com/rizinorg/rz-ghidra) and [rz-dec](#) which will be available soon. The Decompiler view will display the decompilation of the current function. The widget is interactive and support address-syncing, renaming, re-typing and more. Cutter can be extended with more decompilers.   \n\n**Steps:** Windows -> Decompiler  \n\nGraph Overview\n----------------------------------------\n**Description:** One of the main views of Cutter allows you to navigate inside functions in a graph mode. Graph overview will only display a zoomed-out form of the graph, and will help the user understand the flow of a function as a whole.  \n\n**Steps:** Windows -> Graph Overview  \n\nShow Search\n----------------------------------------\n**Description:** Show the Search panel in which you can search data, strings, hex and more in the opened binary.  \n\n**Steps:** Windows -> Search  \n\nShow Strings\n----------------------------------------\n**Description:** Show the Strings view that will display all the printable strings in the program. A combo-box will allow the user to choose whether they want to view strings from the entire binary or from specific segments and sections.    \n\n**Steps:** Windows -> Strings  \n\n**Shortcut:** :kbd:`Shift` + :kbd:`F12`\n\nShow Types\n----------------------------------------\n**Description:** Show the Types widget in which you can define, load, export and manage data types such as Structures and Enums,  \n\n**Steps:** Windows -> Types  \n\nAdd a new instance of the Decompiler Widget\n----------------------------------------------\n**Description:** Create a new instance of the Decompiler widget in order to view multiple decompiled functions using multiple supported decompilers.\n\n**Steps:** Windows -> Add Decompiler  \n\nAdd a new instance of the Disassembly Widget\n----------------------------------------------\n**Description:** Create a new instance of the Disassembly widget in order to view one or multiple locations at the same time.   \n\n**Steps:** Windows -> Add Disassembly  \n\nAdd a new instance of the Graph Widget\n----------------------------------------\n**Description:** One of the main views of Cutter allows you to navigate inside functions in a graph mode. This view displays the flow of a function where each node on the graph represents a basic block in the function. The edges coming-to and getting-out of the blocks represent the control flow. The menu item will create a new instance of the Graph widget in order to view one or multiple locations at the same time. \n\n**Steps:** Windows -> Add Graph  \n\nAdd a new instance of the Hexdump Widget\n-------------------------------------------\n**Description:** Create a new instance of the Hexdump widget in order to view one or multiple locations at the same time.   \n\n**Steps:** Windows -> Add Hexdump  \n\n**Shortcut:** :kbd:`Shift` + :kbd:`G`\n \n\nShow Comments\n----------------------------------------\n**Description:** Show the comments widgets in order to view the automatic and user-defined comments in this session.  \n\n**Steps:** Windows -> Comments  \n\nShow Console\n----------------------------------------\n**Description:** Open the integrated Rizin console. This will allow you to execute Rizin commands straight from Cutter.   \n\n**Steps:** Windows -> Console  \n\n**Shortcut:** :kbd:`:` or :kbd:`Ctrl` + :kbd:`\\``\n"
  },
  {
    "path": "docs/source/user-docs/menus/menu-bar.rst",
    "content": "Menu Bar\n========\n\nDepending on your desktop environment menu bar is located either at the top of the window\nor top of the screen.\n\n.. toctree::\n   :maxdepth: 2\n   :glob:\n\n   menu-bar/file-menu\n   menu-bar/edit-menu\n   menu-bar/view-menu\n   menu-bar/windows-menu\n   menu-bar/plugins-menu\n   menu-bar/debug-menu\n   menu-bar/debug-view-menu\n   menu-bar/help-menu\n   menu-bar/*\n\n \n"
  },
  {
    "path": "docs/source/user-docs/menus/registers-widget-context-menu.rst",
    "content": "Registers Widget Context Menu \n==============================\n\nCopy Register Value\n----------------------------------------\n**Description:** Copy the value of the selected register. *This option is only available on Debug or Emulation modes*.   \n\n**Steps:** Right-click a register in the widget and choose ``Copy register value``.  \n\nCopy Register Reference\n----------------------------------------\n**Description:** Copy the value of the data referenced from the selected register. *This option is only available on Debug or Emulation modes*.  \n\n**Steps:** Right-click a register in the widget and choose ``Copy register reference``."
  },
  {
    "path": "docs/source/user-docs/menus/set-table-layout.rst",
    "content": "Set Table Layout\n==============================\n\nSet Table Layout to Horizontal\n----------------------------------------\n**Description:** This option sets the layout of the current widget to Horizontal, in which each row contains one or more columns with relevant information (such as offset, size, name, etc). This is the default view in most of the widgets in Cutter. \n\n**Steps:** Right-click on a widget title and select ``Horizontal``.  \n\nSet Table Layout to Vertical\n----------------------------------------\n**Description:** This option sets the layout of the current widget to Vertical, in which the items are displayed in a tree-like view, where every item can be expanded to view more information.  \n\n**Steps:** Right-click on a widget title and select ``Vertical``"
  },
  {
    "path": "docs/source/user-docs/menus/stack-widget-context-menu.rst",
    "content": "Stack Widget Context Menu \n==============================\n\n\nEdit Stack Value\n----------------------------------------\n**Description:** Edit the value of the current stack item using a dialog. \n\n**Steps:** Right-click on an item in the Stack widget and choose ``Edit stack value...`` to open the edit dialog."
  },
  {
    "path": "docs/source/user-docs/menus/syncing-a-widget.rst",
    "content": "Syncing a Widget\n==============================\n\nSync Widget to an Offset\n----------------------------------------\n**Description:** By default, widgets like Disassembly, Graph, \nDecompiler and Hexdump are synchronized with each other, pointing to the same address. A change in one widget will affect the others. We consider this feature as a Global-Seek. By un-syncing a widget, the widget will be independent of the global seek and will have its own seek location. Navigating in an unsynced widget will not change the Global-Seek of the rest of the widgets, and vice versa - Changing the Global-Seek will not affect the unsynced widget. Multiple widgets can be unsynced independently.  \n\n**Steps:**  -> Sync/unsync offset"
  },
  {
    "path": "docs/source/user-docs/menus.rst",
    "content": "Menus\n======\n\nThis part of the user documentation will provide the reader with information about the different menus and context menu items they can find on Cutter. Each item has a description that explains about its actions, as well as how to reach this feature from the UI and a keyboard shortcut if one assigned to the feature.\n\n\n.. toctree::\n   :maxdepth: 3\n   :glob:\n\n   menus/*\n\n \n"
  },
  {
    "path": "docs/source/user-docs/preferences/analysis.rst",
    "content": "Analysis Options\n================\n\nCutter will use the underlying Rizin analysis options to analyze a binary. These options are usually \nconfigured when the binary is first loaded. However, they can be later changed using the Analysis \ndialog, and a new analysis that takes these options into account can then be performed.\n\nAnalysis Dialog\n---------------\n\n.. image:: ../../images/analysis_dialog.png\n    :alt: Analysis dialog\n\n**Description:** The Analysis dialog allows setting some Rizin's analysis options. The supported options are described\nbelow.\n\nClicking on the \"Analyze Program\" button will trigger an analysis of the current binary with Rizin's ``aaa``\ncommand, taking into account the configured values of the analysis options.\n\n**Steps to open:** ``Edit -> Preferences -> Analysis``\n\nSearch boundaries for analysis\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nSelect the boundaries in which the analyis will be performed. The different options are:\n\n- All executable maps (``io.maps.x``)\n- All maps (``io.maps``)\n- Current map (``io.map``)\n- Raw (``raw``)\n- Current mapped section (``bin.section``)\n- All mapped sections (``bin.sections``)\n\n**Configuration variable:** ``analysis.in``\n\nSpeculatively set a name for the functions\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nTry to name functions without symbols by using artifacts in the functions such as API calls and strings.\n\n**Configuration variable:** ``analysis.autoname``\n\n\nSearch for new functions following already defined functions\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nCutter will check if there is a candidate for a new function following an already defined one, as the compiler usually\nstate them together. This option is taking the advantages of both Recursive Analysis and Linear Sweep into some kind of a hybrid mode. For each discovered function, the analysis will try to check for a function-prologue, usually following a gap of null bytes or ``cc`` bytes. This will help with discovering functions which are not referenced in the program. As such, it will make the analysis slower and prone to false-positives.\n\n**Configuration variable:** ``analysis.hasnext``\n\n\nCreate references for unconditional jumps\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nWhen encountering unconditional jumps during the analysis, Cutter will add a code-reference even if it is not guaranteed\nthat the jump will take place.\n\n**Configuration variable:** ``analysis.jmp.ref``\n\n\nAnalyze jump tables in switch statements\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nWhen encountering switch statements during analysis, continue and analyze the target blocks of the jump tables.\n\n**Configuration variable:** ``analysis.jmp.tbl``\n\n\nAnalyze ``push`` + ``ret`` as ``jmp``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nWhen performing analysis of a function, treat the sequence of ``push`` followed by ``ret`` instruction as a ``jmp``.\nThis can help Cutter to continue the analysis to target of the ``jmp``.\n\n**Configuration variable:** ``analysis.pushret``\n\n\nShow verbose information when performing analysis\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nWhen enabled, Cutter will print warnings it encountered while preforming analysis. These warnings can help the user\nunderstand if anything went wrong in the analysis. This function is not only helpful when trying to perform a full\nanalysis of the program, but also when trying to analyze and define new functions.\n\n**Configuration variable:** ``analysis.verbose``\n\n\nVerbose output from type analysis\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nPrint warnings encountered while preforming type analysis. These warnings can help the user understand if anything went\nwrong in the analysis.\n\n**Configuration variable:** ``analysis.types.verbose``\n\nPointer depth\n~~~~~~~~~~~~~\nThe maximum number of nested pointers to follow in analysis.\n\n**Configuration variable:** ``analysis.ptrdepth``\n\nFunctions prelude\n~~~~~~~\nHex value that represents certain opcodes that will be used to identify functions.\n\n**Configuration variable:** ``analysis.prelude``\n"
  },
  {
    "path": "docs/source/user-docs/preferences/initialization-script.rst",
    "content": "Initialization Script\n===================================\n\nOn the launch of Cutter, it loads ``.cutterrc`` files from various locations if they are present. The directories from where ``.cutterrc`` files are loaded are all directories of type ``QStandardPaths::AppConfigLocation``. These locations vary according to OS. You can visit `here <https://doc.qt.io/qt-5/qstandardpaths.html>`__ to see all the locations.\n\nIf multiple ``.cutterrc`` scripts are present in different directories, all of them will be loaded. In case of conflicting or contradictory commands, the corresponding command in the script loaded last will override all the previous commands. Cutter has a GUI editor for Initialization Script whose description is given below. The script from the editor will be the last loaded initialization script and hence will override all the previous scripts in the event of conflicting commands.\n\nInitialization Script Editor\n-----------------------------------\n\n.. image:: ../../images/InitializationScriptEditor.png\n    :alt: Image of Initialization Script Editor\n\n\n**Description:** You can add new commands and modify existing commands here. To save the script, click on the ``Save`` button. If you want to see the changes you have made without restarting Cutter, you can use the ``Execute`` button. The hyperlink that shows the location of the script will open the directory containing the script on click.\n\n**Steps to open:** ``Edit -> Preferences -> Initialization Script``\n"
  },
  {
    "path": "docs/source/user-docs/preferences/layout.rst",
    "content": "Layout\n======\n\nThe set of currently opened widgets, their placement, and some properties is grouped into layouts.\nCutter will automatically restore the last layout state when reopening Cutter. The last debug and normal layouts are stored separately.\nYou can :ref:`save<user-docs/menus/menu-bar/view-menu:Save Layout>` multiple named layouts for different use cases.\nUse :doc:`../menus/menu-bar/view-menu` to :ref:`save<user-docs/menus/menu-bar/view-menu:Save Layout>`,\n:ref:`load<user-docs/menus/menu-bar/view-menu:Layouts>` or :ref:`user-docs/menus/menu-bar/view-menu:Reset to default layout`.\nA named layout is never automatically modified. To modify a previously saved layout, instead of entering a new name, select\nan existing layout from the list in Save Layout dialog.\n\nLayout Manager\n-----------------------------------\n\n.. image:: ../../images/layout_manager.png\n    :alt: Layout manager dialog\n\n\n**Description:** Layout manager allows renaming and deleting saved layouts.\n\n**Steps to open:** :doc:`View<../menus/menu-bar/view-menu>` -> :ref:`user-docs/menus/menu-bar/view-menu:Manage Layouts`\n"
  },
  {
    "path": "docs/source/user-docs/preferences.rst",
    "content": "Configuration\n=====================\nThis part of the documentation will provide the reader with information about different configuration options available.\nMost configuration is done using the Preferences dialog. It can be opened by clicking :ref:`Edit -> Preferences<user-docs/menus/menu-bar/edit-menu:Preferences>`.\n\n.. toctree::\n   :maxdepth: 1\n   :glob:\n\n   preferences/analysis\n   preferences/initialization-script\n   preferences/layout\n   preferences/*\n"
  },
  {
    "path": "docs/source/user-docs/shortcuts.rst",
    "content": "Shortcuts\n=========\n\nThis page regroups the common shortcuts available in Cutter.\n\nGlobal Shortcuts\n----------------\n\n+------------+---------------------+\n| Shortcut   | Function            |\n+============+=====================+\n| .          | Focus console input |\n+------------+---------------------+\n| G/S        | Focus search bar    |\n+------------+---------------------+\n| Ctrl/Cmd+R | Refresh contents    |\n+------------+---------------------+\n\nWidget Shortcuts\n----------------\n\n+-----------+---------+\n| Shortcut  | Widget  |\n+===========+=========+\n| Shift+F12 | Strings |\n+-----------+---------+\n| Shift+G   | Graph   |\n+-----------+---------+\n| Shift+I   | Imports |\n+-----------+---------+\n| Shift+E   | Exports |\n+-----------+---------+\n| Ctrl+`    | Console |\n+-----------+---------+\n| :         | Console |\n+-----------+---------+\n\nDisassembly View Shortcuts\n--------------------------\n*Most of these shortcuts are also applied to Disassembly Graph view*\n\n+-------------+----------------------------------+\n| Shortcut    | Function                         |\n+=============+==================================+\n| Esc         | Seek to the previous position    |\n+-------------+----------------------------------+\n| Space       | Switch to disassembly graph view |\n+-------------+----------------------------------+\n| Ctrl/Cmd+C  | Copy                             |\n+-------------+----------------------------------+\n| ;           | Add comment                      |\n+-------------+----------------------------------+\n| P           | Define a new function            |\n+-------------+----------------------------------+\n| Shift+P     | Edit function                    |\n+-------------+----------------------------------+\n| U           | Undefine a function              |\n+-------------+----------------------------------+\n| N           | Rename current function/flag     |\n+-------------+----------------------------------+\n| Shift+N     | Rename flag/function used here   |\n+-------------+----------------------------------+\n| Y           | Edit/rename local variables      |\n+-------------+----------------------------------+\n| A           | Set current address to String    |\n+-------------+----------------------------------+\n| C           | Set current address to Code      |\n+-------------+----------------------------------+\n| X           | Show Xrefs                       |\n+-------------+----------------------------------+\n| Ctrl/Cmd+\\+ | Zoom in                          |\n+-------------+----------------------------------+\n| Ctrl/Cmd+\\- | Zoom out                         |\n+-------------+----------------------------------+\n| Ctrl/Cmd+=  | Reset zoom                       |\n+-------------+----------------------------------+\n\nGraph View Shortcuts\n--------------------\n\n+---------------------+-----------------------------------+\n| Shortcut            | Function                          |\n+=====================+===================================+\n| Esc                 | Seek to previous position         |\n+---------------------+-----------------------------------+\n| Space               | Switch to disassembly view        |\n+---------------------+-----------------------------------+\n| Ctrl/Cmd+MouseWheel | Zoom                              |\n+---------------------+-----------------------------------+\n| \\+                  | Zoom in                           |\n+---------------------+-----------------------------------+\n| \\-                  | Zoom out                          |\n+---------------------+-----------------------------------+\n| =                   | Reset zoom                        |\n+---------------------+-----------------------------------+\n| J                   | Next instruction                  |\n+---------------------+-----------------------------------+\n| K                   | Previous instruction              |\n+---------------------+-----------------------------------+\n| T                   | Follow True/Unconditional branch  |\n+---------------------+-----------------------------------+\n| F                   | Follow False/Unconditional branch |\n+---------------------+-----------------------------------+\n\n\nDebug Shortcuts\n---------------\n\n+-----------------+------------------------------------------+\n| Shortcut        | Function                                 |\n+=================+==========================================+\n| F9              | Start debug                              |\n+-----------------+------------------------------------------+\n| F7              | Step into                                |\n+-----------------+------------------------------------------+\n| F8              | Step over                                |\n+-----------------+------------------------------------------+\n| F5              | Continue                                 |\n+-----------------+------------------------------------------+\n| F2/(Ctrl/Cmd)+B | Add or Remove breakpoint                 |\n+-----------------+------------------------------------------+\n| (Ctrl/Cmd)+F2   | Edit or open Advanced breakpoint dialog  |\n+-----------------+------------------------------------------+\n"
  },
  {
    "path": "docs/source/user-docs.rst",
    "content": "User Documentation\n==================\n\nThis page contains information about the different menus in Cutter.\n\nCutter is an advanced reverse engineering platform powered by Rizin.\nThis user's guide provides detailed information on how to use Cutter.\nThe documentation for users is still on its early stages and will be improved over time.\n\n\n.. toctree::\n   :maxdepth: 3\n   :titlesonly:\n   :glob:\n\n   user-docs/features\n   user-docs/shortcuts\n   user-docs/command-line\n   user-docs/menus\n   user-docs/preferences\n   user-docs/common-issues\n\n"
  },
  {
    "path": "scripts/Brewfile",
    "content": "brew \"p7zip\"\nbrew \"ccache\"\nbrew \"openssl\"\nbrew \"xz\"\nbrew \"llvm\"\nbrew \"meson\"\nbrew \"coreutils\"\n"
  },
  {
    "path": "scripts/_clang-format",
    "content": "# Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>\n#\n# You may use this file under the terms of the 3-clause BSD license.\n# See the file LICENSE from this package for details.\n\n# This is the clang-format configuration style to be used by Qt,\n# based on the rules from https://wiki.qt.io/Qt_Coding_Style and\n# https://wiki.qt.io/Coding_Conventions\n\n# Cutter specific modifications at the bottom. This file is used to generate\n# _clang_format at the root of repository using update_clang_format.sh\n\n---\n# Webkit style was loosely based on the Qt style\nBasedOnStyle: WebKit\n\nStandard: Cpp11\n\n# Column width is limited to 100 in accordance with Qt Coding Style.\n# https://wiki.qt.io/Qt_Coding_Style\n# Note that this may be changed at some point in the future.\nColumnLimit: 100\n# How much weight do extra characters after the line length limit have.\n# PenaltyExcessCharacter: 4\n\n# Disable reflow of qdoc comments: indentation rules are different.\n# Translation comments are also excluded.\nCommentPragmas: \"^!|^:\"\n\n# We want a space between the type and the star for pointer types.\nPointerBindsToType: false\n\n# We use template< without space.\nSpaceAfterTemplateKeyword: false\n\n# We want to break before the operators, but not before a '='.\nBreakBeforeBinaryOperators: NonAssignment\n\n# Braces are usually attached, but not after functions or class declarations.\nBreakBeforeBraces: Custom\nBraceWrapping:\n    AfterClass: true\n    AfterControlStatement: false\n    AfterEnum: false\n    AfterFunction: true\n    AfterNamespace: false\n    AfterObjCDeclaration: false\n    AfterStruct: true\n    AfterUnion: false\n    BeforeCatch: false\n    BeforeElse: false\n    IndentBraces: false\n\n# When constructor initializers do not fit on one line, put them each on a new line.\nConstructorInitializerAllOnOneLineOrOnePerLine: true\n# Indent initializers by 4 spaces\nConstructorInitializerIndentWidth: 4\n\n# Indent width for line continuations.\nContinuationIndentWidth: 8\n\n# No indentation for namespaces.\nNamespaceIndentation: None\n\n# Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125\nIndentPPDirectives: AfterHash\n\n# Horizontally align arguments after an open bracket.\n# The coding style does not specify the following, but this is what gives\n# results closest to the existing code.\nAlignAfterOpenBracket: true\nAlwaysBreakTemplateDeclarations: true\n\n# Ideally we should also allow less short function in a single line, but\n# clang-format does not handle that.\nAllowShortFunctionsOnASingleLine: Inline\n\n# The coding style specifies some include order categories, but also tells to\n# separate categories with an empty line. It does not specify the order within\n# the categories. Since the SortInclude feature of clang-format does not\n# re-order includes separated by empty lines, the feature is not used.\nSortIncludes: false\n\n# macros for which the opening brace stays attached.\nForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]\n\n# Break constructor initializers before the colon and after the commas.\nBreakConstructorInitializers: BeforeColon\n\n# Cutter additions to Qt's Clang file\n# ====================================\n\n# Align the assignment operators of consecutive lines\nAlignConsecutiveAssignments: false\n"
  },
  {
    "path": "scripts/appbundle_patch_qtwebengine.sh",
    "content": "#!/bin/bash\n\nif ! [[ $# -eq 1 ]]; then\n    echo \"Usage: $0 [AppBundle.app]\"\n    exit 1\nfi\n\nappbundle=$1\nqtwebegineprocess=\"$1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess\"\n\necho \"Patching $appbundle to fix QtWebEngine\"\n\nif ! [[ -f \"$qtwebegineprocess\" ]]; then\n\techo \"$qtwebegineprocess does not exist. Did you forget to run macdeployqt?\"\n\texit 1\nfi\n\ninstall_name_tool `otool -L \"$qtwebegineprocess\" | sed -n \"s/^[[:blank:]]*\\(\\/usr\\/local\\/Cellar[^[:blank:]]*\\(Qt[A-Za-z]*\\.framework[^[:blank:]]*\\)\\) (.*$/-change \\1 @executable_path\\/..\\/..\\/..\\/..\\/..\\/..\\/..\\/\\2/p\"` \\\n    $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/MacOS/QtWebEngineProcess \\\n    || exit 1\n\nmkdir -p $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks\nln -s ../../../../../../../QtCore.framework        $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks/\nln -s ../../../../../../../QtQuick.framework       $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks/\nln -s ../../../../../../../QtGui.framework         $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks/\nln -s ../../../../../../../QtQml.framework         $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks/\nln -s ../../../../../../../QtNetwork.framework     $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks/\nln -s ../../../../../../../QtWebChannel.framework  $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks/\nln -s ../../../../../../../QtPositioning.framework $1/Contents/Frameworks/QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Frameworks/ \n      \n      \n      \n      \n"
  },
  {
    "path": "scripts/appimage_embed_python.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\nif ! [[ $# -eq 2 ]]; then\n    echo \"Usage: $0 [appdir] [pyside_major]\"\n    exit 1\nfi\n\npython_prefix=$(pkg-config --variable=prefix python3)\n\npython_version=`$python_prefix/bin/python3 --version`\npython_version=${python_version##* }\npython_version=python${python_version%.*}\n\npyside_major=$2\nappdir=$1\n\necho \"Embedding Python from prefix $python_prefix in appdir $appdir\"\n\nmkdir -p \"$appdir/usr\"\ncd \"$appdir/usr/\" || exit 1\n\ncp -RT \"$python_prefix\" \".\" || exit 1\necho \"Cleaning up embedded Python\"\nfind . | grep -E \"(__pycache__|\\.pyc|\\.pyo$)\" | xargs rm -rf\nrm -r lib/$python_version/test lib/$python_version/idlelib lib/$python_version/curses lib/$python_version/lib2to3\n\necho \"Checking if PySide is available\"\n\npyside_prefix=$(pkg-config --variable=prefix pyside$pyside_major)\nif [ $? -ne 0 ]; then\n\techo \"PySide is not available, ignoring.\"\n\texit 0\nfi\n\necho \"PySide is at $pyside_prefix\"\n\nif [ \"$pyside_prefix\" == \"$python_prefix\" ]; then\n\techo \"Prefixes are equal, not copying anything from lib\"\nelse\n\tcp -RT \"$pyside_prefix/lib/$python_version\" \"lib/$python_version\" || exit 1\nfi\n"
  },
  {
    "path": "scripts/clang-format.py",
    "content": "#!/usr/bin/env python3\n#\n# SPDX-FileCopyrightText: 2021 Anton Kochkov <anton.kochkov@gmail.com>\n# SPDX-License-Identifier: LGPL-3.0-only\n\nimport argparse\nimport glob\nimport itertools\nimport subprocess\nimport sys\n\nfrom git import Repo\n\ndirlist = [\n    \"src\",\n]\n\nskiplist = [\n    \"translations\",\n]\n\npatterns = [\"*.cpp\", \"*.h\", \"*.hpp\"]\n\n\ndef should_scan(filename):\n    return any(directory in filename for directory in dirlist) and any(\n        pattern[1:] in filename for pattern in patterns\n    )\n\n\ndef skip(filename):\n    return any(skipfile in filename for skipfile in skiplist)\n\n\ndef get_matching_files():\n    for directory, pattern in itertools.product(dirlist, patterns):\n        for filename in glob.iglob(directory + \"/**/\" + pattern, recursive=True):\n            if not skip(filename):\n                yield filename\n\n\ndef get_edited_files(args):\n    repo = Repo()\n\n    for diff in repo.index.diff(args.diff):\n        filename = diff.a_path\n        if should_scan(filename) and not skip(filename):\n            yield filename\n\n\ndef build_command(clangformat, check, filenames, verbose):\n    cmd = [clangformat, \"--style=file\"]\n    if verbose:\n        cmd += [\"--verbose\"]\n    if check:\n        cmd += [\"--Werror\", \"--dry-run\"]\n    else:\n        cmd += [\"-i\"]\n    return cmd + filenames\n\n\ndef format_files(args, files):\n    if len(files) == 0:\n        print(\"No C files to format.\")\n        sys.exit(0)\n    cmd = build_command(args.clang_format, args.check, files, args.verbose)\n    r = subprocess.run(cmd, check=False)\n    sys.exit(r.returncode)\n\n\ndef get_file(args):\n    filename = args.file\n    if should_scan(filename) and not skip(filename):\n        return [filename]\n\n    return []\n\n\ndef get_files(args):\n    if args.diff:\n        return get_edited_files(args)\n\n    if args.file:\n        return get_file(args)\n\n    return get_matching_files()\n\n\ndef process(args):\n    files = get_files(args)\n    format_files(args, list(files))\n\n\ndef parse():\n    parser = argparse.ArgumentParser(description=\"Clang format the rizin project\")\n\n    parser.add_argument(\n        \"-C\", \"--clang-format\", default=\"clang-format\", help=\"path of clang-format\"\n    )\n    parser.add_argument(\n        \"-c\", \"--check\", action=\"store_true\", help=\"enable the check mode\"\n    )\n    parser.add_argument(\n        \"-v\", \"--verbose\", action=\"store_true\", help=\"use verbose output\"\n    )\n    parser.add_argument(\"-f\", \"--file\", help=\"formats (or checks) only the given file\")\n    parser.add_argument(\n        \"-d\",\n        \"--diff\",\n        type=str,\n        default=None,\n        help=\"format all modified file related to branch\",\n    )\n    return parser.parse_args()\n\n\ndef main():\n    args = parse()\n    process(args)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/deploy_docs.sh",
    "content": "#!/bin/bash\n\ncd $(dirname \"${BASH_SOURCE[0]}\")/..\n\ncutter_timestamp=$(git show -s --format=%ct)\ncutter_commit=\"$(git show -s --format=\"%H %s\")\"\n\necho \"Cloning current cutter.re\"\n\ngit clone --depth 1 git@github.com:rizinorg/cutter.re.git || exit 1\n\necho \"Updating docs\"\n\nrm -rf cutter.re/docs\ncp -a docs/build/html cutter.re/docs || exit 1\n\necho \"Committing new changes\"\n\ncd cutter.re || exit 1\ndocs_timestamp=$(git show -s --format=%ct)\nif [ $docs_timestamp -ge $cutter_timestamp ]; then\n\techo \"Last commit on cutter.re is newer than this commit on cutter. Skipping.\"\n\texit 0\nfi\n\ngit add . || exit 1\ngit diff --cached --quiet && echo \"No changes.\" && exit 0\nprintf \"Update docs from rizinorg/cutter\\n\\nOriginal Commit:\\n$cutter_commit\" | git commit -F -\ngit push origin master || exit 1\n\n"
  },
  {
    "path": "scripts/deploy_translations.sh",
    "content": "#!/bin/sh\n#########\n#### Push new translation files to cutter-translations\n#### so Crowdin can fetch them\n\nlog() {\n    echo \"[TRANSLATIONS] $1\"\n}\n\nlog \"Script started\"\n\n# Update submodule\nlog \"Updating translations...\"\ncd ${TRAVIS_BUILD_DIR}/src\ngit submodule update translations\ncd translations\ngit pull origin master\n\n# Generate Crowdin single translation file from cutter_fr.ts\nlog \"Generating single translation file\"\nfind . -iname \"*.ts\" | xargs lupdate .. -ts\ncp ./fr/cutter_fr_FR.ts ./Translations.ts\n\n# Push it so Crowdin can find new strings, and later push updated translations\nlog \"Committing...\"\ngit add Translations.ts\ngit commit -m \"Updated translations\"\nlog \"Pushing...\"\nexport GIT_SSH_COMMAND=\"/usr/bin/ssh -i $TRAVIS_BUILD_DIR/scripts/deploy_translations_rsa\"\ngit push \"git@github.com:rizinorg/cutter-translations.git\" HEAD:refs/heads/master\n\nlog \"Script done!\"\n"
  },
  {
    "path": "scripts/dump_syms.gyp",
    "content": "{\n    'includes': [\n        '../../../build/common.gypi',\n    ],\n    'targets': [\n        {\n            'target_name': 'dump_syms',\n            'type': 'executable',\n            'sources': [\n                'dump_syms.cc',\n            ],\n            'dependencies': [\n                '../../../common/windows/common_windows.gyp:common_windows_lib',\n            ],\n        },\n    ],\n}\n"
  },
  {
    "path": "scripts/dump_syms_macos.sh",
    "content": "#!/bin/bash\n\nif [ ! $# -eq 2 ]; then\n\techo \"usage: $0 [Cutter.app] [dst]\"\n\texit 1\nfi\n\nappbundle=\"$1\"\ndst=\"$2\"\nfiles=\"$appbundle/Contents/MacOS/Cutter.bin $appbundle/Contents/Frameworks/Python.framework/Python $(find \"$appbundle\" -type f -name '*.dylib')\"\n\nmkdir -p \"$dst\" || exit 1 \n\nfor file in $files; do\n\techo \"Dumping syms from $file\"\n\tdump_syms \"$file\" > \"$dst/tmp.sym\" || echo \"Failed to dump syms from $file\"\n\tinfoline=($(head -n1 \"$dst/tmp.sym\"))\n\tid=${infoline[3]}\n\tname=${infoline[4]}\n\tdir=\"$dst/$name/$id\"\n\tmkdir -p \"$dir\"\n\tmv \"$dst/tmp.sym\" \"$dir/$name.sym\"\ndone\n\necho \"Done\"\n\n"
  },
  {
    "path": "scripts/fetch_deps.sh",
    "content": "#!/bin/bash\n\nset -e\n\ncd $(dirname \"${BASH_SOURCE[0]}\")/..\nmkdir -p cutter-deps && cd cutter-deps\n\nDEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/v16\n\nif [ \"$CUTTER_QT\" == \"5\" ]; then\n\tDEPS_FILE_linux_x86_64=cutter-deps-q5-linux-x86_64.tar.gz\n\tDEPS_SHA256_linux_x86_64=ab3099fe699db100f2d00e1b70cdf77dec6b8fdd9cd1709c96e123a15fb62571\n\tDEPS_BASE_URL=https://github.com/rizinorg/cutter-deps/releases/download/qt5-v17\nelse\n\tDEPS_FILE_linux_x86_64=cutter-deps-linux-x86_64.tar.gz\n\tDEPS_SHA256_linux_x86_64=f63c5af2d9872bc6538a94c839d6ef6645c7630c42cff30f1d9da8eefd9eb040\nfi\necho $DEPS_SHA256_linux_x86_64\n\nDEPS_FILE_macos_x86_64=cutter-deps-macos-x86_64.tar.gz\nDEPS_SHA256_macos_x86_64=bcdc214e34dc3fd720327ad42e03fe3ec996ca28a9987e99898f149a65299a8c\n\nDEPS_FILE_macos_arm64=cutter-deps-macos-arm64.tar.gz\nDEPS_SHA256_macos_arm64=aa3f5ae91b93c5176d6bd4313af0888a2b6dcdaa2ef1750dd7e2f98156882e0f\n\nDEPS_FILE_win_x86_64=cutter-deps-win-x86_64.tar.gz\nDEPS_SHA256_win_x86_64=710e40cf8329205d09535cc56a9fb155a56ff1a1ca112145864382fb3d4e8160\n\n\nARCH=x86_64\nif [ \"$OS\" == \"Windows_NT\" ]; then\n\tPLATFORM=win\nelse\n\tUNAME_S=\"$(uname -s)\"\n\tif [ \"$UNAME_S\" == \"Linux\" ]; then\n\t\tPLATFORM=linux\n\telif [ \"$UNAME_S\" == \"Darwin\" ]; then\n\t\tPLATFORM=macos\n\t\tARCH=$(uname -m)\n\telse\n\t\techo \"Unsupported Platform: uname -s => $UNAME_S, \\$OS => $OS\"\n\t\texit 1\n\tfi\nfi\n\nDEPS_FILE=DEPS_FILE_${PLATFORM}_${ARCH}\nDEPS_FILE=${!DEPS_FILE}\nDEPS_SHA256=DEPS_SHA256_${PLATFORM}_${ARCH}\nDEPS_SHA256=${!DEPS_SHA256}\nDEPS_URL=${DEPS_BASE_URL}/${DEPS_FILE}\n\nSHA256SUM=sha256sum\nif ! command -v ${SHA256SUM} &> /dev/null; then\n\tSHA256SUM=\"shasum -a 256\"\nfi\n\ncurl -L \"$DEPS_URL\" -o \"$DEPS_FILE\" || exit 1\necho \"$DEPS_SHA256  $DEPS_FILE\" | ${SHA256SUM} -c - || exit 1\n\ntar -xf \"$DEPS_FILE\" || exit 1\n\nif [ -f relocate.sh ]; then\n\t./relocate.sh || exit 1\nfi\n\n"
  },
  {
    "path": "scripts/get_version.py",
    "content": "\nimport os\nimport re\n\nscripts_path = os.path.dirname(os.path.realpath(__file__))\npro_file_path = os.path.join(scripts_path, \"..\", \"CMakeLists.txt\")\n\nwith open(pro_file_path, \"r\") as f:\n    pro_content = f.read()\n\ndef version_var_re(name):\n    return \"^set\\({}[ \\t]*(\\d+)\\)$\".format(name)\n\nm = re.search(version_var_re(\"CUTTER_VERSION_MAJOR\"), pro_content, flags=re.MULTILINE)\nversion_major = int(m.group(1)) if m is not None else 0\n\nm = re.search(version_var_re(\"CUTTER_VERSION_MINOR\"), pro_content, flags=re.MULTILINE)\nversion_minor = int(m.group(1)) if m is not None else 0\n\nm = re.search(version_var_re(\"CUTTER_VERSION_PATCH\"), pro_content, flags=re.MULTILINE)\nversion_patch = int(m.group(1)) if m is not None else 0\n\nprint(\"{}.{}.{}\".format(version_major, version_minor, version_patch))\n"
  },
  {
    "path": "scripts/jsdec.sh",
    "content": "#!/bin/bash\n\nset -e\nINSTALL_PREFIX=\"$1\"\nEXTRA_CMAKE_OPTS=\"$2\"\n\nSCRIPTPATH=$(realpath \"$(dirname \"${BASH_SOURCE[0]}\")\")\n\ncd \"$SCRIPTPATH/..\"\n\nif [ ! -d jsdec ]; then\n\tgit clone https://github.com/rizinorg/jsdec.git --depth 1 --branch \"dev\"\nfi\n\ncd jsdec\nif [ -d build_lib ]; then\n\trm -rf build_lib\nfi\nmeson setup --buildtype=release --pkg-config-path=\"$INSTALL_PREFIX/lib/pkgconfig\" -Dbuild_type=cutter build_lib\nninja -C build_lib\n\nmkdir -p build_plugin && cd build_plugin\ncmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DJSDEC_BUILD_DIR=\"../build_lib\" -DCMAKE_INSTALL_PREFIX=\"$INSTALL_PREFIX\" $EXTRA_CMAKE_OPTS ../cutter-plugin\nninja install\n"
  },
  {
    "path": "scripts/macos_sign.sh",
    "content": "#!/bin/bash\n#\n# Script to be run manually on a maintainer's machine to re-sign\n# and notarize Cutter dmgs created by the CI with Developer ID.\n#\n# https://developer.apple.com/forums/thread/701514\n\nset -e\nshopt -s extglob\n\nusage() {\n\techo \"macos_sign.sh [command] ...\"\n\techo \"\"\n\techo \"Commands:\"\n\techo \"  sign_bundle [Cutter.app]\t Sign the given bundle in-place\"\n\techo \"  notarize_bundle [Cutter.app] Notarize the given bundle after it has been signed and staple the ticket\"\n\techo \"  resign_dmg [Cutter.dmg]\t  Sign and notarize the Cutter.app in the given dmg into a new dmg\"\n\texit 1\n}\n\n\nTARGET=Cutter.app\nIDENT=\"Developer ID Application: Florian Märkl (7C89959B9X)\"\nENTITLEMENTS=\"$(dirname \"${BASH_SOURCE[0]}\")/../dist/macos/Entitlements.plist\"\n\nee() {\n\techo \"$@\"\n\t\"$@\"\n}\n\necho_step() {\n\techo \"\"\n\tprintf \"\\033[0;32m----------- $@ -----------\\033[0m\\n\"\n}\n\nsign_files () {\n\tlocal ARGS=\"$1\"\n\tshift 1\n\tfor f in $@; do\n\t\tee codesign -s \"$IDENT\" $ARGS --timestamp  -f \"$f\"\n\tdone\n}\n\nsign_bundle() {\n\t# Sign \"from the inside out\" with Developer ID\n\t# Libs need only a signature\n\t# Executables need -o runtime for notarization\n\t# Debugging executables also need entitlements\n\n\tlocal TARGET=$1\n\techo_step \"Signing ${TARGET}\"\n\n\tlocal PYTHON_PREFIX=${TARGET}/Contents/Frameworks/Python.framework/Versions/*.*\n\tsign_files \"\" \\\n\t\t${PYTHON_PREFIX}/lib/**/**/*.so \\\n\t\t${PYTHON_PREFIX}/lib/*.dylib \\\n\t\t${PYTHON_PREFIX}/lib/python*/site-packages/PySide6/*.so \\\n\t\t${PYTHON_PREFIX}/lib/python*/site-packages/shiboken6/*.so\n\tsign_files \"-o runtime\" \\\n\t\t${TARGET}/Contents/Resources/bin/!(rizin) \\\n\t\t${PYTHON_PREFIX}/bin/python*.!(*-config)\n\n\tsign_files \"\" \\\n\t\t${TARGET}/Contents/Frameworks/*.framework \\\n\t\t${TARGET}/Contents/Frameworks/*.dylib \\\n\t\t${TARGET}/Contents/PlugIns/**/*.dylib \\\n\t\t${TARGET}/Contents/Resources/lib/*.dylib \\\n\t\t${TARGET}/Contents/Resources/lib/rizin/plugins/*.dylib \\\n\t\t${TARGET}/Contents/Resources/plugins/native/*.so \\\n\n\tsign_files \"-o runtime\" \\\n\t\t${TARGET}/Contents/Resources/bin/!(rizin)\n\n\tsign_files \"--entitlements ${ENTITLEMENTS} -o runtime\" \\\n\t\t${TARGET}/Contents/Resources/bin/rizin \\\n\t\t${TARGET}\n}\n\nnotarize_bundle() {\n\t# Save credentials with:\n\t# xcrun notarytool store-credentials --apple-id 'apple@rizin.re' --team-id 7C89959B9X notarytool-7C89959B9X\n\tlocal AUTH=\"--keychain-profile notarytool-7C89959B9X\"\n\tlocal TARGET=$1\n\n\techo_step \"Notarizing ${TARGET}\"\n\n\tee ditto -c -k --keepParent ${TARGET} Cutter-notarize-submit.zip\n\tee xcrun notarytool submit --wait --timeout 30m ${AUTH} Cutter-notarize-submit.zip\n\t# TODO: if possible, fail the script here if the notarization failed. Unfortunately notarytool does not\n\t# return a non-zero exit code by default on failure, so it does not work automatically yet.\n\t# However, the staple below will still fail if the submission was not notarized, so we still detect it.\n\tee xcrun stapler staple ${TARGET}\n}\n\nresign_dmg() {\n\tlocal TARGET=\"$1\"\n\techo_step \"Mounting temporary rw variant of ${TARGET} to Cutter-rw/\"\n\trm -f Cutter-rw.dmg\n\tee hdiutil convert -format UDRW -o Cutter-rw.dmg \"${TARGET}\"\n\tmkdir -p Cutter-rw\n\tee hdiutil attach Cutter-rw.dmg -mount required -mountpoint Cutter-rw\n\tunmount() {\n\t\tee hdiutil detach Cutter-rw\n\t}\n\ttrap unmount EXIT\n\tsign_bundle Cutter-rw/Cutter.app\n\tnotarize_bundle Cutter-rw/Cutter.app\n\tunmount\n\ttrap - EXIT\n\tOUTPUT=\"${1%.*}-signed.dmg\"\n\techo_step \"Creating final read-only ${OUTPUT}\"\n\tee hdiutil convert -format UDZO -o \"${OUTPUT}\" Cutter-rw.dmg\n}\n\ncase \"$1\" in\n\tsign_bundle)\n\t\tif [ \"$#\" -ne 2 ]; then\n\t\t\tusage\n\t\tfi\n\t\tsign_bundle \"$2\"\n\t;;\n\tnotarize_bundle)\n\t\tif [ \"$#\" -ne 2 ]; then\n\t\t\tusage\n\t\tfi\n\t\tnotarize_bundle \"$2\"\n\t;;\n\tresign_dmg)\n\t\tif [ \"$#\" -ne 2 ]; then\n\t\t\tusage\n\t\tfi\n\t\tresign_dmg \"$2\"\n\t;;\n\t*)\n\t\tusage\n\t;;\nesac\n\n"
  },
  {
    "path": "scripts/prepare_package_ui_macos.sh",
    "content": "#!/bin/bash\n\nif ! [[ $# -eq 3 ]]; then\n    echo \"Usage: $0 [App name] [background.png] [Volume name]\"\n    exit 1\nfi\n\nAPP_NAME=$1\nDMG_BACKGROUND_IMG=$2\nVOL_NAME=$3\n\nif [[ ! -f ${DMG_BACKGROUND_IMG} ]]; then\n    echo \"background not found\"\n    exit 1\nfi\n\necho \"Adding link to '/Applications'...\"\npushd /Volumes/\"${VOL_NAME}\" || exit 1\nln -s /Applications \" \" || exit 1\npopd || exit 1\n\necho \"Package background set up...\"\nmkdir /Volumes/\"${VOL_NAME}\"/.background || exit 1\n\ncp \"${DMG_BACKGROUND_IMG}\" /Volumes/\"${VOL_NAME}\"/.background/ || exit 1\n\nDMG_BACKGROUND_IMG_BASENAME=$(basename \"${DMG_BACKGROUND_IMG}\")\n\n# tell the Finder to resize the window, set the background,\n#  change the icon size, place the icons in the right position, etc.\necho '\n   tell application \"Finder\"\n     tell disk \"'${VOL_NAME}'\"\n           open\n           set current view of container window to icon view\n           set toolbar visible of container window to false\n           set statusbar visible of container window to false\n           set the bounds of container window to {400, 100, 925, 508}\n           set viewOptions to the icon view options of container window\n           set arrangement of viewOptions to not arranged\n           set icon size of viewOptions to 122\n           set background picture of viewOptions to file \".background:'${DMG_BACKGROUND_IMG_BASENAME}'\"\n           set position of item \"'${APP_NAME}'.app\" of container window to {117, 193}\n           set position of item \" \" of container window to {411, 193}\n           set position of item \".background\" of container window to {1000, 1000}\n           set position of item \".fseventsd\" of container window to {1010, 1010}\n           close\n           open\n           update without registering applications\n           delay 20\n     end tell\n   end tell\n' | osascript || echo \"WARNING: Script for .dmg background failed!!!\"\n\necho \"Syncing...\"\nsync || exit 1\n"
  },
  {
    "path": "scripts/prepare_python_macos.sh",
    "content": "#!/bin/bash\n\nSCRIPTPATH=$(realpath \"$(dirname \"${BASH_SOURCE[0]}\")\")\n\nmkdir python && cd python\n\nwget \"https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tar.xz\" || exit 1\ntar -xf Python-3.9.1.tar.xz || exit 1\n\nexport PYTHON_FRAMEWORK_DIR=\"`pwd`/framework\"\n\ncd Python-3.9.1 || exit 1\n\nCPPFLAGS=\"-I$(brew --prefix openssl)/include\" LDFLAGS=\"-L$(brew --prefix openssl)/lib\" ./configure --enable-framework=$PYTHON_FRAMEWORK_DIR || exit 1\n\n# Patch for https://github.com/rizinorg/cutter/issues/424\nsed -i \".original\" \"s/#define HAVE_GETENTROPY 1/#define HAVE_GETENTROPY 0/\" pyconfig.h\n\nmake -j4 || exit 1\nmake frameworkinstallframework > /dev/null || exit 1\n\nPYTHONHOME=$PYTHON_FRAMEWORK_DIR/Python.framework/Versions/Current \\\n       $PYTHON_FRAMEWORK_DIR/Python.framework/Versions/Current/bin/pip3 install -r \"$SCRIPTPATH/pip_requirements.txt\" || exit 1\n\ncd ../..\n"
  },
  {
    "path": "scripts/prepare_rizin.bat",
    "content": "@ECHO OFF\r\nSETLOCAL ENABLEDELAYEDEXPANSION\r\n\r\nIF \"%VisualStudioVersion%\" == \"14.0\" ( IF NOT DEFINED Platform SET \"Platform=X86\" )\r\nFOR /F %%i IN ('powershell -c \"\\\"%Platform%\\\".toLower()\"') DO SET PLATFORM=%%i\r\npowershell -c \"if ('%PLATFORM%' -notin ('x86', 'x64')) {Exit 1}\"\r\nIF !ERRORLEVEL! NEQ 0 (\r\n    ECHO Unknown platform: %PLATFORM%\r\n    EXIT /B 1\r\n)\r\n\r\nSET \"PATH=%CD%;%PATH%\"\r\nSET \"RZDIST=rz_dist\"\r\n\r\nECHO Building Rizin (%PLATFORM%)\r\nCD rizin\r\ngit clean -xfd\r\nRMDIR /S /Q ..\\%RZDIST%\r\nmeson.exe rz_builddir --buildtype=release --prefix=%CD%\\..\\%RZDIST% || EXIT /B 1\r\nninja -C rz_builddir install || EXIT /B 1\r\nIF !ERRORLEVEL! NEQ 0 EXIT /B 1\r\n"
  },
  {
    "path": "scripts/rz-libswift.sh",
    "content": "#!/bin/bash\n\nset -e\n\nSCRIPTPATH=$(realpath \"$(dirname \"${BASH_SOURCE[0]}\")\")\n\ncd \"$SCRIPTPATH/..\"\n\nif [[ ! -d libswift ]]; then\n\tgit clone https://github.com/rizinorg/rz-libswift.git --depth 1 libswift\nfi\n\ncd libswift\nrm -rf build || sleep 0\nmkdir build && cd build\nmeson --buildtype=release \"$@\" ..\nninja\nninja install\n\n"
  },
  {
    "path": "scripts/rz-libyara.sh",
    "content": "#!/bin/bash\nset -e\n\nSCRIPTPATH=$(realpath \"$(dirname \"${BASH_SOURCE[0]}\")\")\nINSTALL_PREFIX=\"$1\"\nEXTRA_CMAKE_OPTS=\"$2\"\n\ncd \"$SCRIPTPATH/..\"\n\nif [[ ! -d rz_libyara ]]; then\n    git clone https://github.com/rizinorg/rz-libyara.git --depth 1 --branch main rz_libyara\n    git -C rz_libyara submodule init\n    git -C rz_libyara submodule update\nfi\n\ncd rz_libyara\n\nmeson --buildtype=release --pkg-config-path=\"$INSTALL_PREFIX/lib/pkgconfig\" --prefix=\"$INSTALL_PREFIX\" -Denable_openssl=false -Duse_sys_yara=disabled build\nninja -C build install\n\ncd cutter-plugin\nmkdir -p build && cd build\ncmake -G Ninja -DRIZIN_INSTALL_PLUGDIR=\"../build\" -DCMAKE_INSTALL_PREFIX=\"$INSTALL_PREFIX\" $EXTRA_CMAKE_OPTS ..\nninja\nninja install\n"
  },
  {
    "path": "scripts/rz-silhouette.sh",
    "content": "#!/bin/bash\nset -e\n\nSCRIPTPATH=$(realpath \"$(dirname \"${BASH_SOURCE[0]}\")\")\nINSTALL_PREFIX=\"$1\"\nEXTRA_CMAKE_OPTS=\"$2\"\n\ncd \"$SCRIPTPATH/..\"\n\nif [[ ! -d rz-silhouette ]]; then\n\tgit clone https://github.com/rizinorg/rz-silhouette.git --depth 1 rz-silhouette\nfi\n\ncd rz-silhouette\n\nmeson --buildtype=release --pkg-config-path=\"$INSTALL_PREFIX/lib/pkgconfig\" --prefix=\"$INSTALL_PREFIX\" build\nninja -C build install\n"
  },
  {
    "path": "scripts/tarball.sh",
    "content": "#!/bin/bash\n\nNAME=${1:-Cutter}\n\nset -xe\ncd $(dirname \"${BASH_SOURCE[0]}\")/..\n\nshopt -s extglob\nshopt -s dotglob\nmkdir \"${NAME}\"\ncp -r !(${NAME}) \"${NAME}\"\n\npushd \"${NAME}\"\ngit clean -dxff .\ngit submodule update --init --recursive\n\npushd rizin\ngit clean -dxff .\n# Possible option: pre-download all subproject, however this makes the tarball huge.\n# As opposed to meson dist used for rizin tarballs, this will not just download the ones\n# used in a default build, but all of them, including multiple capstone variants.\n# meson subprojects download\npopd\n\npushd src/translations\ngit clean -dxff .\npopd\n\nfind . -name \".git*\" | xargs rm -rfv\npopd\n\ntar -czvf \"${NAME}-src.tar.gz\" \"${NAME}\"\n"
  },
  {
    "path": "scripts/update_clang_format.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\ncd $(dirname \"${BASH_SOURCE[0]}\")\n\n# Do not replace with newer without dicussing! Intentionally using older clang-format-version.\ntool=clang-format-8\n# Using full config dumped with older clang format should produce more consistent result when some \n# people have slightly newer clang-format. 8 chosen because it is newest version available in Ubuntu 16.04 official repository.\noutput_file=../_clang-format\n\n\n# Usage:\n# modify scripts/_clang_format\n# run this script\n# compare the generated outptu with previous version\n\ntemp_file=_clang_format_new\necho -e \"# Do not edit this file! Automatically generated using scripts/udate_clang_format.sh and scripts/_clang_format\\n# See update_clang_format.sh for more information.\" > $temp_file\necho \"# generated using `$tool --version`\" >> $temp_file\n$tool -style=file -dump-config >> $temp_file\nmv $temp_file $output_file\n\necho Done\n"
  },
  {
    "path": "scripts/update_deps.py",
    "content": "\nimport sys\nimport os\nimport requests\nimport re\nimport subprocess\n\nplatforms = [\"linux\", \"macos\", \"win\"]\n\nif sys.platform == \"darwin\":\n\tmd5sum_cmd = [\"md5\", \"-r\"]\nelse:\n\tmd5sum_cmd = [\"md5sum\"]\n\nfetch_deps_path = os.path.join(os.path.dirname(sys.argv[0]), \"fetch_deps.sh\")\n\nprint(\"Fetching latest release\")\njson = requests.get(\"https://api.github.com/repos/rizinorg/cutter-deps/releases/latest\").json()\n\nrelease_url = json[\"assets\"][0][\"browser_download_url\"]\nfor platform in platforms:\n\trelease_url = release_url.replace(platform, \"${PLATFORM}\")\n\nwith open(fetch_deps_path) as f:\n\tfetch_deps = f.read()\n\nmd5 = {}\nfor platform in platforms:\n\tplatform_url = release_url.replace(\"${PLATFORM}\", platform)\n\tprint(f\"Getting MD5 for {platform_url}\")\n\n\tcurl = subprocess.Popen([\"curl\", \"-fL\", platform_url], stdout=subprocess.PIPE)\n\tmd5sum = subprocess.run(md5sum_cmd, stdin=curl.stdout, capture_output=True, encoding=\"utf-8\").stdout\n\tcurl.wait()\n\tif curl.returncode != 0:\n\t\tprint(f\"Failed to download {platform_url}, skipping.\")\n\t\tcontinue\n\n\tmd5sum = re.fullmatch(\"([a-zA-Z0-9]+)( +-)?\\n?\", md5sum).group(1)\n\n\tprint(f\"MD5: {md5sum}\")\n\tfetch_deps = re.sub(f\"^{platform.upper()}_URL=.*$\", f\"{platform.upper()}_URL={platform_url}\".replace(\"\\\\\", r\"\\\\\"), fetch_deps, flags=re.MULTILINE)\n\tfetch_deps = re.sub(f\"^{platform.upper()}_MD5=.*$\", f\"{platform.upper()}_MD5={md5sum}\".replace(\"\\\\\", r\"\\\\\"), fetch_deps, flags=re.MULTILINE)\n\nwith open(fetch_deps_path, \"w\") as f:\n\tf.write(fetch_deps)\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "configure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/CutterConfig.h.in\"\n               \"${CMAKE_CURRENT_BINARY_DIR}/CutterConfig.h\")\n\nset(SOURCES\n    Main.cpp\n    core/Cutter.cpp\n    core/CutterJson.cpp\n    core/RizinCpp.cpp\n    core/Basefind.cpp\n    dialogs/EditStringDialog.cpp\n    dialogs/WriteCommandsDialogs.cpp\n    widgets/DisassemblerGraphView.cpp\n    widgets/OverviewView.cpp\n    common/DisassemblyPreview.cpp\n    common/RichTextPainter.cpp\n    dialogs/InitialOptionsDialog.cpp\n    dialogs/AboutDialog.cpp\n    dialogs/CommentsDialog.cpp\n    dialogs/EditInstructionDialog.cpp\n    dialogs/FlagDialog.cpp\n    dialogs/GlobalVariableDialog.cpp\n    dialogs/RemoteDebugDialog.cpp\n    dialogs/NativeDebugDialog.cpp\n    dialogs/XrefsDialog.cpp\n    core/MainWindow.cpp\n    common/Helpers.cpp\n    common/Highlighter.cpp\n    common/MdHighlighter.cpp\n    common/DirectionalComboBox.cpp\n    dialogs/preferences/AsmOptionsWidget.cpp\n    dialogs/NewFileDialog.cpp\n    common/AnalysisTask.cpp\n    widgets/CommentsWidget.cpp\n    widgets/ConsoleWidget.cpp\n    widgets/Dashboard.cpp\n    widgets/EntrypointWidget.cpp\n    widgets/ExportsWidget.cpp\n    widgets/FlagsWidget.cpp\n    widgets/FunctionsWidget.cpp\n    widgets/GlobalsWidget.cpp\n    widgets/ImportsWidget.cpp\n    widgets/Omnibar.cpp\n    widgets/RelocsWidget.cpp\n    widgets/SectionsWidget.cpp\n    widgets/SegmentsWidget.cpp\n    widgets/StringsWidget.cpp\n    widgets/SymbolsWidget.cpp\n    menus/DisassemblyContextMenu.cpp\n    menus/DecompilerContextMenu.cpp\n    menus/FlirtContextMenu.cpp\n    widgets/DisassemblyWidget.cpp\n    widgets/HexdumpWidget.cpp\n    common/Configuration.cpp\n    common/Colors.cpp\n    common/TempConfig.cpp\n    common/SvgIconEngine.cpp\n    common/SyntaxHighlighter.cpp\n    widgets/DecompilerWidget.cpp\n    widgets/VisualNavbar.cpp\n    widgets/GraphView.cpp\n    dialogs/preferences/PreferencesDialog.cpp\n    dialogs/preferences/AppearanceOptionsWidget.cpp\n    dialogs/preferences/GraphOptionsWidget.cpp\n    dialogs/preferences/PreferenceCategory.cpp\n    dialogs/preferences/InitializationFileEditor.cpp\n    widgets/QuickFilterView.cpp\n    widgets/ClassesWidget.cpp\n    widgets/ResourcesWidget.cpp\n    widgets/VTablesWidget.cpp\n    widgets/TypesWidget.cpp\n    widgets/HeadersWidget.cpp\n    widgets/SearchWidget.cpp\n    CutterApplication.cpp\n    dialogs/RizinPluginsDialog.cpp\n    widgets/CutterDockWidget.cpp\n    widgets/CutterTreeWidget.cpp\n    widgets/GraphWidget.cpp\n    widgets/OverviewWidget.cpp\n    common/JsonModel.cpp\n    dialogs/VersionInfoDialog.cpp\n    widgets/FlirtWidget.cpp\n    common/AsyncTask.cpp\n    dialogs/AsyncTaskDialog.cpp\n    widgets/StackWidget.cpp\n    widgets/RegistersWidget.cpp\n    widgets/ThreadsWidget.cpp\n    widgets/ProcessesWidget.cpp\n    widgets/BacktraceWidget.cpp\n    dialogs/MapFileDialog.cpp\n    common/CommandTask.cpp\n    common/ProgressIndicator.cpp\n    common/RizinTask.cpp\n    dialogs/RizinTaskDialog.cpp\n    widgets/DebugActions.cpp\n    widgets/MemoryMapWidget.cpp\n    dialogs/preferences/DebugOptionsWidget.cpp\n    dialogs/preferences/PluginsOptionsWidget.cpp\n    widgets/BreakpointWidget.cpp\n    dialogs/BreakpointsDialog.cpp\n    dialogs/AttachProcDialog.cpp\n    widgets/RegisterRefsWidget.cpp\n    dialogs/SetToDataDialog.cpp\n    dialogs/EditVariablesDialog.cpp\n    dialogs/EditFunctionDialog.cpp\n    widgets/CutterTreeView.cpp\n    widgets/ComboQuickFilterView.cpp\n    dialogs/HexdumpRangeDialog.cpp\n    common/CutterSeekable.cpp\n    common/RefreshDeferrer.cpp\n    dialogs/WelcomeDialog.cpp\n    common/RunScriptTask.cpp\n    dialogs/EditMethodDialog.cpp\n    dialogs/TypesInteractionDialog.cpp\n    dialogs/TypesVariablesDialog.cpp\n    widgets/SdbWidget.cpp\n    plugins/PluginManager.cpp\n    common/BasicBlockHighlighter.cpp\n    common/BasicInstructionHighlighter.cpp\n    widgets/ColorPicker.cpp\n    common/ColorThemeWorker.cpp\n    widgets/ColorThemeComboBox.cpp\n    widgets/ColorThemeListView.cpp\n    dialogs/preferences/ColorThemeEditDialog.cpp\n    common/UpdateWorker.cpp\n    widgets/MemoryDockWidget.cpp\n    common/BugReporting.cpp\n    common/HighDpiPixmap.cpp\n    widgets/GraphGridLayout.cpp\n    widgets/HexWidget.cpp\n    common/SelectionHighlight.cpp\n    common/Decompiler.cpp\n    menus/AddressableItemContextMenu.cpp\n    common/AddressableItemModel.cpp\n    widgets/ListDockWidget.cpp\n    dialogs/MultitypeFileSaveDialog.cpp\n    widgets/BoolToggleDelegate.cpp\n    common/IOModesController.cpp\n    common/SettingsUpgrade.cpp\n    dialogs/LayoutManager.cpp\n    common/CutterLayout.cpp\n    widgets/GraphHorizontalAdapter.cpp\n    common/ResourcePaths.cpp\n    widgets/CutterGraphView.cpp\n    widgets/SimpleTextGraphView.cpp\n    widgets/RizinGraphWidget.cpp\n    widgets/CallGraph.cpp\n    widgets/AddressableDockWidget.cpp\n    dialogs/preferences/AnalysisOptionsWidget.cpp\n    common/DecompilerHighlighter.cpp\n    dialogs/GlibcHeapInfoDialog.cpp\n    widgets/HeapDockWidget.cpp\n    widgets/GlibcHeapWidget.cpp\n    dialogs/GlibcHeapBinsDialog.cpp\n    widgets/HeapBinsGraphView.cpp\n    dialogs/ArenaInfoDialog.cpp\n    tools/basefind/BaseFindDialog.cpp\n    tools/basefind/BaseFindSearchDialog.cpp\n    tools/basefind/BaseFindResultsDialog.cpp\n    widgets/AddressRangeScrollBar.cpp\n    dialogs/preferences/ShortcutOptionsWidget.cpp\n    shortcuts/ShortcutManager.cpp\n    shortcuts/DefaultShortcuts.cpp\n    dialogs/MarkDialog.cpp\n    dialogs/ProfileDirectivesDialog.cpp\n    dialogs/RegisterProfileDialog.cpp\n    dialogs/EditRegProfileDialog.cpp\n    common/DisassemblyHelper.cpp\n    widgets/SearchBarWidget.cpp\n    widgets/SearchableDockWidget.cpp\n    common/CutterSearchable.cpp\n    widgets/SearchableTextEdit.cpp\n)\nset(HEADER_FILES\n    core/Cutter.h\n    core/CutterCommon.h\n    core/CutterDescriptions.h\n    core/CutterJson.h\n    core/RizinCpp.h\n    core/Basefind.h\n    dialogs/EditStringDialog.h\n    dialogs/WriteCommandsDialogs.h\n    widgets/DisassemblerGraphView.h\n    widgets/OverviewView.h\n    common/RichTextPainter.h\n    common/CachedFontMetrics.h\n    dialogs/AboutDialog.h\n    dialogs/preferences/AsmOptionsWidget.h\n    dialogs/CommentsDialog.h\n    dialogs/EditInstructionDialog.h\n    dialogs/FlagDialog.h\n    dialogs/GlobalVariableDialog.h\n    dialogs/RemoteDebugDialog.h\n    dialogs/NativeDebugDialog.h\n    dialogs/XrefsDialog.h\n    common/Helpers.h\n    core/MainWindow.h\n    common/Highlighter.h\n    common/MdHighlighter.h\n    common/DirectionalComboBox.h\n    dialogs/InitialOptionsDialog.h\n    dialogs/NewFileDialog.h\n    common/AnalysisTask.h\n    widgets/CommentsWidget.h\n    widgets/ConsoleWidget.h\n    widgets/Dashboard.h\n    widgets/EntrypointWidget.h\n    widgets/ExportsWidget.h\n    widgets/FlagsWidget.h\n    widgets/FunctionsWidget.h\n    widgets/ImportsWidget.h\n    widgets/Omnibar.h\n    widgets/RelocsWidget.h\n    widgets/SectionsWidget.h\n    widgets/SegmentsWidget.h\n    widgets/StringsWidget.h\n    widgets/SymbolsWidget.h\n    menus/DisassemblyContextMenu.h\n    menus/DecompilerContextMenu.h\n    menus/FlirtContextMenu.h\n    widgets/DisassemblyWidget.h\n    widgets/HexdumpWidget.h\n    common/Configuration.h\n    common/Colors.h\n    common/TempConfig.h\n    common/SvgIconEngine.h\n    common/SyntaxHighlighter.h\n    widgets/DecompilerWidget.h\n    widgets/VisualNavbar.h\n    widgets/GraphView.h\n    dialogs/preferences/PreferencesDialog.h\n    dialogs/preferences/AppearanceOptionsWidget.h\n    dialogs/preferences/PreferenceCategory.h\n    dialogs/preferences/GraphOptionsWidget.h\n    dialogs/preferences/InitializationFileEditor.h\n    widgets/QuickFilterView.h\n    widgets/ClassesWidget.h\n    widgets/ResourcesWidget.h\n    CutterApplication.h\n    widgets/VTablesWidget.h\n    widgets/TypesWidget.h\n    widgets/HeadersWidget.h\n    widgets/SearchWidget.h\n    dialogs/RizinPluginsDialog.h\n    widgets/CutterDockWidget.h\n    widgets/CutterTreeWidget.h\n    widgets/GraphWidget.h\n    widgets/OverviewWidget.h\n    common/JsonModel.h\n    dialogs/VersionInfoDialog.h\n    widgets/FlirtWidget.h\n    common/AsyncTask.h\n    dialogs/AsyncTaskDialog.h\n    widgets/StackWidget.h\n    widgets/RegistersWidget.h\n    widgets/ThreadsWidget.h\n    widgets/ProcessesWidget.h\n    widgets/BacktraceWidget.h\n    dialogs/MapFileDialog.h\n    common/StringsTask.h\n    common/FunctionsTask.h\n    common/CommandTask.h\n    common/ProgressIndicator.h\n    plugins/CutterPlugin.h\n    common/RizinTask.h\n    dialogs/RizinTaskDialog.h\n    widgets/DebugActions.h\n    widgets/MemoryMapWidget.h\n    dialogs/preferences/DebugOptionsWidget.h\n    dialogs/preferences/PluginsOptionsWidget.h\n    widgets/BreakpointWidget.h\n    dialogs/BreakpointsDialog.h\n    dialogs/AttachProcDialog.h\n    widgets/RegisterRefsWidget.h\n    dialogs/SetToDataDialog.h\n    common/InitialOptions.h\n    dialogs/EditVariablesDialog.h\n    dialogs/EditFunctionDialog.h\n    widgets/CutterTreeView.h\n    widgets/ComboQuickFilterView.h\n    dialogs/HexdumpRangeDialog.h\n    common/CutterSeekable.h\n    common/RefreshDeferrer.h\n    dialogs/WelcomeDialog.h\n    common/RunScriptTask.h\n    common/Json.h\n    dialogs/EditMethodDialog.h\n    dialogs/TypesInteractionDialog.h\n    dialogs/TypesVariablesDialog.h\n    widgets/SdbWidget.h\n    plugins/PluginManager.h\n    common/BasicBlockHighlighter.h\n    common/BasicInstructionHighlighter.h\n    common/UpdateWorker.h\n    widgets/ColorPicker.h\n    common/ColorThemeWorker.h\n    widgets/ColorThemeComboBox.h\n    widgets/MemoryDockWidget.h\n    widgets/ColorThemeListView.h\n    dialogs/preferences/ColorThemeEditDialog.h\n    common/BugReporting.h\n    common/HighDpiPixmap.h\n    widgets/GraphLayout.h\n    widgets/GraphGridLayout.h\n    widgets/HexWidget.h\n    common/SelectionHighlight.h\n    common/Decompiler.h\n    menus/AddressableItemContextMenu.h\n    common/AddressableItemModel.h\n    widgets/ListDockWidget.h\n    widgets/AddressableItemList.h\n    dialogs/MultitypeFileSaveDialog.h\n    widgets/BoolToggleDelegate.h\n    common/IOModesController.h\n    common/SettingsUpgrade.h\n    dialogs/LayoutManager.h\n    common/CutterLayout.h\n    common/BinaryTrees.h\n    common/LinkedListPool.h\n    widgets/GraphHorizontalAdapter.h\n    common/ResourcePaths.h\n    widgets/CutterGraphView.h\n    widgets/SimpleTextGraphView.h\n    widgets/RizinGraphWidget.h\n    widgets/CallGraph.h\n    widgets/AddressableDockWidget.h\n    dialogs/preferences/AnalysisOptionsWidget.h\n    common/DecompilerHighlighter.h\n    dialogs/GlibcHeapInfoDialog.h\n    widgets/HeapDockWidget.h\n    widgets/GlibcHeapWidget.h\n    dialogs/GlibcHeapBinsDialog.h\n    widgets/HeapBinsGraphView.h\n    dialogs/ArenaInfoDialog.h\n    tools/basefind/BaseFindDialog.h\n    tools/basefind/BaseFindSearchDialog.h\n    tools/basefind/BaseFindResultsDialog.h\n    widgets/AddressRangeScrollBar.h\n    dialogs/preferences/ShortcutOptionsWidget.h\n    shortcuts/ShortcutManager.h\n    shortcuts/DefaultShortcuts.h\n    dialogs/MarkDialog.h\n    dialogs/ProfileDirectivesDialog.h\n    dialogs/RegisterProfileDialog.h\n    dialogs/EditRegProfileDialog.h\n    common/DisassemblyHelper.h\n    widgets/SearchBarWidget.h\n    widgets/SearchableDockWidget.h\n    common/CutterSearchable.h\n    widgets/SearchableTextEdit.h\n)\nset(UI_FILES\n    dialogs/AboutDialog.ui\n    dialogs/EditStringDialog.ui\n    dialogs/Base64EnDecodedWriteDialog.ui\n    dialogs/DuplicateFromOffsetDialog.ui\n    dialogs/IncrementDecrementDialog.ui\n    dialogs/preferences/AsmOptionsWidget.ui\n    dialogs/CommentsDialog.ui\n    dialogs/EditInstructionDialog.ui\n    dialogs/FlagDialog.ui\n    dialogs/GlobalVariableDialog.ui\n    dialogs/RemoteDebugDialog.ui\n    dialogs/NativeDebugDialog.ui\n    dialogs/XrefsDialog.ui\n    dialogs/NewFileDialog.ui\n    dialogs/InitialOptionsDialog.ui\n    dialogs/EditFunctionDialog.ui\n    core/MainWindow.ui\n    widgets/ConsoleWidget.ui\n    widgets/Dashboard.ui\n    widgets/EntrypointWidget.ui\n    widgets/FlagsWidget.ui\n    widgets/GlobalsWidget.ui\n    widgets/StringsWidget.ui\n    widgets/HexdumpWidget.ui\n    dialogs/preferences/PreferencesDialog.ui\n    dialogs/preferences/AppearanceOptionsWidget.ui\n    dialogs/preferences/GraphOptionsWidget.ui\n    dialogs/preferences/InitializationFileEditor.ui\n    widgets/QuickFilterView.ui\n    widgets/DecompilerWidget.ui\n    widgets/VTablesWidget.ui\n    widgets/TypesWidget.ui\n    widgets/SearchWidget.ui\n    dialogs/RizinPluginsDialog.ui\n    dialogs/VersionInfoDialog.ui\n    widgets/FlirtWidget.ui\n    dialogs/AsyncTaskDialog.ui\n    dialogs/RizinTaskDialog.ui\n    widgets/StackWidget.ui\n    widgets/RegistersWidget.ui\n    widgets/ThreadsWidget.ui\n    widgets/ProcessesWidget.ui\n    widgets/BacktraceWidget.ui\n    dialogs/MapFileDialog.ui\n    dialogs/preferences/DebugOptionsWidget.ui\n    widgets/BreakpointWidget.ui\n    dialogs/BreakpointsDialog.ui\n    dialogs/AttachProcDialog.ui\n    widgets/RegisterRefsWidget.ui\n    dialogs/SetToDataDialog.ui\n    dialogs/EditVariablesDialog.ui\n    widgets/CutterTreeView.ui\n    widgets/ComboQuickFilterView.ui\n    dialogs/HexdumpRangeDialog.ui\n    dialogs/WelcomeDialog.ui\n    dialogs/EditMethodDialog.ui\n    dialogs/TypesInteractionDialog.ui\n    dialogs/TypesVariablesDialog.ui\n    widgets/SdbWidget.ui\n    widgets/ColorPicker.ui\n    dialogs/preferences/ColorThemeEditDialog.ui\n    widgets/ListDockWidget.ui\n    dialogs/LayoutManager.ui\n    widgets/RizinGraphWidget.ui\n    dialogs/preferences/AnalysisOptionsWidget.ui\n    dialogs/GlibcHeapInfoDialog.ui\n    widgets/HeapDockWidget.ui\n    widgets/GlibcHeapWidget.ui\n    dialogs/GlibcHeapBinsDialog.ui\n    dialogs/ArenaInfoDialog.ui\n    tools/basefind/BaseFindDialog.ui\n    tools/basefind/BaseFindSearchDialog.ui\n    tools/basefind/BaseFindResultsDialog.ui\n    dialogs/preferences/ShortcutOptionsWidget.ui\n    dialogs/MarkDialog.ui\n    dialogs/ProfileDirectivesDialog.ui\n    dialogs/RegisterProfileDialog.ui\n    dialogs/EditRegProfileDialog.ui\n    widgets/SearchBarWidget.ui\n)\nset(QRC_FILES\n    resources.qrc\n    themes/native/native.qrc\n    themes/qdarkstyle/dark.qrc\n    themes/midnight/midnight.qrc\n    themes/lightstyle/light.qrc\n)\n\nset(CUTTER_INCLUDE_DIRECTORIES core widgets common plugins menus .)\n\nif (CUTTER_ENABLE_PYTHON)\n    list(APPEND SOURCES common/QtResImporter.cpp common/PythonManager.cpp common/PythonAPI.cpp)\n    list(APPEND HEADER_FILES common/QtResImporter.h common/PythonManager.h common/PythonAPI.h)\nendif()\n\nif(CUTTER_ENABLE_PYTHON_BINDINGS)\n    if (CUTTER_QT EQUAL 6)\n        set(PYSIDE_NAME PySide6)\n        set(SHIBOKEN_COMMAND Shiboken6::shiboken6)\n        set (CUTTER_PYSIDE_LIBS Shiboken6::libshiboken PySide6::pyside6)\n    elseif(CUTTER_QT EQUAL 5)\n        set(PYSIDE_NAME PySide2)\n        set(SHIBOKEN_COMMAND Shiboken2::shiboken2)\n        set (CUTTER_PYSIDE_LIBS Shiboken2::libshiboken PySide2::pyside2)\n    endif()\n\n    set(BINDINGS_SRC_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/bindings\")\n    set(BINDINGS_BUILD_DIR \"${CMAKE_CURRENT_BINARY_DIR}/bindings\")\n\n    configure_file(\"${BINDINGS_SRC_DIR}/bindings.xml.in\" \"${BINDINGS_BUILD_DIR}/bindings.xml\")\n\n    execute_process(COMMAND \"${PYTHON_EXECUTABLE}\" \"${BINDINGS_SRC_DIR}/src_list.py\" cmake \"${BINDINGS_BUILD_DIR}\" OUTPUT_VARIABLE BINDINGS_SOURCE)\n\n    set_property(SOURCE ${BINDINGS_SOURCE} PROPERTY SKIP_AUTOGEN ON)\n\n    include_directories(\"${BINDINGS_BUILD_DIR}/CutterBindings\")\n\n    set(SHIBOKEN_OPTIONS)\n    if (WIN32)\n        list(APPEND SHIBOKEN_OPTIONS --avoid-protected-hack)\n    endif()\n\n    add_custom_command(OUTPUT ${BINDINGS_SOURCE}\n            COMMAND \"${SHIBOKEN_COMMAND}\" --project-file=\"${BINDINGS_BUILD_DIR}/bindings.txt\" ${SHIBOKEN_OPTIONS} ${SHIBOKEN_EXTRA_OPTIONS}\n            DEPENDS \"${BINDINGS_BUILD_DIR}/bindings.xml\" \"${BINDINGS_BUILD_DIR}/bindings.txt\"\n            IMPLICIT_DEPENDS CXX \"${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h\"\n            COMMENT \"Generating Python bindings with ${SHIBOKEN_COMMAND}\")\nelse()\n    set(BINDINGS_SOURCE \"\")\nendif()\n\n\nif (TARGET Graphviz::GVC)\n    list(APPEND SOURCES widgets/GraphvizLayout.cpp)\n    list(APPEND HEADER_FILES widgets/GraphvizLayout.h)\nendif()\n\nif (WIN32)\n    set(PLATFORM_RESOURCES \"img/cutter.rc\")\n    set(OPTIONS WIN32)\nelse()\n    set(PLATFORM_RESOURCES \"\")\nendif()\n\nif(CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\"\n        OR CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\")\n    add_definitions(-Wall -Wextra)\n    set_source_files_properties(${BINDINGS_SOURCE} PROPERTIES COMPILE_FLAGS -w)\nendif()\n\n# Make a source group for Visual Studio\nset(CUTTER_SOURCES ${OPTIONS} ${UI_FILES} ${QRC_FILES} ${PLATFORM_RESOURCES} ${SOURCES} ${HEADER_FILES})\nsource_group(TREE \"${CMAKE_CURRENT_LIST_DIR}\" FILES ${CUTTER_SOURCES})\n\nadd_executable(Cutter ${CUTTER_SOURCES} ${BINDINGS_SOURCE})\nset_target_properties(Cutter PROPERTIES\n        OUTPUT_NAME cutter\n        RUNTIME_OUTPUT_DIRECTORY ..\n        PDB_OUTPUT_DIRECTORY ..\n        ENABLE_EXPORTS ON\n        CXX_VISIBILITY_PRESET hidden)\ntarget_compile_definitions(Cutter PRIVATE CUTTER_SOURCE_BUILD)\n\n# Set Cutter as the startup project in Visual Studio\nset_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Cutter)\n\nforeach(_dir ${CUTTER_INCLUDE_DIRECTORIES})\n    target_include_directories(Cutter PUBLIC\n        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${_dir}>\n        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cutter/${_dir}>\n    )\nendforeach()\n\nif (TARGET Graphviz::GVC)\n    target_link_libraries(Cutter PRIVATE Graphviz::GVC)\n    target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ)\nendif()\n\ntarget_link_libraries(Cutter PUBLIC ${QT_PREFIX}::Core ${QT_PREFIX}::Widgets ${QT_PREFIX}::Gui PRIVATE  ${QT_PREFIX}::Svg ${QT_PREFIX}::Network)\nif (CUTTER_QT EQUAL 6)\n    target_link_libraries(Cutter PUBLIC Qt6::Core5Compat Qt6::SvgWidgets)\n    target_link_libraries(Cutter PRIVATE Qt6::OpenGLWidgets)\nendif()\n\ntarget_link_libraries(Cutter PUBLIC ${RIZIN_TARGET})\nif(CUTTER_ENABLE_PYTHON)\n    target_link_libraries(Cutter PRIVATE Python3::Python)\n\n    if(CUTTER_ENABLE_PYTHON_BINDINGS)\n        target_link_libraries(Cutter PRIVATE ${CUTTER_PYSIDE_LIBS})\n\n        get_target_property(RAW_BINDINGS_INCLUDE_DIRS Cutter INCLUDE_DIRECTORIES)\n        if(NOT CUTTER_USE_BUNDLED_RIZIN)\n            get_target_property(RAW_RIZIN_INCLUDE_DIRS ${RIZIN_TARGET} INTERFACE_INCLUDE_DIRECTORIES)\n            list(APPEND RAW_BINDINGS_INCLUDE_DIRS \"${RAW_RIZIN_INCLUDE_DIRS}\")\n        endif()\n        set(BINDINGS_INCLUDE_DIRS \"\")\n        foreach(_dir ${RAW_BINDINGS_INCLUDE_DIRS})\n            string(REGEX REPLACE \"\\\\$<BUILD_INTERFACE:(.*)>\" \"\\\\1\" _dir ${_dir})\n            string(REGEX REPLACE \"\\\\$<INSTALL_INTERFACE:(.*)>\" \"\" _dir ${_dir})\n            if (NOT \"${_dir}\" STREQUAL \"\")\n                list(APPEND BINDINGS_INCLUDE_DIRS \"${_dir}\")\n            endif()\n        endforeach()\n\n        set(BINDINGS_INCLUDE_DIR_LINES \"\")\n        if(APPLE)\n            if (_qt5Core_install_prefix)\n                list(APPEND BINDINGS_INCLUDE_DIRS \"${_qt5Core_install_prefix}/include\")\n                list(APPEND BINDINGS_INCLUDE_DIRS \"${_qt5Core_install_prefix}/include/QtCore\")\n                list(APPEND BINDINGS_INCLUDE_DIRS \"${_qt5Core_install_prefix}/include/QtGui\")\n                list(APPEND BINDINGS_INCLUDE_DIRS \"${_qt5Core_install_prefix}/include/QtWidgets\")\n            elseif(CUTTER_QT EQUAL 6)\n                string(APPEND BINDINGS_INCLUDE_DIR_LINES \"framework-include-path=${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBS}\\n\")\n            endif()\n        endif()\n        list(APPEND BINDINGS_INCLUDE_DIRS ${${QT_PREFIX}Core_INCLUDE_DIRS} ${${QT_PREFIX}Widgets_INCLUDE_DIRS} ${${QT_PREFIX}Gui_INCLUDE_DIRS})\n        list(APPEND BINDINGS_INCLUDE_DIRS ${Rizin_INCLUDE_DIRS})\n        list(APPEND BINDINGS_INCLUDE_DIRS \"${CMAKE_CURRENT_SOURCE_DIR}\")\n\n        foreach(_dir ${BINDINGS_INCLUDE_DIRS})\n            if (NOT \"${_dir}\" STREQUAL \"\")\n                string(APPEND BINDINGS_INCLUDE_DIR_LINES \"include-path = ${_dir}\\n\")\n            endif()\n        endforeach()\n\n        configure_file(\"${BINDINGS_SRC_DIR}/bindings.txt.in\" \"${BINDINGS_BUILD_DIR}/bindings.txt\")\n        add_definitions(-DWIN32_LEAN_AND_MEAN)\n    endif()\nendif()\n\nif(TARGET KF${CUTTER_QT}::SyntaxHighlighting)\n    target_link_libraries(Cutter PRIVATE KF${CUTTER_QT}::SyntaxHighlighting)\n    target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_KSYNTAXHIGHLIGHTING)\nendif()\n\nif (CUTTER_APPIMAGE_BUILD)\n    target_compile_definitions(Cutter PRIVATE APPIMAGE)\nendif()\n\nif (CUTTER_PACKAGE_JSDEC)\n\ttarget_compile_definitions(Cutter PRIVATE CUTTER_BUNDLE_JSDEC)\nendif()\n\nif(APPLE AND CUTTER_ENABLE_PACKAGING AND CUTTER_USE_BUNDLED_RIZIN)\n\ttarget_compile_definitions(Cutter PRIVATE MACOS_RZ_BUNDLED)\nendif()\n\nif(CUTTER_ENABLE_PACKAGING)\n    target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_PACKAGING)\nendif()\n\ninclude(Translations)\n\n# Install files\ninstall(TARGETS Cutter\n        EXPORT CutterTargets\n        RUNTIME DESTINATION \"${CMAKE_INSTALL_BINDIR}\"\n        BUNDLE DESTINATION \".\" # needs to be tested\n        ARCHIVE DESTINATION \"${CMAKE_INSTALL_LIBDIR}\" COMPONENT Devel)\ninstall(EXPORT CutterTargets\n    NAMESPACE Cutter::\n    DESTINATION \"${CUTTER_INSTALL_CONFIGDIR}\"\n    COMPONENT Devel)\ninclude(CMakePackageConfigHelpers)\nconfigure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/CutterConfig.cmake.in\n    \"${CMAKE_CURRENT_BINARY_DIR}/CutterConfig.cmake\"\n    INSTALL_DESTINATION \"${CUTTER_INSTALL_CONFIGDIR}\"\n    NO_SET_AND_CHECK_MACRO\n    NO_CHECK_REQUIRED_COMPONENTS_MACRO)\nwrite_basic_package_version_file(\n    \"${CMAKE_CURRENT_BINARY_DIR}/CutterConfigVersion.cmake\"\n    VERSION \"${CUTTER_VERSION_FULL}\"\n    COMPATIBILITY AnyNewerVersion)\ninstall(FILES\n    \"${CMAKE_CURRENT_BINARY_DIR}/CutterConfig.cmake\"\n    \"${CMAKE_CURRENT_BINARY_DIR}/CutterConfigVersion.cmake\"\n    DESTINATION ${CUTTER_INSTALL_CONFIGDIR}\n    COMPONENT Devel)\nforeach(_file ${HEADER_FILES})\n    # Can't use target PUBLIC_HEADER option for installing due to multiple directories\n    get_filename_component(_header_dir \"${_file}\" DIRECTORY)\n    install (FILES \"${_file}\" DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}/cutter/${_header_dir}\" COMPONENT Devel)\nendforeach()\n\nif(UNIX AND NOT APPLE)\n    install(FILES \"img/cutter.svg\"\n        DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps\")\n    install(FILES \"re.rizin.cutter.desktop\"\n        DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/applications\")\n    install(FILES \"re.rizin.cutter.appdata.xml\"\n        DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/metainfo\")\nendif()\n"
  },
  {
    "path": "src/CutterApplication.cpp",
    "content": "#include \"common/PythonManager.h\"\n#include \"CutterApplication.h\"\n#include \"plugins/PluginManager.h\"\n#include \"CutterConfig.h\"\n#include \"common/Decompiler.h\"\n#include \"common/ResourcePaths.h\"\n\n#include <QApplication>\n#include <QFileOpenEvent>\n#include <QEvent>\n#include <QMenu>\n#include <QMessageBox>\n#include <QCommandLineParser>\n#include <QTextCodec>\n#include <QStringList>\n#include <QProcess>\n#include <QPluginLoader>\n#include <QDir>\n#include <QTranslator>\n#include <QLibraryInfo>\n#include <QFontDatabase>\n#ifdef Q_OS_WIN\n#    include <QtNetwork/QtNetwork>\n#endif // Q_OS_WIN\n\n#include <cstdlib>\n\n#if CUTTER_RZGHIDRA_STATIC\n#    include <RzGhidraDecompiler.h>\n#endif\n\n// Rizin before 301e5af2170d9f3ed1edd658b0f9633f31fc4126\n// has RZ_GITTAP defined and uses it in rz_core_version().\n// After that, RZ_GITTAP is not defined anymore and RZ_VERSION is used.\n#ifdef RZ_GITTAP\n#    define CUTTER_COMPILE_TIME_RZ_VERSION \"\" RZ_GITTAP\n#else\n#    define CUTTER_COMPILE_TIME_RZ_VERSION \"\" RZ_VERSION\n#endif\n\nCutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv)\n{\n    // Setup application information\n    setApplicationVersion(CUTTER_VERSION_FULL);\n#ifdef Q_OS_MACOS\n    setWindowIcon(QIcon(\":/img/cutter_macos_simple.svg\"));\n#else\n    setWindowIcon(QIcon(\":/img/cutter.svg\"));\n#endif\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    setAttribute(Qt::AA_UseHighDpiPixmaps); // always enabled on Qt >= 6.0.0\n#endif\n    setLayoutDirection(Qt::LeftToRight);\n    // WARN!!! Put initialization code below this line. Code above this line is mandatory to be run\n    // First\n\n#ifdef Q_OS_WIN\n    // Hack to force Cutter load internet connection related DLL's\n    QSslSocket s;\n    s.sslConfiguration();\n#endif // Q_OS_WIN\n\n    // Load translations\n    if (!loadTranslations()) {\n        qWarning() << \"Cannot load translations\";\n    }\n\n    // Load fonts\n    int ret = QFontDatabase::addApplicationFont(\":/fonts/Anonymous Pro.ttf\");\n    if (ret == -1) {\n        qWarning() << \"Cannot load Anonymous Pro font.\";\n    }\n\n    ret = QFontDatabase::addApplicationFont(\":/fonts/Inconsolata-Regular.ttf\");\n    if (ret == -1) {\n        qWarning() << \"Cannot load Incosolata-Regular font.\";\n    }\n\n    // Set QString codec to UTF-8\n    QTextCodec::setCodecForLocale(QTextCodec::codecForName(\"UTF-8\"));\n#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)\n    QTextCodec::setCodecForCStrings(QTextCodec::codecForName(\"UTF-8\"));\n    QTextCodec::setCodecForTr(QTextCodec::codecForName(\"UTF-8\"));\n#endif\n\n    if (!parseCommandLineOptions()) {\n        std::exit(1);\n    }\n\n    // Check rizin version\n    QString rzversion = rz_core_version();\n    QString localVersion = CUTTER_COMPILE_TIME_RZ_VERSION;\n    qDebug() << rzversion << localVersion;\n    if (rzversion != localVersion) {\n        QMessageBox msg;\n        msg.setIcon(QMessageBox::Critical);\n        msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No);\n        msg.setWindowTitle(QObject::tr(\"Version mismatch!\"));\n        msg.setText(QString(QObject::tr(\"The version used to compile Cutter (%1) does not match \"\n                                        \"the binary version of rizin (%2). This could result in \"\n                                        \"unexpected behaviour. Are you sure you want to continue?\"))\n                            .arg(localVersion, rzversion));\n        if (msg.exec() == QMessageBox::No) {\n            std::exit(1);\n        }\n    }\n\n#ifdef CUTTER_ENABLE_PYTHON\n    // Init python\n    if (!clOptions.pythonHome.isEmpty()) {\n        Python()->setPythonHome(clOptions.pythonHome);\n    }\n    Python()->initialize();\n#endif\n\n    Core()->initialize(clOptions.enableRizinPlugins);\n    Core()->setSettings();\n    Config()->loadInitial();\n    Core()->loadCutterRC();\n\n    Config()->setOutputRedirectionEnabled(clOptions.outputRedirectionEnabled);\n\n#if CUTTER_RZGHIDRA_STATIC\n    Core()->registerDecompiler(new RzGhidraDecompiler(Core()));\n#endif\n\n    Plugins()->loadPlugins(clOptions.enableCutterPlugins);\n\n    for (auto &plugin : Plugins()->getPlugins()) {\n        plugin->registerDecompilers();\n    }\n\n    mainWindow = new MainWindow();\n    installEventFilter(mainWindow);\n\n    // set up context menu shortcut display fix\n#if QT_VERSION_CHECK(5, 10, 0) < QT_VERSION\n    setStyle(new CutterProxyStyle());\n#endif // QT_VERSION_CHECK(5, 10, 0) < QT_VERSION\n\n    if (clOptions.args.empty() && clOptions.fileOpenOptions.projectFile.isEmpty()) {\n        // check if this is the first execution of Cutter in this computer\n        // Note: the execution after the preferences been reset, will be considered as\n        // first-execution\n        if (Config()->isFirstExecution()) {\n            mainWindow->displayWelcomeDialog();\n        }\n        mainWindow->displayNewFileDialog();\n    } else { // filename specified as positional argument\n        bool askOptions = (clOptions.analysisLevel != AutomaticAnalysisLevel::Ask)\n                || !clOptions.fileOpenOptions.projectFile.isEmpty();\n        mainWindow->openNewFile(clOptions.fileOpenOptions, askOptions);\n    }\n\n#ifdef APPIMAGE\n    {\n        auto appdir = QDir(QCoreApplication::applicationDirPath()); // appdir/bin\n        appdir.cdUp(); // appdir\n\n        auto sleighHome = appdir;\n        // appdir/lib/rizin/plugins/rz_ghidra_sleigh/\n        sleighHome.cd(\"lib/rizin/plugins/rz_ghidra_sleigh/\");\n        Core()->setConfig(\"ghidra.sleighhome\", sleighHome.absolutePath());\n    }\n#endif\n\n#if defined(Q_OS_MACOS) && defined(CUTTER_ENABLE_PACKAGING)\n    {\n        auto rzprefix = QDir(QCoreApplication::applicationDirPath()); // Contents/MacOS\n        rzprefix.cdUp(); // Contents\n        rzprefix.cd(\"Resources\"); // Contents/Resources/\n\n        auto sleighHome = rzprefix;\n        // Contents/Resources/lib/rizin/plugins/rz_ghidra_sleigh\n        sleighHome.cd(\"lib/rizin/plugins/rz_ghidra_sleigh\");\n        Core()->setConfig(\"ghidra.sleighhome\", sleighHome.absolutePath());\n    }\n#endif\n\n#if defined(Q_OS_WIN) && defined(CUTTER_ENABLE_PACKAGING)\n    {\n        auto sleighHome = QDir(QCoreApplication::applicationDirPath());\n        sleighHome.cd(\"lib/rizin/plugins/rz_ghidra_sleigh\");\n        Core()->setConfig(\"ghidra.sleighhome\", sleighHome.absolutePath());\n    }\n#endif\n}\n\nCutterApplication::~CutterApplication()\n{\n    Plugins()->destroyPlugins();\n    delete mainWindow;\n#ifdef CUTTER_ENABLE_PYTHON\n    Python()->shutdown();\n#endif\n}\n\nvoid CutterApplication::launchNewInstance(const QStringList &args)\n{\n    QProcess process(this);\n    process.setEnvironment(QProcess::systemEnvironment());\n    QStringList allArgs;\n    if (!clOptions.enableCutterPlugins) {\n        allArgs.push_back(\"--no-cutter-plugins\");\n    }\n    if (!clOptions.enableRizinPlugins) {\n        allArgs.push_back(\"--no-rizin-plugins\");\n    }\n    allArgs.append(args);\n    process.startDetached(qApp->applicationFilePath(), allArgs);\n}\n\nbool CutterApplication::event(QEvent *e)\n{\n    if (e->type() == QEvent::FileOpen) {\n        QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(e);\n        if (openEvent) {\n            if (m_FileAlreadyDropped) {\n                // We already dropped a file in macOS, let's spawn another instance\n                // (Like the File -> Open)\n                QString fileName = openEvent->file();\n                launchNewInstance({ fileName });\n            } else {\n                QString fileName = openEvent->file();\n                m_FileAlreadyDropped = true;\n                mainWindow->closeNewFileDialog();\n                InitialOptions options;\n                options.filename = fileName;\n                mainWindow->openNewFile(options);\n            }\n        }\n    }\n    return QApplication::event(e);\n}\n\nbool CutterApplication::loadTranslations()\n{\n    auto locale = Config()->getCurrLocale();\n\n    bool cutterTrLoaded = false;\n\n    auto availableTranslations = Config()->getAvailableTranslations();\n    if (std::find_if(availableTranslations.begin(), availableTranslations.end(),\n                     [&](const Configuration::LangInfo &item) { return locale == item.locale; })\n        != availableTranslations.end()) {\n        QApplication::setLayoutDirection(locale.textDirection());\n        QLocale::setDefault(locale);\n\n        QTranslator *trCutter = new QTranslator;\n        QTranslator *trQtBase = new QTranslator;\n        QTranslator *trQt = new QTranslator;\n\n        const QStringList &cutterTrPaths = Cutter::getTranslationsDirectories();\n\n        for (const auto &trPath : cutterTrPaths) {\n            if (trCutter\n                && trCutter->load(locale, QLatin1String(\"cutter\"), QLatin1String(\"_\"), trPath)) {\n                installTranslator(trCutter);\n                cutterTrLoaded = true;\n                trCutter = nullptr;\n            }\n            if (trQt && trQt->load(locale, \"qt\", \"_\", trPath)) {\n                installTranslator(trQt);\n                trQt = nullptr;\n            }\n\n            if (trQtBase && trQtBase->load(locale, \"qtbase\", \"_\", trPath)) {\n                installTranslator(trQtBase);\n                trQtBase = nullptr;\n            }\n        }\n\n        if (trCutter) {\n            delete trCutter;\n        }\n        if (trQt) {\n            delete trQt;\n        }\n        if (trQtBase) {\n            delete trQtBase;\n        }\n        return true;\n    }\n    if (locale.language() == QLocale::English) {\n        return true;\n    }\n    if (!cutterTrLoaded) {\n        qWarning() << \"Cannot load Cutter's translation for \" << locale.name();\n    }\n    return false;\n}\n\nQStringList CutterApplication::getArgs() const\n{\n    auto &options = clOptions.fileOpenOptions;\n\n    QStringList args;\n    switch (clOptions.analysisLevel) {\n    case AutomaticAnalysisLevel::None:\n        args.push_back(\"-A\");\n        args.push_back(\"0\");\n        break;\n    case AutomaticAnalysisLevel::AAA:\n        args.push_back(\"-A\");\n        args.push_back(\"1\");\n        break;\n    case AutomaticAnalysisLevel::AAAA:\n        args.push_back(\"-A\");\n        args.push_back(\"2\");\n        break;\n    default:\n        break;\n    }\n\n    if (!options.useVA) {\n        args.push_back(\"-P\");\n    }\n    if (options.writeEnabled) {\n        args.push_back(\"-w\");\n    }\n    if (!options.script.isEmpty()) {\n        args.push_back(\"-i\");\n        args.push_back(options.script);\n    }\n    if (!options.projectFile.isEmpty()) {\n        args.push_back(\"-p\");\n        args.push_back(options.projectFile);\n    }\n    if (!options.arch.isEmpty()) {\n        args.push_back(\"-a\");\n        args.push_back(options.arch);\n    }\n    if (options.bits > 0) {\n        args.push_back(\"-b\");\n        args.push_back(QString::asprintf(\"%d\", options.bits));\n    }\n    if (!options.cpu.isEmpty()) {\n        args.push_back(\"-c\");\n        args.push_back(options.cpu);\n    }\n    if (!options.os.isEmpty()) {\n        args.push_back(\"-o\");\n        args.push_back(options.os);\n    }\n\n    switch (options.endian) {\n    case InitialOptions::Endianness::Little:\n        args.push_back(\"-e\");\n        args.push_back(\"little\");\n        break;\n    case InitialOptions::Endianness::Big:\n        args.push_back(\"-e\");\n        args.push_back(\"big\");\n        break;\n    default:\n        break;\n    }\n    if (!options.forceBinPlugin.isEmpty()) {\n        args.push_back(\"-F\");\n        args.push_back(options.forceBinPlugin);\n    }\n    if (options.binLoadAddr != RVA_INVALID) {\n        args.push_back(\"-B\");\n        args.push_back(RzAddressString(options.binLoadAddr));\n    }\n    if (options.mapAddr != RVA_INVALID) {\n        args.push_back(\"-m\");\n        args.push_back(RzAddressString(options.mapAddr));\n    }\n    if (!options.filename.isEmpty()) {\n        args.push_back(options.filename);\n    }\n    return args;\n}\n\nbool CutterApplication::parseCommandLineOptions()\n{\n    // Keep this function in sync with documentation\n\n    QCommandLineParser cmd_parser;\n    cmd_parser.setApplicationDescription(\n            QObject::tr(\"A Qt and C++ GUI for rizin reverse engineering framework\"));\n    cmd_parser.addHelpOption();\n    cmd_parser.addVersionOption();\n    cmd_parser.addPositionalArgument(\"filename\", QObject::tr(\"Filename to open.\"));\n\n    QCommandLineOption analOption(\n            { \"A\", \"analysis\" },\n            QObject::tr(\"Automatically open file and optionally start analysis. \"\n                        \"Needs filename to be specified. May be a value between 0 and 2:\"\n                        \" 0 = no analysis, 1 = aaa, 2 = aaaa (experimental)\"),\n            QObject::tr(\"level\"));\n    cmd_parser.addOption(analOption);\n\n    QCommandLineOption archOption({ \"a\", \"arch\" }, QObject::tr(\"Sets a specific architecture name\"),\n                                  QObject::tr(\"arch\"));\n    cmd_parser.addOption(archOption);\n\n    QCommandLineOption bitsOption({ \"b\", \"bits\" }, QObject::tr(\"Sets a specific architecture bits\"),\n                                  QObject::tr(\"bits\"));\n    cmd_parser.addOption(bitsOption);\n\n    QCommandLineOption cpuOption({ \"c\", \"cpu\" }, QObject::tr(\"Sets a specific CPU\"),\n                                 QObject::tr(\"cpu\"));\n    cmd_parser.addOption(cpuOption);\n\n    QCommandLineOption osOption({ \"o\", \"os\" }, QObject::tr(\"Sets a specific operating system\"),\n                                QObject::tr(\"os\"));\n    cmd_parser.addOption(osOption);\n\n    QCommandLineOption endianOption({ \"e\", \"endian\" },\n                                    QObject::tr(\"Sets the endianness (big or little)\"),\n                                    QObject::tr(\"big|little\"));\n    cmd_parser.addOption(endianOption);\n\n    QCommandLineOption formatOption({ \"F\", \"format\" },\n                                    QObject::tr(\"Force using a specific file format (bin plugin)\"),\n                                    QObject::tr(\"name\"));\n    cmd_parser.addOption(formatOption);\n\n    QCommandLineOption baddrOption({ \"B\", \"base\" },\n                                   QObject::tr(\"Load binary at a specific base address\"),\n                                   QObject::tr(\"base address\"));\n    cmd_parser.addOption(baddrOption);\n\n    QCommandLineOption maddrOption({ \"m\", \"map\" },\n                                   QObject::tr(\"Map the binary at a specific address\"),\n                                   QObject::tr(\"map address\"));\n    cmd_parser.addOption(maddrOption);\n\n    QCommandLineOption scriptOption(\"i\", QObject::tr(\"Run script file\"), QObject::tr(\"file\"));\n    cmd_parser.addOption(scriptOption);\n\n    QCommandLineOption projectOption({ \"p\", \"project\" }, QObject::tr(\"Load project file\"),\n                                     QObject::tr(\"project file\"));\n    cmd_parser.addOption(projectOption);\n\n    QCommandLineOption writeModeOption({ \"w\", \"writemode\" },\n                                       QObject::tr(\"Open file in write mode\"));\n    cmd_parser.addOption(writeModeOption);\n\n    QCommandLineOption phyModeOption({ \"P\", \"phymode\" },\n                                     QObject::tr(\"Disables virtual addressing\"));\n    cmd_parser.addOption(phyModeOption);\n\n    QCommandLineOption pythonHomeOption(\n            \"pythonhome\", QObject::tr(\"PYTHONHOME to use for embedded python interpreter\"),\n            \"PYTHONHOME\");\n    cmd_parser.addOption(pythonHomeOption);\n\n    QCommandLineOption disableRedirectOption(\n            \"no-output-redirect\",\n            QObject::tr(\"Disable output redirection.\"\n                        \" Some of the output in console widget will not be visible.\"\n                        \" Use this option when debuging a crash or freeze and output \"\n                        \" redirection is causing some messages to be lost.\"));\n    cmd_parser.addOption(disableRedirectOption);\n\n    QCommandLineOption disablePlugins(\"no-plugins\", QObject::tr(\"Do not load plugins\"));\n    cmd_parser.addOption(disablePlugins);\n\n    QCommandLineOption disableCutterPlugins(\"no-cutter-plugins\",\n                                            QObject::tr(\"Do not load Cutter plugins\"));\n    cmd_parser.addOption(disableCutterPlugins);\n\n    QCommandLineOption disableRizinPlugins(\"no-rizin-plugins\",\n                                           QObject::tr(\"Do not load rizin plugins\"));\n    cmd_parser.addOption(disableRizinPlugins);\n\n    cmd_parser.process(*this);\n\n    CutterCommandLineOptions opts;\n    opts.args = cmd_parser.positionalArguments();\n\n    if (cmd_parser.isSet(analOption)) {\n        bool analysisLevelSpecified = false;\n        int analysisLevel = cmd_parser.value(analOption).toInt(&analysisLevelSpecified);\n\n        if (!analysisLevelSpecified || analysisLevel < 0 || analysisLevel > 2) {\n            fprintf(stderr, \"%s\\n\",\n                    QObject::tr(\"Invalid Analysis Level. May be a value between 0 and 2.\")\n                            .toLocal8Bit()\n                            .constData());\n            return false;\n        }\n        switch (analysisLevel) {\n        case 0:\n            opts.analysisLevel = AutomaticAnalysisLevel::None;\n            break;\n        case 1:\n            opts.analysisLevel = AutomaticAnalysisLevel::AAA;\n            break;\n        case 2:\n            opts.analysisLevel = AutomaticAnalysisLevel::AAAA;\n            break;\n        }\n    }\n\n    if (opts.args.empty() && opts.analysisLevel != AutomaticAnalysisLevel::Ask) {\n        fprintf(stderr, \"%s\\n\",\n                QObject::tr(\"Filename must be specified to start analysis automatically.\")\n                        .toLocal8Bit()\n                        .constData());\n        return false;\n    }\n\n    if (!opts.args.isEmpty()) {\n        opts.fileOpenOptions.filename = opts.args[0];\n        opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption);\n        if (cmd_parser.isSet(baddrOption)) {\n            bool ok = false;\n            RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0);\n            if (ok) {\n                opts.fileOpenOptions.binLoadAddr = baddr;\n            }\n        }\n        if (cmd_parser.isSet(maddrOption)) {\n            bool ok = false;\n            RVA maddr = cmd_parser.value(maddrOption).toULongLong(&ok, 0);\n            if (ok) {\n                opts.fileOpenOptions.mapAddr = maddr;\n            }\n        }\n        switch (opts.analysisLevel) {\n        case AutomaticAnalysisLevel::Ask:\n            break;\n        case AutomaticAnalysisLevel::None:\n            opts.fileOpenOptions.analysisCmd = {};\n            break;\n        case AutomaticAnalysisLevel::AAA:\n            opts.fileOpenOptions.analysisCmd = {\n                { \"aaa\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Auto analysis\") }\n            };\n            break;\n        case AutomaticAnalysisLevel::AAAA:\n            opts.fileOpenOptions.analysisCmd = {\n                { \"aaaa\",\n                  QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Auto analysis (experimental)\") }\n            };\n            break;\n        }\n        opts.fileOpenOptions.script = cmd_parser.value(scriptOption);\n        opts.fileOpenOptions.arch = cmd_parser.value(archOption);\n        opts.fileOpenOptions.cpu = cmd_parser.value(cpuOption);\n        opts.fileOpenOptions.os = cmd_parser.value(osOption);\n        if (cmd_parser.isSet(bitsOption)) {\n            bool ok = false;\n            int bits = cmd_parser.value(bitsOption).toInt(&ok, 10);\n            if (ok && bits > 0) {\n                opts.fileOpenOptions.bits = bits;\n            }\n        }\n        if (cmd_parser.isSet(endianOption)) {\n            QString endian = cmd_parser.value(endianOption).toLower();\n            opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;\n            if (endian == \"little\") {\n                opts.fileOpenOptions.endian = InitialOptions::Endianness::Little;\n            } else if (endian == \"big\") {\n                opts.fileOpenOptions.endian = InitialOptions::Endianness::Big;\n            } else {\n                fprintf(stderr, \"%s\\n\",\n                        QObject::tr(\"Invalid Endianness. You can only set it to `big` or `little`.\")\n                                .toLocal8Bit()\n                                .constData());\n                return false;\n            }\n        } else {\n            opts.fileOpenOptions.endian = InitialOptions::Endianness::Auto;\n        }\n\n        opts.fileOpenOptions.writeEnabled = cmd_parser.isSet(writeModeOption);\n        opts.fileOpenOptions.useVA = !cmd_parser.isSet(phyModeOption);\n    }\n\n    opts.fileOpenOptions.projectFile = cmd_parser.value(projectOption);\n\n    if (cmd_parser.isSet(pythonHomeOption)) {\n        opts.pythonHome = cmd_parser.value(pythonHomeOption);\n    }\n\n    opts.outputRedirectionEnabled = !cmd_parser.isSet(disableRedirectOption);\n    if (cmd_parser.isSet(disablePlugins)) {\n        opts.enableCutterPlugins = false;\n        opts.enableRizinPlugins = false;\n    }\n\n    if (cmd_parser.isSet(disableCutterPlugins)) {\n        opts.enableCutterPlugins = false;\n    }\n\n    if (cmd_parser.isSet(disableRizinPlugins)) {\n        opts.enableRizinPlugins = false;\n    }\n\n    this->clOptions = opts;\n    return true;\n}\n\nvoid CutterProxyStyle::polish(QWidget *widget)\n{\n    QProxyStyle::polish(widget);\n#if QT_VERSION_CHECK(5, 10, 0) < QT_VERSION\n    // HACK: This is the only way I've found to force Qt (5.10 and newer) to\n    //       display shortcuts in context menus on all platforms. It's ugly,\n    //       but it gets the job done.\n    if (auto menu = qobject_cast<QMenu *>(widget)) {\n        const auto &actions = menu->actions();\n        for (auto action : actions) {\n            action->setShortcutVisibleInContextMenu(true);\n        }\n    }\n#endif // QT_VERSION_CHECK(5, 10, 0) < QT_VERSION\n}\n"
  },
  {
    "path": "src/CutterApplication.h",
    "content": "#ifndef CUTTERAPPLICATION_H\n#define CUTTERAPPLICATION_H\n\n#include <QEvent>\n#include <QApplication>\n#include <QList>\n#include <QProxyStyle>\n\n#include \"core/MainWindow.h\"\n\nenum class AutomaticAnalysisLevel { Ask, None, AAA, AAAA };\n\nstruct CutterCommandLineOptions\n{\n    QStringList args;\n    AutomaticAnalysisLevel analysisLevel = AutomaticAnalysisLevel::Ask;\n    InitialOptions fileOpenOptions;\n    QString pythonHome;\n    bool outputRedirectionEnabled = true;\n    bool enableCutterPlugins = true;\n    bool enableRizinPlugins = true;\n};\n\nclass CutterApplication : public QApplication\n{\n    Q_OBJECT\n\npublic:\n    CutterApplication(int &argc, char **argv);\n    ~CutterApplication();\n\n    MainWindow *getMainWindow() { return mainWindow; }\n\n    void launchNewInstance(const QStringList &args = {});\n\n    InitialOptions getInitialOptions() const { return clOptions.fileOpenOptions; }\n    void setInitialOptions(const InitialOptions &options) { clOptions.fileOpenOptions = options; }\n    QStringList getArgs() const;\n\nprotected:\n    bool event(QEvent *e);\n\nprivate:\n    /**\n     * @brief Load and translations depending on Language settings\n     * @return true on success\n     */\n    bool loadTranslations();\n    /**\n     * @brief Parse commandline options and store them in a structure.\n     * @return false if options have error\n     */\n    bool parseCommandLineOptions();\n\nprivate:\n    bool m_FileAlreadyDropped;\n    CutterCore core;\n    MainWindow *mainWindow;\n    CutterCommandLineOptions clOptions;\n};\n\n/**\n * @brief CutterProxyStyle is used to force shortcuts displaying in context menu\n */\nclass CutterProxyStyle : public QProxyStyle\n{\n    Q_OBJECT\npublic:\n    /**\n     * @brief it is enough to get notification about QMenu polishing to force shortcut displaying\n     */\n    void polish(QWidget *widget) override;\n};\n\n#endif // CUTTERAPPLICATION_H\n"
  },
  {
    "path": "src/CutterConfig.h.in",
    "content": "\n#ifndef CUTTER_CONFIG_H\n#define CUTTER_CONFIG_H\n\n#define CUTTER_VERSION_MAJOR @CUTTER_VERSION_MAJOR@\n#define CUTTER_VERSION_MINOR @CUTTER_VERSION_MINOR@\n#define CUTTER_VERSION_PATCH @CUTTER_VERSION_PATCH@\n\n#define CUTTER_VERSION_FULL \"@CUTTER_VERSION_FULL@\"\n\n#define CUTTER_EXTRA_PLUGIN_DIRS \"@CUTTER_EXTRA_PLUGIN_DIRS@\"\n\n#endif\n"
  },
  {
    "path": "src/Main.cpp",
    "content": "\n#include \"CutterApplication.h\"\n#include \"core/MainWindow.h\"\n#include \"common/UpdateWorker.h\"\n#include \"CutterConfig.h\"\n#include \"common/SettingsUpgrade.h\"\n\n#include <QJsonObject>\n#include <QJsonArray>\n#include <iostream>\n\n/**\n * @brief Attempt to connect to a parent console and configure outputs.\n */\n#ifdef Q_OS_WIN\n#    include <windows.h>\n\nstatic void connectToConsole()\n{\n    BOOL attached = AttachConsole(ATTACH_PARENT_PROCESS);\n\n    // Avoid reconfiguring stdin/stderr/stdout if one of them is already connected to a stream.\n    // This can happen when running with stdout/stderr redirected to a file.\n    if (0 > fileno(stdin)) {\n        // Overwrite FD 0, FD 1 and 2 for the benefit of any code that uses the FDs\n        // directly.  This is safe because the CRT allocates FDs 0, 1 and\n        // 2 at startup even if they don't have valid underlying Windows\n        // handles.  This means we won't be overwriting an FD created by\n        // _open() after startup.\n        _close(0);\n\n        freopen(attached ? \"CONIN$\" : \"NUL\", \"r+\", stdin);\n    }\n    if (0 > fileno(stdout)) {\n        _close(1);\n\n        if (freopen(attached ? \"CONOUT$\" : \"NUL\", \"a+\", stdout)) {\n            // Avoid buffering stdout/stderr since IOLBF is replaced by IOFBF in Win32.\n            setvbuf(stdout, nullptr, _IONBF, 0);\n        }\n    }\n    if (0 > fileno(stderr)) {\n        _close(2);\n\n        if (freopen(attached ? \"CONOUT$\" : \"NUL\", \"a+\", stderr)) {\n            setvbuf(stderr, nullptr, _IONBF, 0);\n        }\n    }\n\n    // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.\n    std::ios::sync_with_stdio();\n}\n#endif\n\nint main(int argc, char *argv[])\n{\n#ifdef Q_OS_WIN\n    connectToConsole();\n#endif\n\n    qRegisterMetaType<QList<StringDescription>>();\n    qRegisterMetaType<QList<FunctionDescription>>();\n\n    QCoreApplication::setOrganizationName(\"rizin\");\n#ifndef Q_OS_MACOS // don't set on macOS so that it doesn't affect config path there\n    QCoreApplication::setOrganizationDomain(\"rizin.re\");\n#endif\n    QCoreApplication::setApplicationName(\"cutter\");\n\n    // Importing settings after setting rename, needs separate handling in addition to regular\n    // version to version upgrade.\n    if (Cutter::shouldOfferSettingImport()) {\n        Cutter::showSettingImportDialog(argc, argv);\n    }\n\n    Cutter::initializeSettings();\n\n    QCoreApplication::setAttribute(\n            Qt::AA_ShareOpenGLContexts); // needed for QtWebEngine inside Plugins\n#ifdef Q_OS_WIN\n    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);\n#    if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)\n    QGuiApplication::setHighDpiScaleFactorRoundingPolicy(\n            Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);\n#    endif\n#endif\n\n    CutterApplication a(argc, argv);\n\n    Cutter::migrateThemes();\n\n    if (Config()->getAutoUpdateEnabled()) {\n#if CUTTER_UPDATE_WORKER_AVAILABLE\n        UpdateWorker *updateWorker = new UpdateWorker;\n        QObject::connect(updateWorker, &UpdateWorker::checkComplete,\n                         [=](const QVersionNumber &version, const QString &error) {\n                             if (error.isEmpty()\n                                 && version > UpdateWorker::currentVersionNumber()) {\n                                 updateWorker->showUpdateDialog(true);\n                             }\n                             updateWorker->deleteLater();\n                         });\n        updateWorker->checkCurrentVersion(7000);\n#endif\n    }\n\n    int ret = a.exec();\n\n    return ret;\n}\n"
  },
  {
    "path": "src/bindings/CutterBindings/meson.build",
    "content": "\n# 😠 https://github.com/mesonbuild/meson/issues/2320\n\nbindings_src_files = run_command([py3_exe,\n                                  join_paths(meson.current_source_dir(), '../src_list.py'),\n                                  'meson']).stdout().split(';')\n\nbindings_target = custom_target('bindings',\n                                input: bindings_txt,\n                                depend_files: [bindings_h, bindings_xml],\n                                output: bindings_src_files,\n                                command: [shiboken_exe, '--project-file=@INPUT@'])"
  },
  {
    "path": "src/bindings/bindings.h",
    "content": "\n#ifndef CUTTER_BINDINGS_H\n#define CUTTER_BINDINGS_H\n\n#define QT_ANNOTATE_ACCESS_SPECIFIER(a) __attribute__((annotate(#a)))\n\n#include \"../core/Cutter.h\"\n#include \"../common/Configuration.h\"\n#include \"../core/MainWindow.h\"\n#include \"../widgets/CutterDockWidget.h\"\n#include \"../plugins/CutterPlugin.h\"\n#include \"../menus/AddressableItemContextMenu.h\"\n\n#endif // CUTTER_BINDINGS_H\n"
  },
  {
    "path": "src/bindings/bindings.txt.in",
    "content": "[generator-project]\n\ngenerator-set = shiboken\n\nheader-file = ${BINDINGS_SRC_DIR}/bindings.h\ntypesystem-file = ${BINDINGS_BUILD_DIR}/bindings.xml\n\noutput-directory = ${BINDINGS_BUILD_DIR}\n\n${BINDINGS_INCLUDE_DIR_LINES}\n\ntypesystem-paths = ${PYSIDE_TYPESYSTEMS}\n\nenable-parent-ctor-heuristic\nenable-pyside-extensions\nenable-return-value-heuristic\nuse-isnull-as-nb_nonzero\n"
  },
  {
    "path": "src/bindings/bindings.xml.in",
    "content": "<?xml version=\"1.0\"?>\n<typesystem package=\"CutterBindings\">\n    <load-typesystem name=\"typesystem_core.xml\" generate=\"no\" />\n    <load-typesystem name=\"typesystem_gui.xml\" generate=\"no\" />\n    <load-typesystem name=\"typesystem_widgets.xml\" generate=\"no\" />\n\n    <primitive-type name=\"bool\"/>\n\n    <object-type name=\"CutterCore\">\n    </object-type>\n    <object-type name=\"Configuration\" />\n    <object-type name=\"MainWindow\" >\n        <enum-type name=\"MenuType\" />\n        <enum-type name=\"ContextMenuType\" />\n    </object-type>\n    <object-type name=\"BasicBlockHighlighter\" />\n    <object-type name=\"BasicInstructionHighlighter\" />\n    <object-type name=\"AddressableItemContextMenu\" />\n    <object-type name=\"CutterDockWidget\" />\n\n    <template name=\"plugin_meta_get\">\n        Shiboken::GilState cutterGil;\n        SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(this);\n        PyObject *classObject = PyObject_GetAttrString(reinterpret_cast&lt;PyObject *&gt;(wrapper), \"__class__\");\n        if (!classObject) {\n        PyErr_Print();\n        return QString();\n        }\n        PyObject *pyResult = PyObject_GetAttrString(classObject, \"VAR_NAME\");\n        if (!pyResult) {\n        PyErr_Print();\n        return QString();\n        }\n        auto converter = Shiboken::Conversions::getConverter(\"str\");\n        if (!converter) {\n            Shiboken::warning(PyExc_RuntimeWarning, 0, \"Can't find converter for str.\");\n            return ::QString();\n        }\n        PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(converter, pyResult);\n        if (!pythonToCpp) {\n        Shiboken::warning(PyExc_RuntimeWarning, 1, \"Invalid return value for plugin metadata VAR_NAME, expected str, got %s.\", Py_TYPE(pyResult)->tp_name);\n        return ::QString();\n        }\n        QString cppResult;\n        pythonToCpp(pyResult, &amp;cppResult);\n        return cppResult;\n    </template>\n    <object-type name=\"CutterPlugin\">\n        <modify-function signature=\"getName()const\">\n            <inject-code class=\"shell\" position=\"beginning\">\n                <insert-template name=\"plugin_meta_get\">\n                    <replace from=\"VAR_NAME\" to=\"name\" />\n                </insert-template>\n            </inject-code>\n        </modify-function>\n        <modify-function signature=\"getAuthor()const\">\n            <inject-code class=\"shell\" position=\"beginning\">\n                <insert-template name=\"plugin_meta_get\">\n                    <replace from=\"VAR_NAME\" to=\"author\" />\n                </insert-template>\n            </inject-code>\n        </modify-function>\n        <modify-function signature=\"getDescription()const\">\n            <inject-code class=\"shell\" position=\"beginning\">\n                <insert-template name=\"plugin_meta_get\">\n                    <replace from=\"VAR_NAME\" to=\"description\" />\n                </insert-template>\n            </inject-code>\n        </modify-function>\n        <modify-function signature=\"getVersion()const\">\n            <inject-code class=\"shell\" position=\"beginning\">\n                <insert-template name=\"plugin_meta_get\">\n                    <replace from=\"VAR_NAME\" to=\"version\" />\n                </insert-template>\n            </inject-code>\n        </modify-function>\n    </object-type>\n</typesystem>\n"
  },
  {
    "path": "src/bindings/src_list.py",
    "content": "#!/bin/env python3\n\nimport os\nimport xml.etree.ElementTree as et\nimport sys\n\n\nscript_path = os.path.dirname(os.path.realpath(__file__))\n\n\ndef get_cpp_files_gen(args, include_package=True):\n    ts_tree = et.parse(os.path.join(script_path, \"bindings.xml.in\"))\n    ts_root = ts_tree.getroot()\n\n    package = ts_root.attrib[\"package\"]\n\n    type_tags = [\"object-type\", \"value-type\"]\n    types = [child.attrib[\"name\"] for child in ts_root if child.tag in type_tags]\n\n    cpp_files_gen = [f\"{package.lower()}_module_wrapper.cpp\"]\n    cpp_files_gen.extend([f\"{typename.lower()}_wrapper.cpp\" for typename in types])\n\n    if include_package:\n        cpp_files_gen = [os.path.join(package, f) for f in cpp_files_gen]\n\n    if len(args) > 0:\n        cpp_files_gen = [os.path.join(args[0], f) for f in cpp_files_gen]\n\n    return cpp_files_gen\n\n\ndef cmd_cmake(args):\n    files = get_cpp_files_gen(args)\n    if sys.platform == \"win32\":\n        files = map(lambda x: x.replace(\"\\\\\", \"/\"), files)\n    sys.stdout.write(\";\".join(files))\n\n\ndef cmd_qmake(args):\n    sys.stdout.write(\"\\n\".join(get_cpp_files_gen(args)) + \"\\n\")\n\n\ndef cmd_meson(args):\n    sys.stdout.write(\";\".join(get_cpp_files_gen(args, include_package=False)))\n\n\ncmds = {\"cmake\": cmd_cmake, \"qmake\": cmd_qmake, \"meson\": cmd_meson}\n\nif len(sys.argv) < 2 or sys.argv[1] not in cmds:\n    print(f\"\"\"usage: {sys.argv[0]} [{\"/\".join(cmds.keys())}] [base path]\"\"\")\n    exit(1)\ncmds[sys.argv[1]](sys.argv[2:])\n"
  },
  {
    "path": "src/common/AddressableItemModel.cpp",
    "content": "#include <stdexcept>\n#include \"AddressableItemModel.h\"\n\n#include <stdexcept>\n\nAddressableFilterProxyModel::AddressableFilterProxyModel(AddressableItemModelI *sourceModel,\n                                                         QObject *parent)\n    : AddressableItemModel<QSortFilterProxyModel>(parent)\n{\n    setSourceModel(sourceModel);\n    addressableSourceModel = sourceModel;\n}\n\nRVA AddressableFilterProxyModel::address(const QModelIndex &index) const\n{\n    if (!addressableSourceModel) {\n        return RVA_INVALID;\n    }\n    return addressableSourceModel->address(this->mapToSource(index));\n}\n\nQString AddressableFilterProxyModel::name(const QModelIndex &index) const\n{\n    if (!addressableSourceModel) {\n        return QString();\n    }\n    return addressableSourceModel->name(this->mapToSource(index));\n}\n\nvoid AddressableFilterProxyModel::setSourceModel(QAbstractItemModel *)\n{\n    throw new std::runtime_error(\"Not supported\");\n}\n\nvoid AddressableFilterProxyModel::setSourceModel(AddressableItemModelI *sourceModel)\n{\n    ParentClass::setSourceModel(sourceModel ? sourceModel->asItemModel() : nullptr);\n    addressableSourceModel = sourceModel;\n}\n"
  },
  {
    "path": "src/common/AddressableItemModel.h",
    "content": "\n#ifndef ADDRESSABLEITEMMODEL_H\n#define ADDRESSABLEITEMMODEL_H\n\n#include <QAbstractItemModel>\n#include <QSortFilterProxyModel>\n#include <QAbstractItemModel>\n\n#include \"core/CutterCommon.h\"\n\nclass CUTTER_EXPORT AddressableItemModelI\n{\npublic:\n    virtual RVA address(const QModelIndex &index) const = 0;\n    /**\n     * @brief Get name for item, optional.\n     * @param index item intex\n     * @return Item name or empty QString if item doesn't have short descriptive name.\n     */\n    virtual QString name(const QModelIndex &index) const\n    {\n        Q_UNUSED(index)\n        return QString();\n    }\n    virtual QAbstractItemModel *asItemModel() = 0;\n};\n\ntemplate<class ParentModel = QAbstractItemModel>\nclass CUTTER_EXPORT AddressableItemModel : public ParentModel, public AddressableItemModelI\n{\n    static_assert(std::is_base_of<QAbstractItemModel, ParentModel>::value,\n                  \"ParentModel needs to inherit from QAbstractItemModel\");\n\npublic:\n    explicit AddressableItemModel(QObject *parent = nullptr) : ParentModel(parent) {}\n    virtual ~AddressableItemModel() {}\n    QAbstractItemModel *asItemModel() { return this; }\n};\n\nclass CUTTER_EXPORT AddressableFilterProxyModel : public AddressableItemModel<QSortFilterProxyModel>\n{\n    using ParentClass = AddressableItemModel<QSortFilterProxyModel>;\n\npublic:\n    AddressableFilterProxyModel(AddressableItemModelI *sourceModel, QObject *parent);\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &) const override;\n    void setSourceModel(AddressableItemModelI *sourceModel);\n\nprivate:\n    void setSourceModel(QAbstractItemModel *sourceModel) override; // Don't use this directly\n    AddressableItemModelI *addressableSourceModel;\n};\n\n#endif // ADDRESSABLEITEMMODEL_H\n"
  },
  {
    "path": "src/common/AnalysisTask.cpp",
    "content": "#include \"core/Cutter.h\"\n#include \"common/AnalysisTask.h\"\n#include \"core/MainWindow.h\"\n#include \"dialogs/InitialOptionsDialog.h\"\n#include <QJsonArray>\n#include <QDebug>\n#include <QCheckBox>\n\nAnalysisTask::AnalysisTask() : AsyncTask() {}\n\nAnalysisTask::~AnalysisTask() {}\n\nvoid AnalysisTask::interrupt()\n{\n    AsyncTask::interrupt();\n    rz_cons_singleton()->context->breaked = true;\n}\n\nQString AnalysisTask::getTitle()\n{\n    // If no file is loaded we consider it's Initial Analysis\n    RzCoreLocked core(Core());\n    RzList *descs = rz_id_storage_list(core->io->files);\n    if (rz_list_empty(descs)) {\n        return tr(\"Initial Analysis\");\n    }\n    return tr(\"Analyzing Program\");\n}\n\nvoid AnalysisTask::runTask()\n{\n    int perms = RZ_PERM_RX;\n    if (options.writeEnabled) {\n        perms |= RZ_PERM_W;\n        emit Core()->ioModeChanged();\n    }\n\n    // Demangle (must be before file Core()->loadFile)\n    Core()->setConfig(\"bin.demangle\", options.demangle);\n\n    // Do not reload the file if already loaded\n    RzCoreLocked core(Core());\n    RzList *descs = rz_id_storage_list(core->io->files);\n    if (rz_list_empty(descs) && options.filename.length()) {\n        log(tr(\"Loading the file...\"));\n        openFailed = false;\n        bool fileLoaded =\n                Core()->loadFile(options.filename, options.binLoadAddr, options.mapAddr, perms,\n                                 options.useVA, options.loadBinInfo, options.forceBinPlugin);\n        if (!fileLoaded) {\n            // Something wrong happened, fallback to open dialog\n            openFailed = true;\n            emit openFileFailed();\n            interrupt();\n            return;\n        }\n    }\n\n    // rz_core_bin_load might change asm.bits, so let's set that after the bin is loaded\n    Core()->setCPU(options.arch, options.cpu, options.bits);\n\n    if (isInterrupted()) {\n        return;\n    }\n\n    if (!options.os.isNull()) {\n        RzCoreLocked core(Core());\n        rz_config_set(core->config, \"asm.os\", options.os.toUtf8().constData());\n    }\n\n    if (!options.pdbFile.isNull()) {\n        log(tr(\"Loading PDB file...\"));\n        Core()->loadPDB(options.pdbFile);\n    }\n\n    if (isInterrupted()) {\n        return;\n    }\n\n    if (!options.shellcode.isNull() && options.shellcode.size() / 2 > 0) {\n        log(tr(\"Loading shellcode...\"));\n        rz_core_write_hexpair(core, core->offset, options.shellcode.toStdString().c_str());\n    }\n\n    if (options.endian != InitialOptions::Endianness::Auto) {\n        Core()->setEndianness(options.endian == InitialOptions::Endianness::Big);\n    }\n\n    rz_flag_space_set(core->flags, \"*\");\n\n    if (!options.script.isNull()) {\n        log(tr(\"Executing script...\"));\n        Core()->loadScript(options.script);\n    }\n\n    if (isInterrupted()) {\n        return;\n    }\n\n    if (!options.analysisCmd.empty()) {\n        log(tr(\"Executing analysis...\"));\n        for (const CommandDescription &cmd : options.analysisCmd) {\n            if (isInterrupted()) {\n                return;\n            }\n            log(cmd.translatedDescription());\n            // use cmd instead of cmdRaw because commands can be unexpected\n            Core()->cmd(cmd.command);\n        }\n        log(tr(\"Analysis complete!\"));\n    } else {\n        log(tr(\"Skipping Analysis.\"));\n    }\n}\n"
  },
  {
    "path": "src/common/AnalysisTask.h",
    "content": "#ifndef ANALTHREAD_H\n#define ANALTHREAD_H\n\n#include \"common/AsyncTask.h\"\n#include \"core/Cutter.h\"\n#include \"common/InitialOptions.h\"\n\nclass CutterCore;\nclass MainWindow;\nclass InitialOptionsDialog;\n\nclass AnalysisTask : public AsyncTask\n{\n    Q_OBJECT\n\npublic:\n    explicit AnalysisTask();\n    ~AnalysisTask();\n\n    QString getTitle() override;\n\n    void setOptions(const InitialOptions &options) { this->options = options; }\n\n    void interrupt() override;\n\n    bool getOpenFileFailed() { return openFailed; }\n\nprotected:\n    void runTask() override;\n\nsignals:\n    void openFileFailed();\n\nprivate:\n    InitialOptions options;\n\n    bool openFailed = false;\n};\n\n#endif // ANALTHREAD_H\n"
  },
  {
    "path": "src/common/AsyncTask.cpp",
    "content": "\n#include \"AsyncTask.h\"\n\nAsyncTask::AsyncTask() : QObject(nullptr), QRunnable()\n{\n    setAutoDelete(false);\n    running = false;\n}\n\nAsyncTask::~AsyncTask()\n{\n    wait();\n}\n\nvoid AsyncTask::wait()\n{\n    runningMutex.lock();\n    runningMutex.unlock();\n}\n\nbool AsyncTask::wait(int timeout)\n{\n    bool r = runningMutex.tryLock(timeout);\n    if (r) {\n        runningMutex.unlock();\n    }\n    return r;\n}\n\nvoid AsyncTask::interrupt()\n{\n    interrupted = true;\n}\n\nvoid AsyncTask::prepareRun()\n{\n    interrupted = false;\n    wait();\n    timer.start();\n}\n\nvoid AsyncTask::run()\n{\n    runningMutex.lock();\n\n    running = true;\n\n    logBuffer.clear();\n    emit logChanged(logBuffer);\n    runTask();\n\n    running = false;\n\n    emit finished();\n\n    runningMutex.unlock();\n}\n\nvoid AsyncTask::log(QString s)\n{\n    logBuffer += s.append(QLatin1Char('\\n'));\n    emit logChanged(logBuffer);\n}\n\nAsyncTaskManager::AsyncTaskManager(QObject *parent) : QObject(parent)\n{\n    threadPool = new QThreadPool(this);\n}\n\nAsyncTaskManager::~AsyncTaskManager() {}\n\nvoid AsyncTaskManager::start(AsyncTask::Ptr task)\n{\n    tasks.append(task);\n    task->prepareRun();\n\n    QWeakPointer<AsyncTask> weakPtr = task;\n    connect(task.data(), &AsyncTask::finished, this, [this, weakPtr]() {\n        tasks.removeOne(weakPtr);\n        emit tasksChanged();\n    });\n    threadPool->start(task.data());\n    emit tasksChanged();\n}\n\nbool AsyncTaskManager::getTasksRunning()\n{\n    return !tasks.isEmpty();\n}\n"
  },
  {
    "path": "src/common/AsyncTask.h",
    "content": "\n#ifndef ASYNCTASK_H\n#define ASYNCTASK_H\n\n#include \"core/CutterCommon.h\"\n\n#include <QRunnable>\n#include <QThreadPool>\n#include <QMutex>\n#include <QElapsedTimer>\n#include <QSharedPointer>\n#include <QList>\n\nclass AsyncTaskManager;\n\nclass CUTTER_EXPORT AsyncTask : public QObject, public QRunnable\n{\n    Q_OBJECT\n\n    friend class AsyncTaskManager;\n\npublic:\n    using Ptr = QSharedPointer<AsyncTask>;\n\n    AsyncTask();\n    ~AsyncTask();\n\n    void run() override final;\n\n    void wait();\n    bool wait(int timeout);\n    virtual void interrupt();\n    bool isInterrupted() { return interrupted; }\n    bool isRunning() { return running; }\n\n    const QString &getLog() { return logBuffer; }\n    const QElapsedTimer &getTimer() { return timer; }\n    qint64 getElapsedTime() { return timer.isValid() ? timer.elapsed() : 0; }\n\n    virtual QString getTitle() { return QString(); }\n\nprotected:\n    virtual void runTask() = 0;\n\n    void log(QString s);\n\nsignals:\n    void finished();\n    void logChanged(const QString &log);\n\nprivate:\n    bool running;\n    bool interrupted;\n    QMutex runningMutex;\n\n    QElapsedTimer timer;\n    QString logBuffer;\n\n    void prepareRun();\n};\n\nclass AsyncTaskManager : public QObject\n{\n    Q_OBJECT\n\nprivate:\n    QThreadPool *threadPool;\n    QList<AsyncTask::Ptr> tasks;\n\npublic:\n    explicit AsyncTaskManager(QObject *parent = nullptr);\n    ~AsyncTaskManager();\n\n    void start(AsyncTask::Ptr task);\n    bool getTasksRunning();\n\nsignals:\n    void tasksChanged();\n};\n\n#endif // ASYNCTASK_H"
  },
  {
    "path": "src/common/BasicBlockHighlighter.cpp",
    "content": "#include \"BasicBlockHighlighter.h\"\n\nBasicBlockHighlighter::BasicBlockHighlighter() {}\n\n/**\n * @brief Highlight the basic block at address\n */\nvoid BasicBlockHighlighter::highlight(RVA address, const QColor &color)\n{\n    BasicBlock block;\n    block.address = address;\n    block.color = color;\n    bbMap[address] = block;\n}\n\n/**\n * @brief Clear the basic block highlighting\n */\nvoid BasicBlockHighlighter::clear(RVA address)\n{\n    bbMap.erase(address);\n}\n\n/**\n * @brief Return a highlighted basic block\n *\n * If there is nothing to highlight at specified address, returns nullptr\n */\nBasicBlockHighlighter::BasicBlock *BasicBlockHighlighter::getBasicBlock(RVA address)\n{\n    auto it = bbMap.find(address);\n    if (it != bbMap.end()) {\n        return &it->second;\n    }\n\n    return nullptr;\n}\n"
  },
  {
    "path": "src/common/BasicBlockHighlighter.h",
    "content": "#ifndef BASICKBLOCKHIGHLIGHTER_H\n#define BASICKBLOCKHIGHLIGHTER_H\n\nclass BasicBlockHighlighter;\n\n#include \"Cutter.h\"\n#include <map>\n\nclass BasicBlockHighlighter\n{\npublic:\n    struct BasicBlock\n    {\n        RVA address;\n        QColor color;\n    };\n\n    BasicBlockHighlighter();\n\n    void highlight(RVA address, const QColor &color);\n    void clear(RVA address);\n    BasicBlock *getBasicBlock(RVA address);\n\nprivate:\n    std::map<RVA, BasicBlock> bbMap;\n};\n\n#endif // BASICBLOCKHIGHLIGHTER_H\n"
  },
  {
    "path": "src/common/BasicInstructionHighlighter.cpp",
    "content": "#include \"BasicInstructionHighlighter.h\"\n#include <vector>\n\n/**\n * @brief Clear the basic instruction highlighting\n */\nvoid BasicInstructionHighlighter::clear(RVA address, RVA size)\n{\n    BasicInstructionIt it = biMap.lower_bound(address);\n    if (it != biMap.begin()) {\n        --it;\n    }\n\n    std::vector<RVA> addrs;\n    while (it != biMap.end() && it->first < address + size) {\n        addrs.push_back(it->first);\n        ++it;\n    }\n\n    // first and last entries may intersect, but not necessarily\n    // be contained in [address, address + size), so we need to\n    // check it and perhaps adjust their addresses.\n    std::vector<BasicInstruction> newInstructions;\n    if (!addrs.empty()) {\n        const BasicInstruction &prev = biMap[addrs.front()];\n        if (prev.address < address && prev.address + prev.size > address) {\n            newInstructions.push_back({ prev.address, address - prev.address, prev.color });\n        }\n\n        const BasicInstruction &next = biMap[addrs.back()];\n        if (next.address < address + size && next.address + next.size > address + size) {\n            const RVA offset = address + size - next.address;\n            newInstructions.push_back({ next.address + offset, next.size - offset, next.color });\n        }\n    }\n\n    for (RVA addr : addrs) {\n        const BasicInstruction &bi = biMap[addr];\n        if (std::max(bi.address, address) < std::min(bi.address + bi.size, address + size)) {\n            biMap.erase(addr);\n        }\n    }\n\n    for (BasicInstruction newInstr : newInstructions) {\n        biMap[newInstr.address] = newInstr;\n    }\n}\n\n/**\n * @brief Highlight the basic instruction at address\n */\nvoid BasicInstructionHighlighter::highlight(RVA address, RVA size, QColor color)\n{\n    clear(address, size);\n    biMap[address] = { address, size, color };\n}\n\n/**\n * @brief Return a highlighted basic instruction\n *\n * If there is nothing to highlight at specified address, returns nullptr\n */\nBasicInstruction *BasicInstructionHighlighter::getBasicInstruction(RVA address)\n{\n    BasicInstructionIt it = biMap.upper_bound(address);\n    if (it == biMap.begin()) {\n        return nullptr;\n    }\n\n    BasicInstruction *bi = &(--it)->second;\n    if (bi->address <= address && address < bi->address + bi->size) {\n        return bi;\n    }\n    return nullptr;\n}\n"
  },
  {
    "path": "src/common/BasicInstructionHighlighter.h",
    "content": "#ifndef BASICINSTRUCTIONHIGHLIGHTER_H\n#define BASICINSTRUCTIONHIGHLIGHTER_H\n\n#include \"CutterCommon.h\"\n#include <map>\n#include <QColor>\n\nstruct BasicInstruction\n{\n    RVA address;\n    RVA size;\n    QColor color;\n};\n\ntypedef std::map<RVA, BasicInstruction>::iterator BasicInstructionIt;\n\nclass BasicInstructionHighlighter\n{\npublic:\n    void clear(RVA address, RVA size);\n    void highlight(RVA address, RVA size, QColor color);\n    BasicInstruction *getBasicInstruction(RVA address);\n\nprivate:\n    std::map<RVA, BasicInstruction> biMap;\n};\n\n#endif // BASICINSTRUCTIONHIGHLIGHTER_H\n"
  },
  {
    "path": "src/common/BinaryTrees.h",
    "content": "#ifndef BINARY_TREES_H\n#define BINARY_TREES_H\n\n/** \\file BinaryTrees.h\n * \\brief Utilities to simplify creation of specialized augmented binary trees.\n */\n\n#include <vector>\n#include <cstdlib>\n#include <climits>\n#include <cstdint>\n#include <algorithm>\n\n/**\n * Not really a segment tree for storing segments as referred in academic literature. Can be\n * considered a full, almost perfect, augmented binary tree. In the context of competitive\n * programming often called segment tree.\n *\n * Child classes are expected to implement updateFromChildren(NodeType&parent, NodeType& left,\n * NodeType& right) method which calculates inner node values from children nodes.\n *\n * \\tparam NodeTypeT type of each tree element\n * \\tparam FinalType final child class used for curiously recurring template pattern\n */\ntemplate<class NodeTypeT, class FinalType>\nclass SegmentTreeBase\n{\npublic:\n    using NodePosition = size_t;\n    using NodeType = NodeTypeT;\n\n    /**\n     * @brief Create tree with \\a size leaves.\n     * @param size number of leaves in the tree\n     */\n    explicit SegmentTreeBase(size_t size) : size(size), nodeCount(2 * size), nodes(nodeCount) {}\n\n    /**\n     * @brief Create a tree with given size and initial value.\n     *\n     * Inner nodes are calculated from leaves.\n     * @param size number of leaves\n     * @param initialValue initial leave value\n     */\n    SegmentTreeBase(size_t size, const NodeType &initialValue) : SegmentTreeBase(size)\n    {\n        init(initialValue);\n    }\n\nprotected:\n    // Curiously recurring template pattern\n    FinalType &This() { return static_cast<FinalType &>(*this); }\n\n    // Curiously recurring template pattern\n    const FinalType &This() const { return static_cast<const FinalType &>(*this); }\n\n    size_t leavePositionToIndex(NodePosition pos) const { return pos - size; }\n\n    NodePosition leaveIndexToPosition(size_t index) const { return index + size; }\n\n    bool isLeave(NodePosition position) const { return position >= size; }\n\n    /**\n     * @brief Calculate inner node values from leaves.\n     */\n    void buildInnerNodes()\n    {\n        for (size_t i = size - 1; i > 0; i--) {\n            This().updateFromChildren(nodes[i], nodes[i << 1], nodes[(i << 1) | 1]);\n        }\n    }\n\n    /**\n     * @brief Initialize leaves with given value.\n     * @param value value that will be assigned to leaves\n     */\n    void init(const NodeType &value)\n    {\n        std::fill_n(nodes.begin() + size, size, value);\n        buildInnerNodes();\n    }\n\n    const size_t size; //< number of leaves and also index of left most leave\n    const size_t nodeCount;\n    std::vector<NodeType> nodes;\n};\n\n/**\n * \\brief Tree for point modification and range queries.\n */\ntemplate<class NodeType, class FinalType>\nclass PointSetSegmentTree : public SegmentTreeBase<NodeType, FinalType>\n{\n    using BaseType = SegmentTreeBase<NodeType, FinalType>;\n\npublic:\n    using BaseType::BaseType;\n\n    /**\n     * @brief Set leave \\a index to \\a value.\n     * @param index Leave index, should be in the range [0,size)\n     * @param value\n     */\n    void set(size_t index, const NodeType &value)\n    {\n        auto pos = this->leaveIndexToPosition(index);\n        this->nodes[pos] = value;\n        while (pos > 1) {\n            auto parrent = pos >> 1;\n            this->This().updateFromChildren(this->nodes[parrent], this->nodes[pos],\n                                            this->nodes[pos ^ 1]);\n            pos = parrent;\n        }\n    }\n\n    const NodeType &valueAtPoint(size_t index) const\n    {\n        return this->nodes[this->leaveIndexToPosition(index)];\n    }\n\n    // Implement range query when necessary\n};\n\nclass PointSetMinTree : public PointSetSegmentTree<int, PointSetMinTree>\n{\n    using BaseType = PointSetSegmentTree<int, PointSetMinTree>;\n\npublic:\n    using NodeType = int;\n\n    using BaseType::BaseType;\n\n    void updateFromChildren(NodeType &parent, NodeType &leftChild, NodeType &rightChild)\n    {\n        parent = std::min(leftChild, rightChild);\n    }\n\n    /**\n     * @brief Find right most position with value than less than given in range [0; position].\n     * @param position inclusive right side of query range\n     * @param value search for position less than this\n     * @return returns the position with searched property or -1 if there is no such position.\n     */\n    int rightMostLessThan(size_t position, int value)\n    {\n        auto isGood = [&](size_t pos) { return nodes[pos] < value; };\n        // right side exclusive range [l;r)\n        size_t goodSubtree = 0;\n        for (size_t l = leaveIndexToPosition(0), r = leaveIndexToPosition(position + 1); l < r;\n             l >>= 1, r >>= 1) {\n            if (l & 1) {\n                if (isGood(l)) {\n                    // mark subtree as good but don't stop yet, there might be something good\n                    // further to the right\n                    goodSubtree = l;\n                }\n                ++l;\n            }\n            if (r & 1) {\n                --r;\n                if (isGood(r)) {\n                    goodSubtree = r;\n                    break;\n                }\n            }\n        }\n        if (!goodSubtree) {\n            return -1;\n        }\n        // find rightmost good leave\n        while (goodSubtree < size) {\n            goodSubtree = (goodSubtree << 1) + 1;\n            if (!isGood(goodSubtree)) {\n                goodSubtree ^= 1;\n            }\n        }\n        return leavePositionToIndex(goodSubtree);\n    }\n\n    /**\n     * @brief Find left most position with value less than \\a value in range [position; size).\n     * @param position inclusive left side of query range\n     * @param value search for position less than this\n     * @return returns the position with searched property or -1 if there is no such position.\n     */\n    int leftMostLessThan(size_t position, int value)\n    {\n        auto isGood = [&](size_t pos) { return nodes[pos] < value; };\n        // right side exclusive range [l;r)\n        size_t goodSubtree = 0;\n        for (size_t l = leaveIndexToPosition(position), r = leaveIndexToPosition(size); l < r;\n             l >>= 1, r >>= 1) {\n            if (l & 1) {\n                if (isGood(l)) {\n                    goodSubtree = l;\n                    break;\n                }\n                ++l;\n            }\n            if (r & 1) {\n                --r;\n                if (isGood(r)) {\n                    goodSubtree = r;\n                    // mark subtree as good but don't stop yet, there might be something good\n                    // further to the left\n                }\n            }\n        }\n        if (!goodSubtree) {\n            return -1;\n        }\n        // find leftmost good leave\n        while (goodSubtree < size) {\n            goodSubtree = (goodSubtree << 1);\n            if (!isGood(goodSubtree)) {\n                goodSubtree ^= 1;\n            }\n        }\n        return leavePositionToIndex(goodSubtree);\n    }\n};\n\n/**\n * \\brief Tree that supports lazily applying an operation to range.\n *\n * Each inner node has a promise value describing an operation that needs to be applied to\n * corresponding subtree.\n *\n * Child classes are expected to implement to pushDown(size_t nodePosition) method. Which applies\n * the applies the operation stored in \\a promise for nodePosition to the direct children nodes.\n *\n * \\tparam NodeType type of tree nodes\n * \\tparam PromiseType type describing operation that needs to be applied to subtree\n * \\tparam FinalType child class type for CRTP. See SegmentTreeBase\n */\ntemplate<class NodeType, class PromiseType, class FinalType>\nclass LazySegmentTreeBase : public SegmentTreeBase<NodeType, FinalType>\n{\n    using BaseType = SegmentTreeBase<NodeType, FinalType>;\n\npublic:\n    /**\n     * @param size Number of tree leaves.\n     * @param neutralPromise Promise value that doesn't modify tree nodes.\n     */\n    LazySegmentTreeBase(size_t size, const PromiseType &neutralPromise)\n        : BaseType(size), neutralPromiseElement(neutralPromise), promise(size, neutralPromise)\n    {\n        h = 0;\n        size_t v = size;\n        while (v) {\n            v >>= 1;\n            h++;\n        }\n    }\n\n    LazySegmentTreeBase(size_t size, NodeType value, PromiseType neutralPromise)\n        : LazySegmentTreeBase(size, neutralPromise)\n    {\n        this->init(value);\n    }\n\n    /**\n     * @brief Calculate the tree operation over the range [\\a l, \\a r)\n     * @param l inclusive range left side\n     * @param r exclusive range right side\n     * @param initialValue Initial value for aggregate operation.\n     * @return Tree operation calculated over the range.\n     */\n    NodeType rangeOperation(size_t l, size_t r, NodeType initialValue)\n    {\n        NodeType result = initialValue;\n        l = this->leaveIndexToPosition(l);\n        r = this->leaveIndexToPosition(r);\n        pushDownFromRoot(l);\n        pushDownFromRoot(r - 1);\n        for (; l < r; l >>= 1, r >>= 1) {\n            if (l & 1) {\n                This().updateFromChildren(result, result, this->nodes[l++]);\n            }\n            if (r & 1) {\n                This().updateFromChildren(result, result, this->nodes[--r]);\n            }\n        }\n        return result;\n    }\n\nprotected:\n    /**\n     * @brief Ensure that all the parents of node \\a p have the operation applied.\n     * @param p Node position\n     */\n    void pushDownFromRoot(typename BaseType::NodePosition p)\n    {\n        for (size_t i = h; i > 0; i--) {\n            This().pushDown(p >> i);\n        }\n    }\n\n    /**\n     * @brief Update all the inner nodes in path from \\a p to root.\n     * @param p node position\n     */\n    void updateUntilRoot(typename BaseType::NodePosition p)\n    {\n        while (p > 1) {\n            auto parent = p >> 1;\n            if (promise[parent] == neutralPromiseElement) {\n                This().updateFromChildren(this->nodes[parent], this->nodes[p & ~size_t(1)],\n                                          this->nodes[p | 1]);\n            }\n            p = parent;\n        }\n    }\n\n    using BaseType::This;\n\n    int h; //< Tree height\n    const PromiseType neutralPromiseElement;\n    std::vector<PromiseType> promise;\n};\n\n/**\n * @brief Structure supporting range assignment and range maximum operations.\n */\nclass RangeAssignMaxTree : public LazySegmentTreeBase<int, uint8_t, RangeAssignMaxTree>\n{\n    using BaseType = LazySegmentTreeBase<int, uint8_t, RangeAssignMaxTree>;\n\npublic:\n    using ValueType = int;\n    RangeAssignMaxTree(size_t size, ValueType initialValue) : BaseType(size, initialValue, 0) {}\n\n    void updateFromChildren(NodeType &parent, const NodeType &left, const NodeType &right)\n    {\n        parent = std::max(left, right);\n    }\n\n    void pushDown(size_t parent)\n    {\n        if (promise[parent]) {\n            size_t left = (parent << 1);\n            size_t right = (parent << 1) | 1;\n            nodes[left] = nodes[right] = nodes[parent];\n            if (left < size) {\n                promise[left] = promise[parent];\n            }\n            if (right < size) {\n                promise[right] = promise[parent];\n            }\n            promise[parent] = neutralPromiseElement;\n        }\n    }\n\n    /**\n     * @brief Change all the elements in range [\\a left, \\a right) to \\a value.\n     * @param left inclusive range left side\n     * @param right exclusive right side of range\n     * @param value value to be assigned\n     */\n    void setRange(size_t left, size_t right, NodeType value)\n    {\n        left = leaveIndexToPosition(left);\n        right = leaveIndexToPosition(right);\n        pushDownFromRoot(left);\n        pushDownFromRoot(right - 1);\n        for (size_t l = left, r = right; l < r; l >>= 1, r >>= 1) {\n            if (l & 1) {\n                nodes[l] = value;\n                if (!isLeave(l)) {\n                    promise[l] = 1;\n                }\n                l += 1;\n            }\n            if (r & 1) {\n                r -= 1;\n                nodes[r] = value;\n                if (!isLeave(r)) {\n                    promise[r] = 1;\n                }\n            }\n        }\n        updateUntilRoot(left);\n        updateUntilRoot(right - 1);\n    }\n\n    /**\n     * @brief Calculate biggest value in the range [l, r)\n     * @param l inclusive left side of range\n     * @param r exclusive right side of range\n     * @return biggest value in given range\n     */\n    int rangeMaximum(size_t l, size_t r)\n    {\n        return rangeOperation(l, r, std::numeric_limits<ValueType>::min());\n    }\n};\n\n/**\n * @brief Structure for keeping track of minimum and maximum value set at each position.\n *\n * Supports range update and range query.\n *\n * Example:\n *  @code{.cpp}\n *  MinMaxAccumulateTree t(30); // operate within range [0; 30)\n *  t.updateRange(1, 5, 10);\n *  rangeMinMax(0, 20);// -> {10, 10}\n *  t.updateRange(4, 6, 15);\n *  t.updateRange(3, 10, 20);\n *  t.rangeMinMax(0, 20); // -> {10, 20}\n *  t.rangeMinMax(1, 3); // -> {10, 10}\n *  t.rangeMinMax(5, 8); // -> {15, 20}\n *  @endcode\n */\ntemplate<class IntegerType>\nclass MinMaxAccumulateTree : public LazySegmentTreeBase<std::pair<IntegerType, IntegerType>,\n                                                        std::pair<IntegerType, IntegerType>,\n                                                        MinMaxAccumulateTree<IntegerType>>\n{\n    // Could work with other types but that would require changing LIMITS\n    static_assert(std::is_integral<IntegerType>::value,\n                  \"Template argument IntegerType must be integer\");\n    using MinMax = std::pair<IntegerType, IntegerType>;\n    using ValueType = MinMax;\n    using ThisType = MinMaxAccumulateTree<IntegerType>;\n    using BaseType = LazySegmentTreeBase<ValueType, MinMax, ThisType>;\n    using NodeType = typename BaseType::NodeType;\n    using NodePosition = typename BaseType::NodePosition;\n\n    static constexpr MinMax LIMITS()\n    {\n        return { std::numeric_limits<IntegerType>::max(), std::numeric_limits<IntegerType>::min() };\n    }\n\n    static MinMax Combine(const MinMax &a, const MinMax &b)\n    {\n        return { std::min(a.first, b.first), std::max(a.second, b.second) };\n    }\n\n    void UpdateNode(NodePosition nodePos, ValueType value)\n    {\n        this->nodes[nodePos] = Combine(this->nodes[nodePos], value);\n        if (!this->isLeave(nodePos)) {\n            this->promise[nodePos] = Combine(this->promise[nodePos], value);\n        }\n    }\n\npublic:\n    MinMaxAccumulateTree(size_t size, ValueType initialValue = LIMITS())\n        : BaseType(size, initialValue, LIMITS())\n    {\n    }\n\n    void updateFromChildren(NodeType &parent, const NodeType &left, const NodeType &right)\n    {\n        parent = Combine(left, right);\n    }\n\n    void pushDown(NodePosition parent)\n    {\n        size_t left = (parent << 1);\n        size_t right = (parent << 1) | 1;\n        this->UpdateNode(left, this->promise[parent]);\n        this->UpdateNode(right, this->promise[parent]);\n        this->promise[parent] = this->neutralPromiseElement;\n    }\n\n    /**\n     * @brief Update min and max values in the range [\\a left, \\a right) with number \\a value.\n     * @param left inclusive range left side\n     * @param right exclusive right side of range\n     * @param value number to be used for updating minimum and maximum\n     */\n    void updateRange(size_t left, size_t right, IntegerType value)\n    {\n        left = this->leaveIndexToPosition(left);\n        right = this->leaveIndexToPosition(right);\n        this->pushDownFromRoot(left);\n        this->pushDownFromRoot(right - 1);\n        MinMax pairValue { value, value };\n        for (size_t l = left, r = right; l < r; l >>= 1, r >>= 1) {\n            if (l & 1) {\n                UpdateNode(l, pairValue);\n                l += 1;\n            }\n            if (r & 1) {\n                r -= 1;\n                UpdateNode(r, pairValue);\n            }\n        }\n        this->updateUntilRoot(left);\n        this->updateUntilRoot(right - 1);\n    }\n\n    /**\n     * @brief Calculate minimum and maximum value in the range [l, r)\n     * @param l inclusive left side of range\n     * @param r exclusive right side of range\n     * @return std::pair {min, max}\n     */\n    MinMax rangeMinMax(size_t l, size_t r)\n    {\n        return this->rangeOperation(l, r, this->neutralPromiseElement);\n    }\n};\n\n#endif // BINARY_TREES_H\n"
  },
  {
    "path": "src/common/BugReporting.cpp",
    "content": "#include \"BugReporting.h\"\n\n#include \"Cutter.h\"\n#include <QUrl>\n#include <QJsonObject>\n#include \"CutterConfig.h\"\n#include <QDesktopServices>\n\nvoid openIssue()\n{\n    RzCoreLocked core(Core());\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    RzBinObject *bobj = rz_bin_cur_object(core->bin);\n    const RzBinInfo *info = bobj ? rz_bin_object_get_info(bobj) : nullptr;\n    RzBinPlugin *plugin = rz_bin_file_cur_plugin(bf);\n\n    QString url, osInfo, format, arch, type;\n    // Pull in info needed for git issue\n    osInfo = QSysInfo::productType() + \" \"\n            + (QSysInfo::productVersion() == \"unknown\" ? \"\" : QSysInfo::productVersion());\n    format = plugin && RZ_STR_ISNOTEMPTY(plugin->name) ? plugin->name : \"N/A\";\n    arch = info && RZ_STR_ISNOTEMPTY(info->arch) ? info->arch : \"N/A\";\n    type = info && RZ_STR_ISNOTEMPTY(info->type) ? info->type : \"N/A\";\n    url = \"https://github.com/rizinorg/cutter/issues/new?&body=**Environment information**\\n* \"\n          \"Operating System: \"\n            + osInfo + \"\\n* Cutter version: \" + CUTTER_VERSION_FULL + \"\\n* Obtained from:\\n\"\n            + \"  - [ ] Built from source\\n  - [ ] Downloaded release from Cutter website or GitHub \"\n              \"\\n\"\n              \"  - [ ] Distribution repository\\n* File format: \"\n            + format + \"\\n * Arch: \" + arch + \"\\n * Type: \" + type\n            + \"\\n\\n**Describe the bug**\\n\\n<!-- A clear and concise description of what the bug \"\n              \"is. -->\"\n              \"\\n\\n**To Reproduce**\\n\\n\"\n              \"Steps to reproduce the behavior:\\n1. Go to '...'\\n2. Click on '....'\\n3. Scroll \"\n              \"down to '....'\\n\"\n              \"4. See error\\n\\n**Expected behavior**\\n\\n\"\n              \"<!-- A clear and concise description of what you expected to happen. -->\\n\\n\\n\"\n              \"**Screenshots**\\n\\n<!-- If applicable, add screenshots to help explain your \"\n              \"problem. -->\\n\\n\\n\"\n              \"**Additional context**\\n\\n<!-- Add any other context about the problem here. -->\\n\";\n\n    QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));\n}\n"
  },
  {
    "path": "src/common/BugReporting.h",
    "content": "#ifndef CRASHREPORTING_H\n#define CRASHREPORTING_H\n\n/**\n * @brief Opens issue on Cutter's github page\n * with current file and system information.\n */\nvoid openIssue();\n\n#endif // CRASHREPORTING_H\n"
  },
  {
    "path": "src/common/CachedFontMetrics.h",
    "content": "#ifndef CACHEDFONTMETRICS_H\n#define CACHEDFONTMETRICS_H\n\n#include \"common/Metrics.h\"\n\n#include <QObject>\n#include <QFont>\n#include <QFontMetrics>\n\ntemplate<typename T>\nclass CachedFontMetrics\n{\npublic:\n    explicit CachedFontMetrics(const QFont &font) : mFontMetrics(font)\n    {\n        memset(mWidths, 0, sizeof(mWidths));\n        mHeight = mFontMetrics.height();\n    }\n\n    T width(const QChar &ch)\n    {\n        // return mFontMetrics.width(ch);\n        auto unicode = ch.unicode();\n        if (unicode >= 0xD800) {\n            if (unicode >= 0xE000)\n                unicode -= 0xE000 - 0xD800;\n            else\n                // is lonely surrogate\n                return fetchWidth(ch);\n        }\n        if (!mWidths[unicode])\n            return mWidths[unicode] = fetchWidth(ch);\n        return mWidths[unicode];\n    }\n\n    T width(const QString &text)\n    {\n        T result = 0;\n        QChar temp;\n        for (const QChar &ch : text) {\n            if (ch.isHighSurrogate())\n                temp = ch;\n            else if (ch.isLowSurrogate())\n                result += fetchWidth(QString(temp) + ch);\n            else\n                result += width(ch);\n        }\n        return result;\n    }\n\n    T height() { return mHeight; }\n\n    T position(const QString &text, T offset)\n    {\n        T curWidth = 0;\n        QChar temp;\n\n        for (int i = 0; i < text.length(); i++) {\n            QChar ch = text[i];\n\n            if (ch.isHighSurrogate())\n                temp = ch;\n            else if (ch.isLowSurrogate())\n                curWidth += fetchWidth(QString(temp) + ch);\n            else\n                curWidth += width(ch);\n\n            if (curWidth >= offset) {\n                return i;\n            }\n        }\n\n        return -1;\n    }\n\nprivate:\n    typename Metrics<T>::FontMetrics mFontMetrics;\n    T mWidths[0x10000 - 0xE000 + 0xD800];\n    T mHeight;\n\n    T fetchWidth(QChar c)\n    {\n#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)\n        return mFontMetrics.width(c);\n#else\n        return mFontMetrics.horizontalAdvance(c);\n#endif\n    }\n\n    T fetchWidth(const QString &s)\n    {\n#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)\n        return mFontMetrics.width(s);\n#else\n        return mFontMetrics.horizontalAdvance(s);\n#endif\n    }\n};\n\n#endif // CACHEDFONTMETRICS_H\n"
  },
  {
    "path": "src/common/ColorThemeWorker.cpp",
    "content": "#include \"ColorThemeWorker.h\"\n\n#include <QDir>\n#include <QFile>\n#include <QColor>\n#include <QJsonArray>\n#include <QStandardPaths>\n#include <QRegularExpression>\n#include <rz_util/rz_path.h>\n\n#include \"common/Configuration.h\"\n\nconst QStringList ColorThemeWorker::cutterSpecificOptions = {\n    \"wordHighlightBg\",       \"wordHighlightFg\",\n    \"lineHighlight\",         \"searchCurrent\",\n    \"searchHighlight\",       \"gui.main\",\n    \"gui.imports\",           \"highlightPC\",\n    \"gui.navbar.err\",        \"gui.navbar.seek\",\n    \"gui.navbar.pc\",         \"gui.navbar.sym\",\n    \"gui.dataoffset\",        \"gui.navbar.code\",\n    \"gui.navbar.unexplored\", \"gui.navbar.str\",\n    \"gui.navbar.import\",     \"gui.navbar.signature\",\n    \"gui.navbar.data\",       \"gui.breakpoint_background\",\n    \"gui.overview.node\",     \"gui.overview.fill\",\n    \"gui.overview.border\",   \"gui.border\",\n    \"gui.background\",        \"gui.alt_background\",\n    \"gui.disass_selected\",\n};\n\nconst QStringList ColorThemeWorker::rizinUnusedOptions = {\n    \"linehl\",     \"wordhl\",        \"graph.box\",  \"graph.box2\", \"graph.box3\",\n    \"graph.box4\", \"graph.current\", \"graph.box2\", \"widget_sel\", \"widget_bg\",\n    \"label\",      \"ai.write\",      \"invalid\",    \"ai.seq\",     \"args\",\n    \"ai.read\",    \"ai.exec\",       \"ai.ascii\",   \"prompt\",     \"graph.traced\"\n};\n\nColorThemeWorker::ColorThemeWorker(QObject *parent) : QObject(parent)\n{\n    char *szThemes = rz_path_home_prefix(RZ_THEMES);\n    customRzThemesLocationPath = szThemes;\n    rz_mem_free(szThemes);\n    if (!QDir(customRzThemesLocationPath).exists()) {\n        QDir().mkpath(customRzThemesLocationPath);\n    }\n\n    RzPath *sys_path = rz_path_new();\n    const char *theme_dir = rz_path_system(sys_path, RZ_THEMES);\n    QDir currDir { theme_dir };\n    if (currDir.exists()) {\n        standardRzThemesLocationPath = currDir.absolutePath();\n    } else {\n        QMessageBox::critical(nullptr, tr(\"Standard themes not found\"),\n                              tr(\"The Rizin standard themes could not be found in '%1'. \"\n                                 \"Most likely, Rizin is not properly installed.\")\n                                      .arg(currDir.path()));\n    }\n    rz_path_free(sys_path);\n}\n\nQColor ColorThemeWorker::mergeColors(const QColor &upper, const QColor &lower) const\n{\n    qhelpers::ColorFloat r1, g1, b1, a1;\n    qhelpers::ColorFloat r2, g2, b2, a2;\n    qhelpers::ColorFloat r, g, b, a;\n\n    upper.getRgbF(&r1, &g1, &b1, &a1);\n    lower.getRgbF(&r2, &g2, &b2, &a2);\n\n    a = (1.0 - a1) * a2 + a1;\n    r = ((1.0 - a1) * a2 * r2 + a1 * r1) / a;\n    g = ((1.0 - a1) * a2 * g2 + a1 * g1) / a;\n    b = ((1.0 - a1) * a2 * b2 + a1 * b1) / a;\n\n    QColor res;\n    res.setRgbF(r, g, b, a);\n    return res;\n}\n\nQString ColorThemeWorker::copy(const QString &srcThemeName, const QString &copyThemeName) const\n{\n    if (!isThemeExist(srcThemeName)) {\n        return tr(\"Theme <b>%1</b> does not exist.\").arg(srcThemeName);\n    }\n\n    return save(getTheme(srcThemeName), copyThemeName);\n}\n\nQString ColorThemeWorker::save(const Theme &theme, const QString &themeName) const\n{\n    QFile fOut(QDir(customRzThemesLocationPath).filePath(themeName));\n    if (!fOut.open(QFile::WriteOnly | QFile::Truncate)) {\n        return tr(\"The file <b>%1</b> cannot be opened.\").arg(QFileInfo(fOut).filePath());\n    }\n\n    for (auto it = theme.constBegin(); it != theme.constEnd(); it++) {\n        const QColor &color = it.value();\n        if (cutterSpecificOptions.contains(it.key())) {\n            fOut.write(QString(\"#~%1 rgb:%2\\n\")\n                               .arg(it.key(), color.name(QColor::HexArgb).remove('#'))\n                               .toUtf8());\n        } else {\n            fOut.write(QString(\"ec %1 rgb:%2\\n\")\n                               .arg(it.key(), color.name(QColor::HexRgb).remove('#'))\n                               .toUtf8());\n        }\n    }\n\n    fOut.close();\n    return \"\";\n}\n\nbool ColorThemeWorker::isCustomTheme(const QString &themeName) const\n{\n    return QFile::exists(QDir(customRzThemesLocationPath).filePath(themeName));\n}\n\nbool ColorThemeWorker::isThemeExist(const QString &name) const\n{\n    QStringList themes = Core()->getColorThemes();\n    return themes.contains(name);\n}\n\nColorThemeWorker::Theme ColorThemeWorker::getTheme(const QString &themeName) const\n{\n    Theme theme;\n    QString curr = Config()->getColorTheme();\n\n    if (themeName != curr) {\n        RzCoreLocked core(Core());\n        rz_core_theme_load(core, themeName.toUtf8().constData());\n        theme = Core()->getTheme();\n        rz_core_theme_load(core, curr.toUtf8().constData());\n    } else {\n        theme = Core()->getTheme();\n    }\n\n    ColorFlags colorFlags = ColorFlags::DarkFlag;\n    if (Configuration::relevantThemes.contains(themeName)) {\n        colorFlags = Configuration::relevantThemes[themeName];\n    }\n    if (colorFlags == DualColor) {\n        colorFlags = Config()->windowColorIsDark() ? DarkFlag : LightFlag;\n    }\n\n    for (auto &it : cutterSpecificOptions) {\n        theme.insert(it, QColor(Configuration::cutterOptionColors[it][colorFlags]));\n    }\n\n    if (isCustomTheme(themeName)) {\n        QFile src(QDir(customRzThemesLocationPath).filePath(themeName));\n        if (!src.open(QFile::ReadOnly)) {\n            return {};\n        }\n        QStringList sl;\n        for (auto &line : QString(src.readAll()).split('\\n', CUTTER_QT_SKIP_EMPTY_PARTS)) {\n            sl = line.replace(\"#~\", \"ec \")\n                         .replace(\"rgb:\", \"#\")\n                         .split(' ', CUTTER_QT_SKIP_EMPTY_PARTS);\n            if (sl.size() != 3 || sl[0][0] == '#') {\n                continue;\n            }\n            theme.insert(sl[1], QColor(sl[2]));\n        }\n    }\n\n    for (auto &key : rizinUnusedOptions) {\n        theme.remove(key);\n    }\n\n    return theme;\n}\n\nQString ColorThemeWorker::deleteTheme(const QString &themeName) const\n{\n    if (!isCustomTheme(themeName)) {\n        return tr(\"You can not delete standard Rizin color themes.\");\n    }\n    if (!isThemeExist(themeName)) {\n        return tr(\"Theme <b>%1</b> does not exist.\").arg(themeName);\n    }\n\n    QFile file(QDir(customRzThemesLocationPath).filePath(themeName));\n    if (file.isWritable()) {\n        return tr(\"You have no permission to write to <b>%1</b>\").arg(QFileInfo(file).filePath());\n    }\n    if (!file.open(QFile::ReadOnly)) {\n        return tr(\"File <b>%1</b> can not be opened.\").arg(QFileInfo(file).filePath());\n    }\n    if (!file.remove()) {\n        return tr(\"File <b>%1</b> can not be removed.\").arg(QFileInfo(file).filePath());\n    }\n    return \"\";\n}\n\nQString ColorThemeWorker::importTheme(const QString &file) const\n{\n    QFileInfo src(file);\n    if (!src.exists()) {\n        return tr(\"File <b>%1</b> does not exist.\").arg(file);\n    }\n\n    bool ok;\n    bool isTheme = isFileTheme(file, &ok);\n    if (!ok) {\n        return tr(\"File <b>%1</b> could not be opened. \"\n                  \"Please make sure you have access to it and try again.\")\n                .arg(file);\n    } else if (!isTheme) {\n        return tr(\"File <b>%1</b> is not a Cutter color theme\").arg(file);\n    }\n\n    QString name = src.fileName();\n    if (isThemeExist(name)) {\n        return tr(\"A color theme named <b>%1</b> already exists.\").arg(name);\n    }\n\n    if (QFile::copy(file, QDir(customRzThemesLocationPath).filePath(name))) {\n        return \"\";\n    } else {\n        return tr(\"Error occurred during importing. \"\n                  \"Please make sure you have an access to \"\n                  \"the directory <b>%1</b> and try again.\")\n                .arg(src.dir().path());\n    }\n}\n\nQString ColorThemeWorker::renameTheme(const QString &themeName, const QString &newName) const\n{\n    if (isThemeExist(newName)) {\n        return tr(\"A color theme named <b>\\\"%1\\\"</b> already exists.\").arg(newName);\n    }\n\n    if (!isCustomTheme(themeName)) {\n        return tr(\"You can not rename standard Rizin themes.\");\n    }\n\n    QDir dir = customRzThemesLocationPath;\n    bool ok = QFile::rename(dir.filePath(themeName), dir.filePath(newName));\n    if (!ok) {\n        return tr(\"Something went wrong during renaming. \"\n                  \"Please make sure you have access to the directory <b>\\\"%1\\\"</b>.\")\n                .arg(dir.path());\n    }\n    return \"\";\n}\n\nbool ColorThemeWorker::isFileTheme(const QString &filePath, bool *ok) const\n{\n    QFile f(filePath);\n    if (!f.open(QFile::ReadOnly)) {\n        *ok = false;\n        return false;\n    }\n\n    const QString colors = \"black|red|white|green|magenta|yellow|cyan|blue|gray|none\";\n    QString options =\n            (Core()->getThemeKeys() << cutterSpecificOptions).join('|').replace(\".\", \"\\\\.\");\n\n    QString pattern =\n            QString(\"((ec\\\\s+(%1)\\\\s+(((rgb:|#)[0-9a-fA-F]{3,8})|(%2))))\\\\s*\").arg(options, colors);\n    // The below construct mimics the behaviour of QRegexP::exactMatch(), which was here before\n    QRegularExpression regexp(\"\\\\A(?:\" + pattern + \")\\\\z\");\n\n    for (auto &line : QString(f.readAll()).split('\\n', CUTTER_QT_SKIP_EMPTY_PARTS)) {\n        line.replace(\"#~\", \"ec \");\n        if (!line.isEmpty() && !regexp.match(line).hasMatch()) {\n            *ok = true;\n            return false;\n        }\n    }\n\n    *ok = true;\n    return true;\n}\n\nQStringList ColorThemeWorker::customThemes() const\n{\n    QStringList themes = Core()->getColorThemes();\n    QStringList ret;\n    for (int i = 0; i < themes.size(); i++) {\n        if (isCustomTheme(themes[i])) {\n            ret.push_back(themes[i]);\n        }\n    }\n    return ret;\n}\n\nconst QStringList &ColorThemeWorker::getRizinSpecificOptions()\n{\n    if (rizinSpecificOptions.isEmpty()) {\n        rizinSpecificOptions << Core()->getThemeKeys();\n    }\n    return rizinSpecificOptions;\n}\n"
  },
  {
    "path": "src/common/ColorThemeWorker.h",
    "content": "#ifndef COLORTHEMEWORKER_H\n#define COLORTHEMEWORKER_H\n\n#include <QFile>\n#include <QColor>\n#include <QObject>\n#include \"Cutter.h\"\n#include <QJsonDocument>\n#include <QJsonObject>\n\n#define ThemeWorker() (ColorThemeWorker::instance())\n\n/**\n * @brief The ColorThemeWorker class is a singletone that provides API for working with\n * color themes.\n */\nclass ColorThemeWorker : public QObject\n{\n    Q_OBJECT\npublic:\n    typedef QHash<QString, QColor> Theme;\n\n    /**\n     * @brief cutterSpecificOptions is list of all available Cutter-only color options.\n     */\n    static const QStringList cutterSpecificOptions;\n\n    /**\n     * @brief rizinUnusedOptions is a list of all Rizin options that Cutter does not use.\n     */\n    static const QStringList rizinUnusedOptions;\n\n    static ColorThemeWorker &instance()\n    {\n        static ColorThemeWorker ex;\n        return ex;\n    }\n\n    virtual ~ColorThemeWorker() {}\n\n    /**\n     * @brief Copies @a srcThemeName with name @a copyThemeName.\n     * @param srcThemeName\n     * Name of theme to be copied.\n     * @param copyThemeName\n     * Name of copy.\n     * @return \"\" on success or error message.\n     */\n    QString copy(const QString &srcThemeName, const QString &copyThemeName) const;\n\n    /**\n     * @brief Saves @a theme as @a themeName theme.\n     * @param theme\n     * Theme to be saved.\n     * @param themeName\n     * Name of theme to save.\n     * @return \"\" on success or error message.\n     */\n    QString save(const Theme &theme, const QString &themeName) const;\n\n    /**\n     * @brief Returns whether or not @a themeName theme is custom (created by user or imported) or\n     * not.\n     * @param themeName\n     * Name of theme to check.\n     */\n    bool isCustomTheme(const QString &themeName) const;\n\n    /**\n     * @brief Returns whether or not @a name theme already exists.\n     * @return true if theme exists, false - if not.\n     */\n    bool isThemeExist(const QString &name) const;\n\n    /**\n     * @brief Returns theme as QHash where key is option name and value is QColor.\n     * @param themeName\n     * Theme to get.\n     */\n    Theme getTheme(const QString &themeName) const;\n\n    /**\n     * @brief Deletes theme named @a themeName.\n     * @param themeName\n     * Name of theme to be removed.\n     * @return \"\" on success or error message.\n     */\n    QString deleteTheme(const QString &themeName) const;\n\n    /**\n     * @brief Imports theme from @a file.\n     * @return \"\" on success or error message.\n     */\n    QString importTheme(const QString &file) const;\n\n    /**\n     * @brief Renames theme from @a themeName to @a newName.\n     * @return \"\" on success or error message.\n     */\n    QString renameTheme(const QString &themeName, const QString &newName) const;\n\n    /**\n     * @brief Returns whether or not file at @a filePath is a color theme.\n     * @param filePath\n     * Path to file to check.\n     * @param ok\n     * Output parameter. Indicates wheter or not check was successful.\n     * @return true if given file is color theme and ok == true, otherwise returns false.\n     */\n    bool isFileTheme(const QString &filePath, bool *ok) const;\n\n    /**\n     * @brief Returns list of all custom themes.\n     */\n    QStringList customThemes() const;\n\n    QString getStandardThemesPath() { return standardRzThemesLocationPath; }\n    QString getCustomThemesPath() { return customRzThemesLocationPath; }\n\n    const QStringList &getRizinSpecificOptions();\n\nprivate:\n    /**\n     * @brief list of all available Rizin-only color options.\n     */\n    QStringList rizinSpecificOptions;\n\n    QString standardRzThemesLocationPath;\n    QString customRzThemesLocationPath;\n\n    ColorThemeWorker(QObject *parent = nullptr);\n    ColorThemeWorker(const ColorThemeWorker &root) = delete;\n    ColorThemeWorker &operator=(const ColorThemeWorker &) = delete;\n\n    QColor mergeColors(const QColor &upper, const QColor &lower) const;\n};\n\n#endif // COLORTHEMEWORKER_H\n"
  },
  {
    "path": "src/common/Colors.cpp",
    "content": "#include \"Colors.h\"\n#include \"common/Configuration.h\"\n\nColors::Colors() {}\n\nvoid Colors::colorizeAssembly(RichTextPainter::List &list, QString opcode, ut64 type_num)\n{\n    RichTextPainter::CustomRichText_t assembly;\n    assembly.highlight = false;\n    assembly.flags = RichTextPainter::FlagColor;\n\n    // TODO cut opcode and use op[\"ptr\"] to colorate registers and immediate values\n    assembly.text = opcode;\n\n    QString colorName = Colors::getColor(type_num);\n    assembly.textColor = ConfigColor(colorName);\n    list.push_back(assembly);\n}\n\n// Temporary solution\n// Copied from RZ_API const char* r_print_color_op_type(RPrint *p, ut64 analysis_type) {\nQString Colors::getColor(ut64 type)\n{\n    switch (type & RZ_ANALYSIS_OP_TYPE_MASK) {\n    case RZ_ANALYSIS_OP_TYPE_NOP:\n        return \"nop\";\n    case RZ_ANALYSIS_OP_TYPE_ADD:\n    case RZ_ANALYSIS_OP_TYPE_SUB:\n    case RZ_ANALYSIS_OP_TYPE_MUL:\n    case RZ_ANALYSIS_OP_TYPE_DIV:\n    case RZ_ANALYSIS_OP_TYPE_MOD:\n    case RZ_ANALYSIS_OP_TYPE_LENGTH:\n        return \"math\";\n    case RZ_ANALYSIS_OP_TYPE_AND:\n    case RZ_ANALYSIS_OP_TYPE_OR:\n    case RZ_ANALYSIS_OP_TYPE_XOR:\n    case RZ_ANALYSIS_OP_TYPE_NOT:\n    case RZ_ANALYSIS_OP_TYPE_SHL:\n    case RZ_ANALYSIS_OP_TYPE_SAL:\n    case RZ_ANALYSIS_OP_TYPE_SAR:\n    case RZ_ANALYSIS_OP_TYPE_SHR:\n    case RZ_ANALYSIS_OP_TYPE_ROL:\n    case RZ_ANALYSIS_OP_TYPE_ROR:\n    case RZ_ANALYSIS_OP_TYPE_CPL:\n        return \"bin\";\n    case RZ_ANALYSIS_OP_TYPE_IO:\n        return \"swi\";\n    case RZ_ANALYSIS_OP_TYPE_JMP:\n    case RZ_ANALYSIS_OP_TYPE_UJMP:\n    case RZ_ANALYSIS_OP_TYPE_IJMP:\n    case RZ_ANALYSIS_OP_TYPE_RJMP:\n    case RZ_ANALYSIS_OP_TYPE_IRJMP:\n    case RZ_ANALYSIS_OP_TYPE_MJMP:\n        return \"jmp\";\n    case RZ_ANALYSIS_OP_TYPE_CJMP:\n    case RZ_ANALYSIS_OP_TYPE_UCJMP:\n    case RZ_ANALYSIS_OP_TYPE_SWITCH:\n        return \"cjmp\";\n    case RZ_ANALYSIS_OP_TYPE_CMP:\n    case RZ_ANALYSIS_OP_TYPE_ACMP:\n        return \"cmp\";\n    case RZ_ANALYSIS_OP_TYPE_UCALL:\n    case RZ_ANALYSIS_OP_TYPE_ICALL:\n    case RZ_ANALYSIS_OP_TYPE_RCALL:\n    case RZ_ANALYSIS_OP_TYPE_IRCALL:\n    case RZ_ANALYSIS_OP_TYPE_UCCALL:\n    case RZ_ANALYSIS_OP_TYPE_CALL:\n    case RZ_ANALYSIS_OP_TYPE_CCALL:\n        return \"call\";\n    case RZ_ANALYSIS_OP_TYPE_NEW:\n    case RZ_ANALYSIS_OP_TYPE_SWI:\n        return \"swi\";\n    case RZ_ANALYSIS_OP_TYPE_ILL:\n    case RZ_ANALYSIS_OP_TYPE_TRAP:\n        return \"trap\";\n    case RZ_ANALYSIS_OP_TYPE_CRET:\n    case RZ_ANALYSIS_OP_TYPE_RET:\n        return \"ret\";\n    case RZ_ANALYSIS_OP_TYPE_CAST:\n    case RZ_ANALYSIS_OP_TYPE_MOV:\n    case RZ_ANALYSIS_OP_TYPE_LEA:\n    case RZ_ANALYSIS_OP_TYPE_CMOV: // TODO: add cmov cathegory?\n        return \"mov\";\n    case RZ_ANALYSIS_OP_TYPE_PUSH:\n    case RZ_ANALYSIS_OP_TYPE_UPUSH:\n    case RZ_ANALYSIS_OP_TYPE_LOAD:\n        return \"push\";\n    case RZ_ANALYSIS_OP_TYPE_POP:\n    case RZ_ANALYSIS_OP_TYPE_STORE:\n        return \"pop\";\n    case RZ_ANALYSIS_OP_TYPE_CRYPTO:\n        return \"crypto\";\n    case RZ_ANALYSIS_OP_TYPE_NULL:\n        return \"other\";\n    case RZ_ANALYSIS_OP_TYPE_UNK:\n    default:\n        return \"invalid\";\n    }\n}\n\nQColor Colors::overlayColor(const QColor &base, const QColor &overlay)\n{\n    int alpha = overlay.alpha();\n    if (alpha == 255) {\n        return overlay;\n    }\n    if (alpha == 0) {\n        return base;\n    }\n\n    int r = (overlay.red() * alpha + base.red() * (255 - alpha)) / 255;\n    int g = (overlay.green() * alpha + base.green() * (255 - alpha)) / 255;\n    int b = (overlay.blue() * alpha + base.blue() * (255 - alpha)) / 255;\n\n    return QColor(r, g, b);\n}\n"
  },
  {
    "path": "src/common/Colors.h",
    "content": "#ifndef COLORS_H\n#define COLORS_H\n\n#include \"core/Cutter.h\"\n#include \"common/RichTextPainter.h\"\n#include <rz_analysis.h>\n\nclass Colors\n{\npublic:\n    Colors();\n    static void colorizeAssembly(RichTextPainter::List &list, QString opcode, ut64 type_num);\n    static QString getColor(ut64 type);\n    /**\n     * @brief Blends an overlay color onto an existing base color\n     * @param base The original color (assumes base color is solid)\n     * @param overlay The overlay color with alpha transparency\n     * @return The resulting blended color\n     */\n    static QColor overlayColor(const QColor &base, const QColor &overlay);\n};\n\n#endif // COLORS_H\n"
  },
  {
    "path": "src/common/CommandTask.cpp",
    "content": "\n#include \"CommandTask.h\"\n#include \"TempConfig.h\"\n\nCommandTask::CommandTask(const QString &cmd, ColorMode colorMode) : cmd(cmd), colorMode(colorMode)\n{\n}\n\nvoid CommandTask::runTask()\n{\n    TempConfig tempConfig;\n    tempConfig.set(\"scr.color\", colorMode);\n    auto res = Core()->cmdTask(cmd);\n    emit finished(res);\n}\n"
  },
  {
    "path": "src/common/CommandTask.h",
    "content": "\n#ifndef COMMANDTASK_H\n#define COMMANDTASK_H\n\n#include \"common/AsyncTask.h\"\n#include \"core/Cutter.h\"\n\nclass CUTTER_EXPORT CommandTask : public AsyncTask\n{\n    Q_OBJECT\n\npublic:\n    enum ColorMode {\n        DISABLED = COLOR_MODE_DISABLED,\n        MODE_16 = COLOR_MODE_16,\n        MODE_256 = COLOR_MODE_256,\n        MODE_16M = COLOR_MODE_16M\n    };\n\n    CommandTask(const QString &cmd, ColorMode colorMode = ColorMode::DISABLED);\n\n    QString getTitle() override { return tr(\"Running Command\"); }\n\nsignals:\n    void finished(const QString &result);\n\nprotected:\n    void runTask() override;\n\nprivate:\n    QString cmd;\n    ColorMode colorMode;\n};\n\n#endif // COMMANDTASK_H\n"
  },
  {
    "path": "src/common/Configuration.cpp",
    "content": "#include \"Configuration.h\"\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QDir>\n#include <QFontDatabase>\n#include <QFile>\n#include <QApplication>\n#include <QHash>\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n#    include <KSyntaxHighlighting/Repository>\n#    include <KSyntaxHighlighting/Theme>\n#    include <KSyntaxHighlighting/Definition>\n#endif\n\n#include \"common/ColorThemeWorker.h\"\n#include \"common/SyntaxHighlighter.h\"\n#include \"common/ResourcePaths.h\"\n\n/* Map with names of themes associated with its color palette\n * (Dark or Light), so for dark interface themes will be shown only Dark color themes\n * and for light - only light ones.\n */\nconst QHash<QString, ColorFlags> Configuration::relevantThemes = {\n    { \"ayu\", DarkFlag },        { \"basic\", DarkFlag },   { \"behelit\", DarkFlag },\n    { \"bold\", DarkFlag },       { \"bright\", DarkFlag },  { \"cga\", DarkFlag },\n    { \"consonance\", DarkFlag }, { \"darkda\", DarkFlag },  { \"default\", DarkFlag },\n    { \"defragger\", DarkFlag },  { \"focus\", DarkFlag },   { \"gb\", DarkFlag },\n    { \"gentoo\", DarkFlag },     { \"lima\", DarkFlag },    { \"mars\", DarkFlag },\n    { \"monokai\", DarkFlag },    { \"nord\", DarkFlag },    { \"ogray\", DarkFlag },\n    { \"onedark\", DarkFlag },    { \"pink\", DarkFlag },    { \"rasta\", DarkFlag },\n    { \"sepia\", DarkFlag },      { \"smyck\", DarkFlag },   { \"solarized\", DarkFlag },\n    { \"twilight\", DarkFlag },   { \"xvilka\", DarkFlag },  { \"zenburn\", DarkFlag },\n\n    { \"dark\", DualColor },      { \"durian\", DualColor }, { \"tango\", DualColor },\n    { \"white2\", DualColor },\n\n    { \"cutter\", LightFlag },    { \"matrix\", LightFlag }, { \"white\", LightFlag },\n};\nstatic const QString DEFAULT_LIGHT_COLOR_THEME = \"cutter\";\nstatic const QString DEFAULT_DARK_COLOR_THEME = \"ayu\";\n\nconst QHash<QString, QHash<ColorFlags, QColor>> Configuration::cutterOptionColors = {\n    { \"gui.cflow\",\n      { { DarkFlag, QColor(0xff, 0xff, 0xff) }, { LightFlag, QColor(0x00, 0x00, 0x00) } } },\n    { \"gui.dataoffset\",\n      { { DarkFlag, QColor(0xff, 0xff, 0xff) }, { LightFlag, QColor(0x00, 0x00, 0x00) } } },\n    { \"gui.imports\",\n      { { DarkFlag, QColor(0x32, 0x8c, 0xff) }, { LightFlag, QColor(0x32, 0x8c, 0xff) } } },\n    { \"gui.item_invalid\",\n      { { DarkFlag, QColor(0x9b, 0x9b, 0x9b) }, { LightFlag, QColor(0x9b, 0x9b, 0x9b) } } },\n    { \"gui.main\",\n      { { DarkFlag, QColor(0x21, 0xd8, 0x93) }, { LightFlag, QColor(0x00, 0x80, 0x00) } } },\n    { \"gui.flirt\",\n      { { DarkFlag, QColor(0xd8, 0xbb, 0x21) }, { LightFlag, QColor(0xf1, 0xc4, 0x0f) } } },\n    { \"gui.item_unsafe\",\n      { { DarkFlag, QColor(0xff, 0x81, 0x7b) }, { LightFlag, QColor(0xff, 0x81, 0x7b) } } },\n    { \"gui.navbar.seek\",\n      { { DarkFlag, QColor(0xe9, 0x56, 0x56) }, { LightFlag, QColor(0xff, 0x00, 0x00) } } },\n    { \"gui.navbar.pc\",\n      { { DarkFlag, QColor(0x42, 0xee, 0xf4) }, { LightFlag, QColor(0x42, 0xee, 0xf4) } } },\n    { \"gui.navbar.code\",\n      { { DarkFlag, QColor(0x82, 0xc8, 0x6f) }, { LightFlag, QColor(0x68, 0xe5, 0x45) } } },\n    { \"gui.navbar.str\",\n      { { DarkFlag, QColor(0x6f, 0x86, 0xd8) }, { LightFlag, QColor(0x45, 0x68, 0xe5) } } },\n    { \"gui.navbar.sym\",\n      { { DarkFlag, QColor(0xdd, 0xa3, 0x68) }, { LightFlag, QColor(0xe5, 0x96, 0x45) } } },\n    { \"gui.navbar.import\",\n      { { DarkFlag, QColor(0xEE, 0x8B, 0xA2) }, { LightFlag, QColor(0xEE, 0x8B, 0xA2) } } },\n    { \"gui.navbar.signature\",\n      { { DarkFlag, QColor(0xF5, 0xF5, 0xF5) }, { LightFlag, QColor(0x8D, 0x6E, 0x63) } } },\n    { \"gui.navbar.data\",\n      { { DarkFlag, QColor(0x23, 0x3d, 0x4d) }, { LightFlag, QColor(0x4E, 0xD1, 0xC1) } } },\n    { \"gui.navbar.unexplored\",\n      { { DarkFlag, QColor(0x64, 0x64, 0x64) }, { LightFlag, QColor(0xdc, 0xec, 0xf5) } } },\n    { \"gui.breakpoint_background\",\n      { { DarkFlag, QColor(0x8c, 0x4c, 0x4c) }, { LightFlag, QColor(0xe9, 0x8f, 0x8f) } } },\n    { \"gui.overview.node\",\n      { { DarkFlag, QColor(0x64, 0x64, 0x64) }, { LightFlag, QColor(0xf5, 0xfa, 0xff) } } },\n    { \"gui.tooltip.background\",\n      { { DarkFlag, QColor(0x2a, 0x2c, 0x2e) }, { LightFlag, QColor(0xfa, 0xfc, 0xfe) } } },\n    { \"gui.tooltip.foreground\",\n      { { DarkFlag, QColor(0xfa, 0xfc, 0xfe) }, { LightFlag, QColor(0x2a, 0x2c, 0x2e) } } },\n    { \"gui.border\",\n      { { DarkFlag, QColor(0x64, 0x64, 0x64) }, { LightFlag, QColor(0x91, 0xc8, 0xfa) } } },\n    { \"gui.background\",\n      { { DarkFlag, QColor(0x25, 0x28, 0x2b) }, { LightFlag, QColor(0xff, 0xff, 0xff) } } },\n    { \"gui.alt_background\",\n      { { DarkFlag, QColor(0x1c, 0x1f, 0x24) }, { LightFlag, QColor(0xf5, 0xfa, 0xff) } } },\n    { \"gui.disass_selected\",\n      { { DarkFlag, QColor(0x1f, 0x22, 0x28) }, { LightFlag, QColor(0xff, 0xff, 0xff) } } },\n    { \"lineHighlight\",\n      { { DarkFlag, QColor(0x15, 0x1d, 0x1d, 0x96) },\n        { LightFlag, QColor(0xd2, 0xd2, 0xff, 0x96) } } },\n    { \"wordHighlightBg\",\n      { { DarkFlag, QColor(0x3a, 0x41, 0x50, 0xff) },\n        { LightFlag, QColor(0xb3, 0x77, 0xd6, 0x50) } } },\n    { \"wordHighlightFg\",\n      { { DarkFlag, QColor(0x00, 0x00, 0x00, 0x00) },\n        { LightFlag, QColor(0x00, 0x00, 0x00, 0x00) } } },\n    { \"highlightPC\",\n      { { DarkFlag, QColor(0x57, 0x1a, 0x07) }, { LightFlag, QColor(0xd6, 0xff, 0xd2) } } },\n    { \"gui.overview.fill\",\n      { { DarkFlag, QColor(0xff, 0xff, 0xff, 0x28) },\n        { LightFlag, QColor(0xaf, 0xd9, 0xea, 0x41) } } },\n    { \"gui.overview.border\",\n      { { DarkFlag, QColor(0x63, 0xda, 0xe8, 0x32) },\n        { LightFlag, QColor(0x63, 0xda, 0xe8, 0x32) } } },\n    { \"gui.navbar.err\",\n      { { DarkFlag, QColor(0x03, 0xaa, 0xf5) }, { LightFlag, QColor(0x03, 0xaa, 0xf5) } } },\n    { \"gui.navbar.err\",\n      { { DarkFlag, QColor(0x03, 0xaa, 0xf5) }, { LightFlag, QColor(0x03, 0xaa, 0xf5) } } },\n    { \"searchCurrent\",\n      { { DarkFlag, QColor(0x8B, 0x3A, 0x22) }, { LightFlag, QColor(0xA5, 0xC8, 0xA2) } } },\n    { \"searchHighlight\",\n      { { DarkFlag, QColor(0x3a, 0x41, 0x50, 0xff) },\n        { LightFlag, QColor(0xb3, 0x77, 0xd6, 0x50) } } }\n};\n\nConfiguration *Configuration::mPtr = nullptr;\n\n/**\n * @brief All asm.* options saved as settings. Values are the default values.\n */\nstatic const QHash<QString, QVariant> asmOptions = { { \"asm.esil\", false },\n                                                     { \"asm.pseudo\", false },\n                                                     { \"asm.offset\", true },\n                                                     { \"asm.xrefs\", false },\n                                                     { \"asm.indent\", false },\n                                                     { \"asm.describe\", false },\n                                                     { \"asm.slow\", true },\n                                                     { \"asm.lines\", true },\n                                                     { \"asm.lines.fcn\", true },\n                                                     { \"asm.flags.offset\", false },\n                                                     { \"asm.emu\", false },\n                                                     { \"emu.str\", false },\n                                                     { \"asm.cmt.right\", true },\n                                                     { \"asm.cmt.col\", 35 },\n                                                     { \"asm.var.summary\", false },\n                                                     { \"asm.bytes\", false },\n                                                     { \"asm.size\", false },\n                                                     { \"asm.bytes.space\", false },\n                                                     { \"asm.lbytes\", true },\n                                                     { \"asm.nbytes\", 10 },\n                                                     { \"asm.syntax\", \"intel\" },\n                                                     { \"asm.ucase\", false },\n                                                     { \"asm.bb.line\", false },\n                                                     { \"asm.capitalize\", false },\n                                                     { \"asm.sub.var\", true },\n                                                     { \"asm.sub.varonly\", true },\n                                                     { \"asm.tabs\", 8 },\n                                                     { \"asm.tabs.off\", 5 },\n                                                     { \"asm.marks\", false },\n                                                     { \"asm.refptr\", false },\n                                                     { \"asm.flags.real\", true },\n                                                     { \"asm.reloff\", false },\n                                                     { \"asm.reloff.flags\", false },\n                                                     { \"esil.breakoninvalid\", true },\n                                                     { \"dbg.trace_continue\", true } };\n\nConfiguration::Configuration() : QObject(), nativePalette(qApp->palette())\n{\n    mPtr = this;\n    if (!s.isWritable()) {\n        QMessageBox::critical(\n                nullptr, tr(\"Critical Error!\"),\n                tr(\"Settings are not writable! Make sure you have a write access to \\\"%1\\\".\")\n                        .arg(s.fileName()));\n    }\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n    kSyntaxHighlightingRepository = nullptr;\n#endif\n}\n\nConfiguration *Configuration::instance()\n{\n    if (!mPtr)\n        mPtr = new Configuration();\n    return mPtr;\n}\n\nvoid Configuration::loadInitial()\n{\n    setInterfaceTheme(getInterfaceTheme());\n    setColorTheme(getColorTheme());\n    applySavedAsmOptions();\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n    kSyntaxHighlightingRepository = new KSyntaxHighlighting::Repository();\n#endif\n}\n\nQString Configuration::getRecentFolder()\n{\n    QString recentFolder = s.value(\"dir.recentFolder\", QDir::homePath()).toString();\n\n    return QDir::toNativeSeparators(recentFolder);\n}\n\nvoid Configuration::setRecentFolder(const QString &dir)\n{\n    s.setValue(\"dir.recentFolder\", QDir::toNativeSeparators(dir));\n}\n\n/**\n * @brief Configuration::setFilesTabLastClicked\n * Set the new file dialog last clicked tab\n * @param lastClicked\n */\nvoid Configuration::setNewFileLastClicked(int lastClicked)\n{\n    s.setValue(\"newFileLastClicked\", lastClicked);\n}\n\nint Configuration::getNewFileLastClicked()\n{\n    return s.value(\"newFileLastClicked\").toInt();\n}\n\nvoid Configuration::resetAll()\n{\n    // Don't reset all rizin vars, that currently breaks a bunch of stuff.\n    // settingsFile.remove()+loadInitials() should reset all settings configurable using Cutter GUI.\n\n    Core()->setSettings();\n    // Delete the file so no extra configuration is in it.\n    QFile settingsFile(s.fileName());\n    settingsFile.remove();\n    s.clear();\n\n    loadInitial();\n    emit fontsUpdated();\n}\n\nbool Configuration::getAutoUpdateEnabled() const\n{\n    return s.value(\"autoUpdateEnabled\", false).toBool();\n}\n\nvoid Configuration::setAutoUpdateEnabled(bool au)\n{\n    s.setValue(\"autoUpdateEnabled\", au);\n}\n\n/**\n * @brief get the current Locale set in Cutter's user configuration\n * @return a QLocale object describes user's current locale\n */\nQLocale Configuration::getCurrLocale() const\n{\n    return s.value(\"locale\", QLocale().system()).toLocale();\n}\n\n/**\n * @brief sets Cutter's locale\n * @param l - a QLocale object describes the locate to configure\n */\nvoid Configuration::setLocale(const QLocale &l)\n{\n    s.setValue(\"locale\", l);\n}\n\n/**\n * @brief set Cutter's interface language by a given locale name\n * @param language - a string represents the name of a locale language\n * @return true on success\n */\nbool Configuration::setLocaleByName(const QString &language)\n{\n    const auto &allLocales =\n            QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);\n\n    for (auto &it : allLocales) {\n        if (QString::compare(it.nativeLanguageName(), language, Qt::CaseInsensitive) == 0\n            || it.name() == language) {\n            setLocale(it);\n            return true;\n        }\n    }\n    return false;\n}\n\nbool Configuration::windowColorIsDark()\n{\n    ColorFlags currentThemeColorFlags = getCurrentTheme()->flag;\n    if (currentThemeColorFlags == ColorFlags::LightFlag) {\n        return false;\n    } else if (currentThemeColorFlags == ColorFlags::DarkFlag) {\n        return true;\n    }\n    return nativeWindowIsDark();\n}\n\nbool Configuration::nativeWindowIsDark()\n{\n    const QPalette &palette = qApp->palette();\n    auto windowColor = palette.color(QPalette::Window).toRgb();\n    return (windowColor.red() + windowColor.green() + windowColor.blue()) < 382;\n}\n\nvoid Configuration::loadNativeStylesheet()\n{\n    /* Load Qt Theme */\n    QFile f(\":native/native.qss\");\n    if (!f.exists()) {\n        qWarning() << \"Can't find Native theme stylesheet.\";\n    } else {\n        f.open(QFile::ReadOnly | QFile::Text);\n        QTextStream ts(&f);\n        QString stylesheet = ts.readAll();\n#ifdef Q_OS_MACOS\n        QFile mf(nativeWindowIsDark() ? \":native/native-macos-dark.qss\"\n                                      : \":native/native-macos-light.qss\");\n        if (mf.exists()) {\n            mf.open(QFile::ReadOnly | QFile::Text);\n            QTextStream mts(&mf);\n            stylesheet += \"\\n\" + mts.readAll();\n        }\n#endif\n        qApp->setStyleSheet(stylesheet);\n    }\n\n    qApp->setPalette(nativePalette);\n    /* Some widgets does not change its palette when QApplication changes one\n     * so this loop force all widgets do this, but all widgets take palette from\n     * QApplication::palette() when they are created so line above is necessary too.\n     */\n    for (auto widget : qApp->allWidgets()) {\n        widget->setPalette(nativePalette);\n    }\n}\n\n/**\n * @brief Loads the Light theme of Cutter and modify special theme colors\n */\nvoid Configuration::loadLightStylesheet()\n{\n    /* Load Qt Theme */\n    QFile f(\":lightstyle/light.qss\");\n    if (!f.exists()) {\n        qWarning() << \"Can't find Light theme stylesheet.\";\n    } else {\n        f.open(QFile::ReadOnly | QFile::Text);\n        QTextStream ts(&f);\n        QString stylesheet = ts.readAll();\n\n        QPalette p = qApp->palette();\n        p.setColor(QPalette::Text, Qt::black);\n        qApp->setPalette(p);\n\n        qApp->setStyleSheet(stylesheet);\n    }\n}\n\nvoid Configuration::loadDarkStylesheet()\n{\n    /* Load Qt Theme */\n    QFile f(\":qdarkstyle/style.qss\");\n    if (!f.exists()) {\n        qWarning() << \"Can't find Dark theme stylesheet.\";\n    } else {\n        f.open(QFile::ReadOnly | QFile::Text);\n        QTextStream ts(&f);\n        QString stylesheet = ts.readAll();\n#ifdef Q_OS_MACX\n        // see https://github.com/ColinDuquesnoy/QDarkStyleSheet/issues/22#issuecomment-96179529\n        stylesheet += \"QDockWidget::title\"\n                      \"{\"\n                      \"    background-color: #31363b;\"\n                      \"    text-align: center;\"\n                      \"    height: 12px;\"\n                      \"}\";\n#endif\n        QPalette p = qApp->palette();\n        p.setColor(QPalette::Text, Qt::white);\n        qApp->setPalette(p);\n        qApp->setStyleSheet(stylesheet);\n    }\n}\n\nvoid Configuration::loadMidnightStylesheet()\n{\n    /* Load Qt Theme */\n    QFile f(\":midnight/style.css\");\n    if (!f.exists()) {\n        qWarning() << \"Can't find Midnight theme stylesheet.\";\n    } else {\n        f.open(QFile::ReadOnly | QFile::Text);\n        QTextStream ts(&f);\n        QString stylesheet = ts.readAll();\n\n        QPalette p = qApp->palette();\n        p.setColor(QPalette::Text, Qt::white);\n        qApp->setPalette(p);\n\n        qApp->setStyleSheet(stylesheet);\n    }\n}\n\nconst QFont Configuration::getBaseFont() const\n{\n    QFont font = s.value(\"font\", QFont(\"Inconsolata\", 11)).value<QFont>();\n    return font;\n}\n\nconst QFont Configuration::getFont() const\n{\n    QFont font = getBaseFont();\n    font.setPointSizeF(font.pointSizeF() * getZoomFactor());\n    return font;\n}\n\nvoid Configuration::setFont(const QFont &font)\n{\n    s.setValue(\"font\", font);\n    emit fontsUpdated();\n}\n\nvoid Configuration::refreshFont()\n{\n    emit fontsUpdated();\n}\n\nqreal Configuration::getZoomFactor() const\n{\n    qreal fontZoom = s.value(\"zoomFactor\", 1.0).value<qreal>();\n    return qMax(fontZoom, 0.1);\n}\n\nvoid Configuration::setZoomFactor(qreal zoom)\n{\n    s.setValue(\"zoomFactor\", qMax(zoom, 0.1));\n    emit fontsUpdated();\n}\n\nQString Configuration::getLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme) const\n{\n    return s.value(\"lastThemeOf.\" + currInterfaceTheme.name, Config()->getColorTheme()).toString();\n}\n\nvoid Configuration::setInterfaceTheme(int theme)\n{\n    if (theme >= cutterInterfaceThemesList().size() || theme < 0) {\n        theme = 0;\n    }\n    s.setValue(\"ColorPalette\", theme);\n\n    CutterInterfaceTheme interfaceTheme = cutterInterfaceThemesList()[theme];\n\n    if (interfaceTheme.name == \"Native\") {\n        loadNativeStylesheet();\n    } else if (interfaceTheme.name == \"Dark\") {\n        loadDarkStylesheet();\n    } else if (interfaceTheme.name == \"Midnight\") {\n        loadMidnightStylesheet();\n    } else if (interfaceTheme.name == \"Light\") {\n        loadLightStylesheet();\n    } else {\n        loadNativeStylesheet();\n    }\n\n    for (auto it = cutterOptionColors.cbegin(); it != cutterOptionColors.cend(); it++) {\n        setColor(it.key(), it.value()[interfaceTheme.flag]);\n    }\n\n    adjustColorThemeDarkness();\n\n    emit interfaceThemeChanged();\n    emit colorsUpdated();\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n    emit kSyntaxHighlightingThemeChanged();\n#endif\n}\n\nconst CutterInterfaceTheme *Configuration::getCurrentTheme()\n{\n    int i = getInterfaceTheme();\n    if (i < 0 || i >= cutterInterfaceThemesList().size()) {\n        i = 0;\n        setInterfaceTheme(i);\n    }\n    return &cutterInterfaceThemesList()[i];\n}\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\nKSyntaxHighlighting::Repository *Configuration::getKSyntaxHighlightingRepository()\n{\n    return kSyntaxHighlightingRepository;\n}\n\nKSyntaxHighlighting::Theme Configuration::getKSyntaxHighlightingTheme()\n{\n    auto repo = getKSyntaxHighlightingRepository();\n    if (!repo) {\n        return KSyntaxHighlighting::Theme();\n    }\n    return repo->defaultTheme(getCurrentTheme()->flag & DarkFlag\n                                      ? KSyntaxHighlighting::Repository::DefaultTheme::DarkTheme\n                                      : KSyntaxHighlighting::Repository::DefaultTheme::LightTheme);\n}\n#endif\n\nQSyntaxHighlighter *Configuration::createSyntaxHighlighter(QTextDocument *document)\n{\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n    auto syntaxHighlighter = new SyntaxHighlighter(document);\n    auto repo = getKSyntaxHighlightingRepository();\n    if (repo) {\n        syntaxHighlighter->setDefinition(repo->definitionForName(\"C\"));\n    }\n    return syntaxHighlighter;\n#else\n    return new FallbackSyntaxHighlighter(document);\n#endif\n}\n\nQString Configuration::getLogoFile()\n{\n    return windowColorIsDark() ? QString(\":/img/cutter_white_plain.svg\")\n                               : QString(\":/img/cutter_plain.svg\");\n}\n\n/**\n * @brief Configuration::setColor sets the local Cutter configuration color\n * @param name Color Name\n * @param color The color you want to set\n */\nvoid Configuration::setColor(const QString &name, const QColor &color)\n{\n    s.setValue(\"colors.\" + name, color);\n}\n\nvoid Configuration::setLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme,\n                                   const QString &theme)\n{\n    s.setValue(\"lastThemeOf.\" + currInterfaceTheme.name, theme);\n}\n\nconst QColor Configuration::getColor(const QString &name) const\n{\n    if (s.contains(\"colors.\" + name)) {\n        return s.value(\"colors.\" + name).value<QColor>();\n    } else {\n        return s.value(\"colors.other\").value<QColor>();\n    }\n}\n\nvoid Configuration::setColorTheme(const QString &theme)\n{\n    RzCoreLocked core = Core()->lock();\n    if (theme == \"default\") {\n        rz_cons_pal_init(core->cons->context);\n        s.setValue(\"theme\", \"default\");\n    } else {\n        rz_core_theme_load(core, theme.toUtf8().constData());\n        s.setValue(\"theme\", theme);\n    }\n\n    ColorThemeWorker::Theme colorTheme = ThemeWorker().getTheme(theme);\n    for (auto it = colorTheme.constBegin(); it != colorTheme.constEnd(); it++) {\n        setColor(it.key(), it.value());\n    }\n\n    emit colorsUpdated();\n}\n\nvoid Configuration::adjustColorThemeDarkness()\n{\n    bool windowIsDark = windowColorIsDark();\n    int windowDarkness = windowIsDark ? DarkFlag : LightFlag;\n    int currentColorThemeDarkness = colorThemeDarkness(getColorTheme());\n\n    if ((currentColorThemeDarkness & windowDarkness) == 0) {\n        setColorTheme(windowIsDark ? DEFAULT_DARK_COLOR_THEME : DEFAULT_LIGHT_COLOR_THEME);\n    }\n}\n\nint Configuration::colorThemeDarkness(const QString &colorTheme) const\n{\n    auto flags = relevantThemes.find(colorTheme);\n    if (flags != relevantThemes.end()) {\n        return static_cast<int>(*flags);\n    }\n    return DarkFlag | LightFlag;\n}\n\nvoid Configuration::resetToDefaultAsmOptions()\n{\n    for (auto it = asmOptions.cbegin(); it != asmOptions.cend(); it++) {\n        setConfig(it.key(), it.value());\n    }\n}\n\nvoid Configuration::applySavedAsmOptions()\n{\n    for (auto it = asmOptions.cbegin(); it != asmOptions.cend(); it++) {\n        Core()->setConfig(it.key(), s.value(it.key(), it.value()));\n    }\n}\n\nconst QList<CutterInterfaceTheme> &Configuration::cutterInterfaceThemesList()\n{\n    static const QList<CutterInterfaceTheme> list = {\n        { \"Native\", Configuration::nativeWindowIsDark() ? DarkFlag : LightFlag },\n        { \"Dark\", DarkFlag },\n        { \"Midnight\", DarkFlag },\n        { \"Light\", LightFlag }\n    };\n    return list;\n}\n\nQVariant Configuration::getConfigVar(const QString &key)\n{\n    QHash<QString, QVariant>::const_iterator it = asmOptions.find(key);\n    if (it != asmOptions.end()) {\n        switch (it.value().type()) {\n        case QVariant::Type::Bool:\n            return Core()->getConfigb(key);\n        case QVariant::Type::Int:\n            return Core()->getConfigi(key);\n        default:\n            return Core()->getConfig(key);\n        }\n    }\n    return QVariant();\n}\n\nbool Configuration::getConfigBool(const QString &key)\n{\n    return getConfigVar(key).toBool();\n}\n\nint Configuration::getConfigInt(const QString &key)\n{\n    return getConfigVar(key).toInt();\n}\n\nQString Configuration::getConfigString(const QString &key)\n{\n    return getConfigVar(key).toString();\n}\n\n/**\n * @brief Configuration::setConfig\n * Set Rizin configuration value (e.g. \"asm.lines\")\n * @param key\n * @param value\n */\nvoid Configuration::setConfig(const QString &key, const QVariant &value)\n{\n    if (asmOptions.contains(key)) {\n        s.setValue(key, value);\n    }\n\n    Core()->setConfig(key, value);\n}\n\n/**\n * @brief this function will gather and return available translation for Cutter\n * @return a list of locales and their names\n */\nstd::vector<Configuration::LangInfo> Configuration::getAvailableTranslations()\n{\n    const auto &trDirs = Cutter::getTranslationsDirectories();\n\n    QSet<QString> fileNamesSet;\n    for (const auto &trDir : trDirs) {\n        QDir dir(trDir);\n        if (!dir.exists()) {\n            continue;\n        }\n        const QStringList &currTrFileNames =\n                dir.entryList(QStringList(\"cutter_*.qm\"), QDir::Files, QDir::Name);\n        for (const auto &trFile : currTrFileNames) {\n            fileNamesSet << trFile;\n        }\n    }\n\n    QStringList fileNames = fileNamesSet.values();\n    std::sort(fileNames.begin(), fileNames.end());\n    QString currLanguageName;\n    std::vector<Configuration::LangInfo> result;\n    QHash<QString, int> langCount;\n    for (const auto &translationFile : fileNames) {\n        auto name = QFileInfo(translationFile).baseName();\n        auto parts = name.split(\"_\");\n        if (parts.length() < 2) {\n            continue;\n        }\n        auto langCode = parts[1];\n        ++langCount[langCode];\n    }\n\n    for (auto &i : fileNames) {\n        auto name = QFileInfo(i).baseName();\n        QString localeName = name.mid(sizeof(\"cutter_\") - 1);\n        QLocale locale(localeName);\n        if (locale.language() == QLocale::C) {\n            continue;\n        }\n        auto langCode = locale.name().split(\"_\").first();\n        currLanguageName = locale.nativeLanguageName();\n        if (currLanguageName.isEmpty()) { // Qt doesn't have native language name for some languages\n            currLanguageName = QLocale::languageToString(locale.language());\n        }\n        // When there is single translation try to use generic language name without region name\n        if (langCount[langCode] <= 1\n            && locale.language() != QLocale::Chinese) { // Always distinguish Chinese Traditional,\n                                                        // Chinese simplified\n            QLocale localSimple(locale.language());\n            auto simpleName = localSimple.nativeLanguageName();\n            if (!simpleName.isEmpty()) {\n                currLanguageName = simpleName;\n            }\n        }\n        if (!currLanguageName.isEmpty()) {\n            result.push_back({ currLanguageName, locale });\n        }\n    }\n    if (langCount[\"en\"] == 0) {\n        result.push_back({ \"English\", QLocale(\"en\") });\n    }\n    return result;\n}\n\n/**\n * @brief check if this is the first time Cutter's is executed on this computer\n * @return true if this is first execution; otherwise returns false.\n */\nbool Configuration::isFirstExecution()\n{\n    // check if a variable named firstExecution existed in the configuration\n    if (s.contains(\"firstExecution\")) {\n        return false;\n    } else {\n        s.setValue(\"firstExecution\", false);\n        return true;\n    }\n}\n\nQString Configuration::getSelectedDecompiler()\n{\n    return s.value(\"selectedDecompiler\").toString();\n}\n\nvoid Configuration::setSelectedDecompiler(const QString &id)\n{\n    s.setValue(\"selectedDecompiler\", id);\n}\n\nbool Configuration::getDecompilerAutoRefreshEnabled()\n{\n    return s.value(\"decompilerAutoRefresh\", true).toBool();\n}\n\nvoid Configuration::setDecompilerAutoRefreshEnabled(bool enabled)\n{\n    s.setValue(\"decompilerAutoRefresh\", enabled);\n}\n\nvoid Configuration::enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter)\n{\n    s.setValue(\"decompilerAnnotationHighlighter\", useDecompilerHighlighter);\n    emit colorsUpdated();\n}\n\nbool Configuration::isDecompilerAnnotationHighlighterEnabled()\n{\n    return s.value(\"decompilerAnnotationHighlighter\", true).value<bool>();\n}\n\nbool Configuration::getBitmapTransparentState()\n{\n    return s.value(\"bitmapGraphExportTransparency\", false).value<bool>();\n}\n\ndouble Configuration::getBitmapExportScaleFactor()\n{\n    return s.value(\"bitmapGraphExportScale\", 1.0).value<double>();\n}\n\nvoid Configuration::setBitmapTransparentState(bool inputValueGraph)\n{\n    s.setValue(\"bitmapGraphExportTransparency\", inputValueGraph);\n}\n\nvoid Configuration::setBitmapExportScaleFactor(double inputValueGraph)\n{\n    s.setValue(\"bitmapGraphExportScale\", inputValueGraph);\n}\n\nvoid Configuration::setGraphSpacing(QPoint blockSpacing, QPoint edgeSpacing)\n{\n    s.setValue(\"graph.blockSpacing\", blockSpacing);\n    s.setValue(\"graph.edgeSpacing\", edgeSpacing);\n}\n\nQPoint Configuration::getGraphBlockSpacing()\n{\n    return s.value(\"graph.blockSpacing\", QPoint(20, 40)).value<QPoint>();\n}\n\nQPoint Configuration::getGraphEdgeSpacing()\n{\n    return s.value(\"graph.edgeSpacing\", QPoint(10, 10)).value<QPoint>();\n}\n\nvoid Configuration::setOutputRedirectionEnabled(bool enabled)\n{\n    this->outputRedirectEnabled = enabled;\n}\n\nbool Configuration::getOutputRedirectionEnabled() const\n{\n    return outputRedirectEnabled;\n}\n\nvoid Configuration::setPreviewValue(bool checked)\n{\n    s.setValue(\"asm.preview\", checked);\n}\n\nbool Configuration::getPreviewValue() const\n{\n    return s.value(\"asm.preview\").toBool();\n}\n\nvoid Configuration::setShowVarTooltips(bool enabled)\n{\n    s.setValue(\"showVarTooltips\", enabled);\n}\n\nbool Configuration::getShowVarTooltips() const\n{\n    return s.value(\"showVarTooltips\").toBool();\n}\n\nbool Configuration::getGraphBlockEntryOffset()\n{\n    return s.value(\"graphBlockEntryOffset\", true).value<bool>();\n}\n\nvoid Configuration::setGraphBlockEntryOffset(bool enabled)\n{\n    s.setValue(\"graphBlockEntryOffset\", enabled);\n}\n\nQList<RecentFileEntry> Configuration::getRecentFiles() const\n{\n    QList<RecentFileEntry> recentFiles;\n\n    const QStringList list = s.value(\"recentFileList\").toStringList();\n    for (const QString &file : list) {\n        int sep = file.indexOf(\"://\");\n        if (sep != -1) {\n            QString ioMode = file.left(sep + 3);\n            QString path = file.mid(sep + 3);\n            recentFiles.append({ ioMode, path });\n        } else {\n            recentFiles.append({ \"file://\", file });\n        }\n    }\n\n    return recentFiles;\n}\n\nvoid Configuration::setRecentFiles(const QList<RecentFileEntry> &list)\n{\n    QStringList recentFiles;\n    for (const RecentFileEntry &file : list) {\n        recentFiles.append(file.ioMode + file.path);\n    }\n    s.setValue(\"recentFileList\", recentFiles);\n}\n\nQList<RecentFileEntry> Configuration::getRecentProjects() const\n{\n    QList<RecentFileEntry> recentProjects;\n    const QStringList list = s.value(\"recentProjectsList\").toStringList();\n    for (const QString &project : list) {\n        recentProjects.append(\n                { \"\", project }); // recent projects don’t include ioMode, so just leave it empty\n    }\n    return recentProjects;\n}\n\nvoid Configuration::setRecentProjects(const QList<RecentFileEntry> &list)\n{\n    QStringList recentProjects;\n    for (const RecentFileEntry &project : list) {\n        recentProjects.append(project.path);\n    }\n    s.setValue(\"recentProjectsList\", recentProjects);\n}\n\nvoid Configuration::addRecentProject(QString file)\n{\n    RecentFileEntry project = { \"\", file };\n    QList<RecentFileEntry> files = getRecentProjects();\n    files.removeAll(project);\n    files.prepend(project);\n    setRecentProjects(files);\n}\n\nQStringList Configuration::getRecentRegProfiles() const\n{\n    return s.value(\"recentRegProfilesList\").toStringList();\n}\n\nvoid Configuration::setRecentRegProfiles(const QStringList &list)\n{\n    s.setValue(\"recentRegProfilesList\", list);\n}\n\nvoid Configuration::addRecentRegProfile(const QString &profile)\n{\n    QStringList recentProfiles = getRecentRegProfiles();\n    recentProfiles.removeAll(profile);\n    recentProfiles.prepend(profile);\n    setRecentRegProfiles(recentProfiles);\n}\n\nvoid Configuration::removeRecentRegProfile(const QString &profile)\n{\n\n    QStringList recentProfiles = getRecentRegProfiles();\n    recentProfiles.removeAll(profile);\n    setRecentRegProfiles(recentProfiles);\n}\n\nQString Configuration::getFunctionsWidgetLayout()\n{\n    return s.value(\"functionsWidgetLayout\").toString();\n}\n\nvoid Configuration::setFunctionsWidgetLayout(const QString &layout)\n{\n    s.setValue(\"functionsWidgetLayout\", layout);\n}\n\nvoid Configuration::setNavBarLegendEnabled(bool enabled)\n{\n    s.setValue(\"navBarLegend\", enabled);\n}\n\nbool Configuration::getNavBarLegendEnabled()\n{\n    return s.value(\"navBarLegend\").toBool();\n}\n"
  },
  {
    "path": "src/common/Configuration.h",
    "content": "#ifndef CONFIGURATION_H\n#define CONFIGURATION_H\n\n#include <QSettings>\n#include <QFont>\n#include <core/Cutter.h>\n\n#define Config() (Configuration::instance())\n#define ConfigColor(x) Config()->getColor(x)\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\nnamespace KSyntaxHighlighting {\nclass Repository;\nclass Theme;\n}\n#endif\n\nclass QSyntaxHighlighter;\nclass QTextDocument;\n\nenum ColorFlags {\n    LightFlag = 1,\n    DarkFlag = 2,\n    DualColor = LightFlag | DarkFlag,\n};\n\nstruct CutterInterfaceTheme\n{\n    QString name;\n    ColorFlags flag;\n};\n\nstruct RecentFileEntry\n{\n    QString ioMode;\n    QString path;\n\n    bool operator==(const RecentFileEntry &other) const\n    {\n        return ioMode == other.ioMode && path == other.path;\n    }\n};\n\nclass CUTTER_EXPORT Configuration : public QObject\n{\n    Q_OBJECT\nprivate:\n    QPalette nativePalette;\n    QSettings s;\n    static Configuration *mPtr;\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n    KSyntaxHighlighting::Repository *kSyntaxHighlightingRepository;\n#endif\n    bool outputRedirectEnabled = true;\n\n    Configuration();\n    // Colors\n    void loadBaseThemeNative();\n    void loadBaseThemeDark();\n    void loadNativeStylesheet();\n    void loadLightStylesheet();\n    void loadDarkStylesheet();\n    void loadMidnightStylesheet();\n\n    // Asm Options\n    void applySavedAsmOptions();\n\npublic:\n    static const QList<CutterInterfaceTheme> &cutterInterfaceThemesList();\n    static const QHash<QString, ColorFlags> relevantThemes;\n    static const QHash<QString, QHash<ColorFlags, QColor>> cutterOptionColors;\n\n    // Functions\n    static Configuration *instance();\n\n    void loadInitial();\n\n    void resetAll();\n\n    // Auto update\n    bool getAutoUpdateEnabled() const;\n    void setAutoUpdateEnabled(bool au);\n\n    // Languages\n    QLocale getCurrLocale() const;\n    void setLocale(const QLocale &l);\n    bool setLocaleByName(const QString &language);\n    struct LangInfo\n    {\n        QString name;\n        QLocale locale;\n    };\n    std::vector<LangInfo> getAvailableTranslations();\n\n    // Fonts\n\n    /**\n     * @brief Gets the configured font set by the font selection box\n     * @return the configured font\n     */\n    const QFont getBaseFont() const;\n\n    /**\n     * @brief Gets the configured font with the point size adjusted by the configured zoom\n     * level (minimum of 10%)\n     * @return the configured font size adjusted by zoom level\n     */\n    const QFont getFont() const;\n    void setFont(const QFont &font);\n    qreal getZoomFactor() const;\n    void setZoomFactor(qreal zoom);\n\n    // Colors\n    bool windowColorIsDark();\n    static bool nativeWindowIsDark();\n    void setLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme, const QString &theme);\n    QString getLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme) const;\n    void setInterfaceTheme(int theme);\n    int getInterfaceTheme() { return s.value(\"ColorPalette\", 0).toInt(); }\n\n    const CutterInterfaceTheme *getCurrentTheme();\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n    KSyntaxHighlighting::Repository *getKSyntaxHighlightingRepository();\n    KSyntaxHighlighting::Theme getKSyntaxHighlightingTheme();\n#endif\n    QSyntaxHighlighter *createSyntaxHighlighter(QTextDocument *document);\n\n    QString getRecentFolder();\n    void setRecentFolder(const QString &dir);\n\n    void setNewFileLastClicked(int lastClicked);\n    int getNewFileLastClicked();\n\n    // Images\n    QString getLogoFile();\n\n    // Asm Options\n    void resetToDefaultAsmOptions();\n\n    QString getColorTheme() const { return s.value(\"theme\", \"cutter\").toString(); }\n    void setColorTheme(const QString &theme);\n    /**\n     * @brief Change current color theme if it doesn't much native theme's darkness.\n     */\n    void adjustColorThemeDarkness();\n    int colorThemeDarkness(const QString &colorTheme) const;\n\n    void setColor(const QString &name, const QColor &color);\n    const QColor getColor(const QString &name) const;\n\n    /**\n     * @brief Get the value of a config var either from Rizin or settings, depending on the key.\n     */\n    QVariant getConfigVar(const QString &key);\n    bool getConfigBool(const QString &key);\n    int getConfigInt(const QString &key);\n    QString getConfigString(const QString &key);\n\n    /**\n     * @brief Set the value of a config var either to Rizin or settings, depending on the key.\n     */\n    void setConfig(const QString &key, const QVariant &value);\n    bool isFirstExecution();\n\n    /**\n     * @return id of the last selected decompiler (see CutterCore::getDecompilerById)\n     */\n    QString getSelectedDecompiler();\n    void setSelectedDecompiler(const QString &id);\n\n    bool getDecompilerAutoRefreshEnabled();\n    void setDecompilerAutoRefreshEnabled(bool enabled);\n\n    void enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter);\n    bool isDecompilerAnnotationHighlighterEnabled();\n\n    // Graph\n    int getGraphBlockMaxChars() const { return s.value(\"graph.maxcols\", 100).toInt(); }\n    void setGraphBlockMaxChars(int ch) { s.setValue(\"graph.maxcols\", ch); }\n\n    int getGraphMinFontSize() const { return s.value(\"graph.minfontsize\", 4).toInt(); }\n\n    void setGraphMinFontSize(int sz) { s.setValue(\"graph.minfontsize\", sz); }\n\n    /**\n     * @brief Get the boolean setting for preview in Graph\n     * @return True if preview checkbox is checked, false otherwise\n     */\n    bool getGraphPreview() { return s.value(\"graph.preview\").toBool(); }\n    /**\n     * @brief Set the boolean setting for preview in Graph\n     * @param checked is a boolean that represents the preview checkbox\n     */\n    void setGraphPreview(bool checked) { s.setValue(\"graph.preview\", checked); }\n\n    /**\n     * @brief Getters and setters for the transaparent option state and scale factor for bitmap\n     * graph exports.\n     */\n    bool getBitmapTransparentState();\n    double getBitmapExportScaleFactor();\n    void setBitmapTransparentState(bool inputValueGraph);\n    void setBitmapExportScaleFactor(double inputValueGraph);\n    void setGraphSpacing(QPoint blockSpacing, QPoint edgeSpacing);\n    QPoint getGraphBlockSpacing();\n    QPoint getGraphEdgeSpacing();\n\n    /**\n     * @brief Gets whether the entry offset of each block has to be displayed or not\n     * @return true if the entry offset has to be displayed, false otherwise\n     */\n    bool getGraphBlockEntryOffset();\n\n    /**\n     * @brief Enable or disable the displaying of the entry offset in each graph block\n     * @param enabled set this to true for displaying the entry offset in each graph block, false\n     * otherwise\n     */\n    void setGraphBlockEntryOffset(bool enabled);\n\n    /**\n     * @brief Enable or disable Cutter output redirection.\n     * Output redirection state can only be changed early during Cutter initialization.\n     * Changing it later will have no effect\n     * @param enabled set this to false for disabling output redirection\n     */\n    void setOutputRedirectionEnabled(bool enabled);\n    bool getOutputRedirectionEnabled() const;\n\n    void setPreviewValue(bool checked);\n    bool getPreviewValue() const;\n\n    /**\n     * @brief Show tooltips for known values of registers, variables, and memory when debugging\n     */\n    void setShowVarTooltips(bool enabled);\n    bool getShowVarTooltips() const;\n\n    /**\n     * @brief Recently opened binaries, as shown in NewFileDialog.\n     */\n    QList<RecentFileEntry> getRecentFiles() const;\n    void setRecentFiles(const QList<RecentFileEntry> &list);\n\n    /**\n     * @brief Recently opened projects, as shown in NewFileDialog.\n     */\n    QList<RecentFileEntry> getRecentProjects() const;\n    void setRecentProjects(const QList<RecentFileEntry> &list);\n    void addRecentProject(QString file);\n\n    /**\n     * @brief Recently used register profiles, as shown in the RegisterProfileDialog\n     */\n    QStringList getRecentRegProfiles() const;\n\n    /**\n     * @brief Sets the list of recently used register profiles\n     * @param list List of serialized profile strings\n     */\n    void setRecentRegProfiles(const QStringList &list);\n\n    /**\n     * @brief Adds a profile to the top of recent profiles list\n     * @param profile Serialized profile path to add\n     */\n    void addRecentRegProfile(const QString &profile);\n\n    /**\n     * @brief Removes all occurences of a profile from recent profiles list\n     * @param profile Serialized profile path to remove\n     */\n    void removeRecentRegProfile(const QString &profile);\n\n    // Functions Widget Layout\n\n    /**\n     * @brief Get the layout of the Functions widget.\n     * @return The layout.\n     */\n    QString getFunctionsWidgetLayout();\n\n    /**\n     * @brief Set the layout of the Functions widget\n     * @param layout The layout of the Functions widget, either horizontal or vertical.\n     */\n    void setFunctionsWidgetLayout(const QString &layout);\n\n    /**\n     * @brief Enable or disable the display of the legend in the navigation bar\n     * @param enabled Set to true to show the legend, false to hide it\n     */\n    void setNavBarLegendEnabled(bool enabled);\n\n    /**\n     * @brief Check if the navigation bar legend is enabled\n     * @return True if the legend is enabled, false otherwise\n     */\n    bool getNavBarLegendEnabled();\n\npublic slots:\n    void refreshFont();\nsignals:\n    void fontsUpdated();\n    void colorsUpdated();\n    void interfaceThemeChanged();\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n    void kSyntaxHighlightingThemeChanged();\n#endif\n};\n\n#endif // CONFIGURATION_H\n"
  },
  {
    "path": "src/common/CutterLayout.cpp",
    "content": "#include \"CutterLayout.h\"\n\nusing namespace Cutter;\n\nbool Cutter::isBuiltinLayoutName(const QString &name)\n{\n    return name == LAYOUT_DEFAULT || name == LAYOUT_DEBUG;\n}\n"
  },
  {
    "path": "src/common/CutterLayout.h",
    "content": "#ifndef CUTTER_LAYOUT_H\n#define CUTTER_LAYOUT_H\n\n#include <QByteArray>\n#include <QMap>\n#include <QString>\n#include <QVariantMap>\n\nnamespace Cutter {\n\nstruct CutterLayout\n{\n    QByteArray geometry;\n    QByteArray state;\n    QMap<QString, QVariantMap> viewProperties;\n};\n\nconst QString LAYOUT_DEFAULT = \"Default\";\nconst QString LAYOUT_DEBUG = \"Debug\";\n\nbool isBuiltinLayoutName(const QString &name);\n\n}\n#endif\n"
  },
  {
    "path": "src/common/CutterSearchable.cpp",
    "content": "#include \"CutterSearchable.h\"\n#include \"SearchBarWidget.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QObject>\n#include <QAbstractScrollArea>\n#include <QScrollBar>\n\nvoid CutterSearchableHelper::setupConnections(QWidget *parent, SearchBarWidget *searchBar)\n{\n    CutterSearchable *searchable = dynamic_cast<CutterSearchable *>(parent);\n    if (!searchBar || !parent || !searchable) {\n        return;\n    }\n\n    searchBar->hide();\n\n    QObject::connect(searchBar, &SearchBarWidget::searchChanged, parent,\n                     [searchable](const QString &text, int options) {\n                         searchable->searchChanged(text, options);\n                     });\n\n    QObject::connect(searchBar, &SearchBarWidget::findNextTriggered, parent,\n                     [searchable]() { searchable->findNext(); });\n\n    QObject::connect(searchBar, &SearchBarWidget::findPrevTriggered, parent,\n                     [searchable]() { searchable->findPrev(); });\n\n    QObject::connect(searchBar, &SearchBarWidget::findLastTriggered, parent,\n                     [searchable]() { searchable->findLast(); });\n\n    QObject::connect(searchBar, &SearchBarWidget::hideTriggered, parent,\n                     [searchable]() { searchable->searchBarHidden(); });\n\n    QObject::connect(searchBar, &SearchBarWidget::showTriggered, parent,\n                     [searchable]() { searchable->searchBarShown(); });\n\n    QShortcut *shortcut = Shortcuts()->makeQShortcut(\"Search.toggle\", parent);\n    QObject::connect(shortcut, &QShortcut::activated, parent, [=]() {\n        if (searchBar->isVisible()) {\n            if (searchBar->hasFocus()) {\n                searchBar->hideSearchBar();\n            } else {\n                searchBar->setFocus();\n                searchBar->selectText();\n            }\n        } else {\n            searchBar->showSearchBar();\n\n            QWidget *searchArea = searchable->searchableArea();\n            int hPadding = searchable->searchHPadding();\n            int vPadding = searchable->searchVPadding();\n\n            positionSearchBar(parent, searchBar, searchArea, hPadding, vPadding);\n        }\n    });\n}\n\nvoid CutterSearchableHelper::positionSearchBar(QWidget *parent, SearchBarWidget *searchBar,\n                                               QWidget *searchArea, int hPadding, int vPadding)\n{\n    if (!searchBar || !searchArea) {\n        return;\n    }\n\n    int searchBarWidth = 0;\n    if (auto *scrollArea = qobject_cast<QAbstractScrollArea *>(searchArea)) {\n        QScrollBar *scrollBar = scrollArea->verticalScrollBar();\n        searchBarWidth = (scrollBar && scrollBar->isVisible()) ? scrollBar->width() : 0;\n    }\n\n    QPoint areaPos = searchArea->mapTo(parent, QPoint(0, 0));\n    int x = areaPos.x() + searchArea->width() - searchBarWidth - searchBar->width() - hPadding;\n    int y = areaPos.y() + vPadding;\n\n    searchBar->move(x, y);\n}\n"
  },
  {
    "path": "src/common/CutterSearchable.h",
    "content": "#ifndef CUTTERSEARCHABLE_H\n#define CUTTERSEARCHABLE_H\n\nclass QString;\nclass SearchBarWidget;\nclass QWidget;\n\nenum SearchOption {\n    CaseSensitive = 1,\n    WholeWords = 1 << 1,\n    RegExp = 1 << 2,\n    HighlightMatches = 1 << 3\n};\n\n/**\n * @brief Interface for any widget that needs a search bar\n */\nclass CutterSearchable\n{\npublic:\n    virtual ~CutterSearchable() = default;\n\n    /**\n     * @brief Widget which the search bar will be shown relative to\n     */\n    virtual QWidget *searchableArea() const = 0;\n\n    /**\n     * @brief Padding from the right\n     */\n    virtual int searchHPadding() const = 0;\n\n    /**\n     * @brief Padding from the top\n     */\n    virtual int searchVPadding() const = 0;\n\n    /**\n     * @brief Called when the user types in the search bar or changes search options\n     * Text has a debounce timer of 200ms\n     */\n    virtual void searchChanged(const QString &text, int options) = 0;\n\n    virtual void findNext() = 0;\n    virtual void findPrev() = 0;\n    virtual void findLast() = 0;\n\n    /**\n     * @brief Called when the search bar is closed\n     */\n    virtual void searchBarHidden() = 0;\n\n    /**\n     * @brief Called when the search bar is shown\n     */\n    virtual void searchBarShown() = 0;\n};\n\n/**\n * @brief Helper functions for the search bar\n */\nnamespace CutterSearchableHelper {\n\n/**\n * @brief Connects the search bar to the parent widget\n * @param parent The widget that owns the search bar\n * @param bar The search bar to connect\n */\nvoid setupConnections(QWidget *parent, SearchBarWidget *bar);\n\n/**\n * @brief Sets the search bar position\n * Call this in the resizeEvent of the parent widget\n * @param parent The main widget holding the search bar\n * @param searchBar The search bar being moved\n * @param searchArea The widget which the search bar will be shown relative to\n * @param hPadding Padding from the right\n * @param vPadding Padding from the top\n */\nvoid positionSearchBar(QWidget *parent, SearchBarWidget *searchBar, QWidget *searchArea,\n                       int hPadding, int vPadding);\n};\n\n#endif // CUTTERSEARCHABLE_H\n"
  },
  {
    "path": "src/common/CutterSeekable.cpp",
    "content": "#include \"core/MainWindow.h\"\n#include \"CutterSeekable.h\"\n\n#include <QPlainTextEdit>\n\nCutterSeekable::CutterSeekable(QObject *parent) : QObject(parent)\n{\n    connect(Core(), &CutterCore::seekChanged, this, &CutterSeekable::onCoreSeekChanged);\n}\n\nCutterSeekable::~CutterSeekable() {}\n\nvoid CutterSeekable::setSynchronization(bool sync)\n{\n    synchronized = sync;\n    onCoreSeekChanged(Core()->getOffset(), CutterCore::SeekHistoryType::New);\n    emit syncChanged();\n}\n\nvoid CutterSeekable::onCoreSeekChanged(RVA addr, CutterCore::SeekHistoryType type)\n{\n    if (synchronized && widgetOffset != addr) {\n        updateSeek(addr, type, true);\n    }\n}\n\nvoid CutterSeekable::updateSeek(RVA addr, CutterCore::SeekHistoryType type, bool localOnly)\n{\n    previousOffset = widgetOffset;\n    widgetOffset = addr;\n    if (synchronized && !localOnly) {\n        Core()->seek(addr);\n    }\n\n    emit seekableSeekChanged(addr, type);\n}\n\nvoid CutterSeekable::seekPrev()\n{\n    if (synchronized) {\n        Core()->seekPrev();\n    } else {\n        this->seek(previousOffset, CutterCore::SeekHistoryType::Undo);\n    }\n}\n\nRVA CutterSeekable::getOffset()\n{\n    return (synchronized) ? Core()->getOffset() : widgetOffset;\n}\n\nvoid CutterSeekable::toggleSynchronization()\n{\n    setSynchronization(!synchronized);\n}\n\nbool CutterSeekable::isSynchronized()\n{\n    return synchronized;\n}\n\nvoid CutterSeekable::seekToReference(RVA offset)\n{\n    if (offset == RVA_INVALID) {\n        return;\n    }\n\n    QList<XrefDescription> refs = Core()->getXRefs(offset, false, false);\n\n    if (refs.length()) {\n        if (refs.length() > 1) {\n            qWarning() << tr(\"More than one (%1) references here. Weird behaviour expected.\")\n                                  .arg(refs.length());\n        }\n        // Try first call\n        for (auto &ref : refs) {\n            if (ref.to != RVA_INVALID && ref.type == \"CALL\") {\n                seek(ref.to);\n                return;\n            }\n        }\n        // Fallback to first valid, if any\n        for (auto &ref : refs) {\n            if (ref.to != RVA_INVALID) {\n                seek(ref.to);\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/common/CutterSeekable.h",
    "content": "#pragma once\n\n#include \"core/Cutter.h\"\n\nclass MainWindow;\n\nclass CUTTER_EXPORT CutterSeekable : public QObject\n{\n    Q_OBJECT\n\npublic:\n    explicit CutterSeekable(QObject *parent = nullptr);\n    ~CutterSeekable();\n\n    /**\n     * @brief seek changes current offset.\n     * If the seekable is synchronized with Core, then\n     * the Core offset will be modified and then the CutterCore::seekChanged\n     * signal will be emitted.\n     * In any case, CutterSeekable::seekableSeekChanged is emitted.\n     * @param addr the location to seek at.\n     * @param type the type of seek wrt history (Undo, Redo, or New)\n     */\n    void seek(RVA addr, CutterCore::SeekHistoryType type = CutterCore::SeekHistoryType::New)\n    {\n        updateSeek(addr, type, false);\n    }\n\n    /**\n     * @brief setSynchronization sets\n     * Core seek synchronization.\n     */\n    void setSynchronization(bool sync);\n\n    /**\n     * @brief getOffset returns the seekable offset.\n     * If the seekable is synchronized with Core, this function\n     * is similar to Core()->getOffset.\n     * If it's not synchronized, it will return the seekable current seek.\n     * @return the seekable current offset.\n     */\n    RVA getOffset();\n\n    /**\n     * @brief isSynchronized tells whether the seekable\n     * is synchronized with Core or not.\n     * @return boolean\n     */\n    bool isSynchronized();\n\n    /**\n     * @brief seekToReference will seek to the function or the object which is referenced in a given\n     * offset\n     * @param offset - an address that contains a reference to jump to\n     */\n    void seekToReference(RVA offset);\n\npublic slots:\n    /**\n     * @brief seekPrev seeks to last location.\n     */\n    void seekPrev();\n\n    /**\n     * @brief toggleSyncWithCore toggles\n     * Core seek synchronization.\n     */\n    void toggleSynchronization();\n\nprivate slots:\n    /**\n     * @brief onCoreSeekChanged\n     */\n    void onCoreSeekChanged(RVA addr, CutterCore::SeekHistoryType type);\n\nprivate:\n    /**\n     * @brief widgetOffset widget seek location.\n     */\n    RVA widgetOffset = RVA_INVALID;\n\n    /**\n     * @brief previousOffset last seek location.\n     * @todo maybe use an actual history?\n     */\n    RVA previousOffset = RVA_INVALID;\n\n    /**\n     * @brief synchronized tells with the seekable's offset is\n     * synchronized with core or not.\n     */\n    bool synchronized = true;\n\n    /**\n     * @brief internal method for changing the seek\n     * @param localOnly whether the seek should be updated globally if synchronized\n     */\n    void updateSeek(RVA addr, CutterCore::SeekHistoryType type, bool localOnly);\n\nsignals:\n    void seekableSeekChanged(RVA addr, CutterCore::SeekHistoryType type);\n    void syncChanged();\n};\n"
  },
  {
    "path": "src/common/Decompiler.cpp",
    "content": "\n#include \"Decompiler.h\"\n#include \"Cutter.h\"\n\n#include <QJsonObject>\n#include <QJsonArray>\n\nDecompiler::Decompiler(const QString &id, const QString &name, QObject *parent)\n    : QObject(parent), id(id), name(name)\n{\n}\n\nRzAnnotatedCode *Decompiler::makeWarning(QString warningMessage)\n{\n    std::string temporary = warningMessage.toStdString();\n    return rz_annotated_code_new(strdup(temporary.c_str()));\n}\n"
  },
  {
    "path": "src/common/Decompiler.h",
    "content": "#ifndef DECOMPILER_H\n#define DECOMPILER_H\n\n#include \"CutterCommon.h\"\n#include \"RizinTask.h\"\n#include <rz_util/rz_annotated_code.h>\n\n#include <QString>\n#include <QObject>\n\n/**\n * Implements a decompiler that can be registered using CutterCore::registerDecompiler()\n */\nclass CUTTER_EXPORT Decompiler : public QObject\n{\n    Q_OBJECT\n\nprivate:\n    const QString id;\n    const QString name;\n\npublic:\n    Decompiler(const QString &id, const QString &name, QObject *parent = nullptr);\n    virtual ~Decompiler() = default;\n\n    static RzAnnotatedCode *makeWarning(QString warningMessage);\n\n    QString getId() const { return id; }\n    QString getName() const { return name; }\n    virtual bool isRunning() { return false; }\n    virtual bool isCancelable() { return false; }\n\n    virtual void decompileAt(RVA addr) = 0;\n    virtual void cancel() {}\n\nsignals:\n    void finished(RzAnnotatedCode *codeDecompiled);\n};\n\n#endif // DECOMPILER_H\n"
  },
  {
    "path": "src/common/DecompilerHighlighter.cpp",
    "content": "\n#include \"DecompilerHighlighter.h\"\n#include \"common/Configuration.h\"\n\n#include <memory>\n\nDecompilerHighlighter::DecompilerHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)\n{\n    setupTheme();\n    connect(Config(), &Configuration::colorsUpdated, this, [this]() {\n        setupTheme();\n        rehighlight();\n    });\n}\n\nvoid DecompilerHighlighter::setAnnotations(RzAnnotatedCode *code)\n{\n    this->code = code;\n}\n\nvoid DecompilerHighlighter::setupTheme()\n{\n    struct\n    {\n        RSyntaxHighlightType type;\n        QString name;\n    } mapping[] = {\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_KEYWORD, \"pop\" },\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_COMMENT, \"comment\" },\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_DATATYPE, \"func_var_type\" },\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME, \"fname\" },\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER, \"args\" },\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE, \"func_var\" },\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE, \"num\" },\n        { RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE, \"flag\" },\n    };\n    for (const auto &pair : mapping) {\n        assert(pair.type < format.size());\n        format[pair.type].setForeground(Config()->getColor(pair.name));\n    }\n}\n\nvoid DecompilerHighlighter::highlightBlock(const QString &)\n{\n    if (!code) {\n        return;\n    }\n    auto block = currentBlock();\n    size_t start = block.position();\n    size_t end = block.position() + block.length();\n\n    auto annotations = fromOwned(rz_annotated_code_annotations_range(code, start, end));\n    void **iter;\n    rz_pvector_foreach(annotations.get(), iter)\n    {\n        RzCodeAnnotation *annotation = static_cast<RzCodeAnnotation *>(*iter);\n        if (annotation->type != RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT) {\n            continue;\n        }\n        auto type = annotation->syntax_highlight.type;\n        if (size_t(type) >= HIGHLIGHT_COUNT) {\n            continue;\n        }\n        auto annotationStart = annotation->start;\n        if (annotationStart < start) {\n            annotationStart = 0;\n        } else {\n            annotationStart -= start;\n        }\n        auto annotationEnd = annotation->end - start;\n\n        setFormat(annotationStart, annotationEnd - annotationStart, format[type]);\n    }\n}\n"
  },
  {
    "path": "src/common/DecompilerHighlighter.h",
    "content": "#ifndef DECOMPILER_HIGHLIGHTER_H\n#define DECOMPILER_HIGHLIGHTER_H\n\n#include \"CutterCommon.h\"\n#include <rz_util/rz_annotated_code.h>\n#include <QSyntaxHighlighter>\n#include <QTextDocument>\n#include <QTextCharFormat>\n#include <array>\n\n/**\n * \\brief SyntaxHighlighter based on annotations from decompiled code.\n * Can be only used in combination with DecompilerWidget.\n */\nclass CUTTER_EXPORT DecompilerHighlighter : public QSyntaxHighlighter\n{\n    Q_OBJECT\n\npublic:\n    DecompilerHighlighter(QTextDocument *parent = nullptr);\n    virtual ~DecompilerHighlighter() = default;\n\n    /**\n     * @brief Set the code with annotations to be used for highlighting.\n     *\n     * It is callers responsibility to ensure that it is synchronized with currentTextDocument and\n     * has sufficiently long lifetime.\n     *\n     * @param code\n     */\n    void setAnnotations(RzAnnotatedCode *code);\n\nprotected:\n    void highlightBlock(const QString &text) override;\n\nprivate:\n    void setupTheme();\n\n    static const int HIGHLIGHT_COUNT = RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE + 1;\n    std::array<QTextCharFormat, HIGHLIGHT_COUNT> format;\n    RzAnnotatedCode *code = nullptr;\n};\n\n#endif\n"
  },
  {
    "path": "src/common/DirectionalComboBox.cpp",
    "content": "#include \"DirectionalComboBox.h\"\n\nDirectionalComboBox::DirectionalComboBox(QWidget *parent, bool upwards)\n    : QComboBox(parent), popupUpwards(upwards)\n{\n}\n\nvoid DirectionalComboBox::showPopup()\n{\n    QComboBox::showPopup();\n    QWidget *popup = this->findChild<QFrame *>();\n    if (popupUpwards) {\n        popup->move(popup->x(), mapToGlobal(this->rect().bottomLeft()).y() - popup->height());\n    } else {\n        popup->move(popup->x(), mapToGlobal(this->rect().topLeft()).y());\n    }\n}\n\nvoid DirectionalComboBox::setPopupDirection(bool upwards)\n{\n    popupUpwards = upwards;\n}\n"
  },
  {
    "path": "src/common/DirectionalComboBox.h",
    "content": "#pragma once\n\n#include <QComboBox>\n/**\n * @brief Custom QComboBox created to prevent the menu popup from opening up at different\n *        offsets for different items, which may result in list items being rendered outside\n *        of the screen/containing widget.\n */\nclass DirectionalComboBox : public QComboBox\n{\n    Q_OBJECT\n\npublic:\n    explicit DirectionalComboBox(QWidget *parent = nullptr, bool upwards = true);\n\n    void setPopupDirection(bool upwards);\n\nprivate:\n    bool popupUpwards;\n\n    void showPopup();\n};\n"
  },
  {
    "path": "src/common/DisassemblyHelper.cpp",
    "content": "#include \"DisassemblyHelper.h\"\n#include \"Cutter.h\"\n\nDisassemblyTextBlockUserData::DisassemblyTextBlockUserData(const DisassemblyLine &line)\n    : line { line }\n{\n}\n\nDisassemblyTextBlockUserData *DisassemblyHelper::getUserData(const QTextBlock &block)\n{\n    QTextBlockUserData *userData = block.userData();\n    if (!userData) {\n        return nullptr;\n    }\n\n    return static_cast<DisassemblyTextBlockUserData *>(userData);\n}\n\nRVA DisassemblyHelper::getXRefFromWord(RVA offset, const QString &selectedWord)\n{\n    RVA selectedOffset = Core()->num(selectedWord);\n    auto xrefsTo = Core()->getXRefs(offset, true, false);\n    for (const auto &xref : xrefsTo) {\n        if (xref.from == selectedOffset) {\n            return xref.from;\n        }\n    }\n    return RVA_INVALID;\n}\n\nbool DisassemblyHelper::isXRefFromComment(RVA offset, const QString &line)\n{\n    return Core()->getXRefCommentAt(offset).simplified().contains(line.simplified());\n}\n\nRVA DisassemblyHelper::readDisassemblyOffset(QTextCursor tc)\n{\n    auto userData = DisassemblyHelper::getUserData(tc.block());\n    if (!userData) {\n        return RVA_INVALID;\n    }\n\n    return userData->line.offset;\n}\n\nRVA DisassemblyHelper::readDisassemblyArrow(QTextCursor tc)\n{\n    auto userData = getUserData(tc.block());\n    if (!userData) {\n        return RVA_INVALID;\n    }\n\n    return userData->line.arrow;\n}\n\nDisassemblyHelper::TargetContext DisassemblyHelper::getContextFromCursor(QTextCursor tc)\n{\n    tc.select(QTextCursor::WordUnderCursor);\n    TargetContext ctx;\n    ctx.word = tc.selectedText();\n    ctx.line = tc.block().text();\n    ctx.offset = DisassemblyHelper::readDisassemblyOffset(tc);\n    ctx.arrow = DisassemblyHelper::readDisassemblyArrow(tc);\n    return ctx;\n}\n\nDisassemblyHelper::TargetAction DisassemblyHelper::resolveTarget(const TargetContext &ctx,\n                                                                 int filter)\n{\n    TargetAction res = { RVA_INVALID, TargetType::None };\n\n    // Xref comments need special handling to show preview for each caller offset\n    if (filter & TargetFilter::XRefComments) {\n        bool showXRefComments = Core()->getConfigb(\"asm.xrefs\");\n        if (showXRefComments && isXRefFromComment(ctx.offset, ctx.line)) {\n            res.offset = getXRefFromWord(ctx.offset, ctx.word);\n            res.type = TargetType::XRefComment;\n            return res;\n        }\n    }\n\n    if (filter & TargetFilter::Variables) {\n        XrefDescription xref = Core()->getFirstXRefForVariable(ctx.word, ctx.offset);\n        if (!xref.from_str.isEmpty() || !xref.to_str.isEmpty()) {\n            res.offset = xref.from;\n            res.type = TargetType::VariableName;\n            return res;\n        }\n    }\n\n    if (filter & TargetFilter::Types) {\n        if (Core()->typeExists(ctx.word)) {\n            res.type = TargetType::TypeName;\n            return res;\n        }\n    }\n\n    if (filter & TargetFilter::Arrows) {\n        if (ctx.arrow != RVA_INVALID) {\n            res.type = TargetType::Arrow;\n            res.offset = ctx.arrow;\n            return res;\n        }\n    }\n\n    return res;\n}\n"
  },
  {
    "path": "src/common/DisassemblyHelper.h",
    "content": "#ifndef DISASSEMBLYHELPER_H\n#define DISASSEMBLYHELPER_H\n\n#include <QTextBlockUserData>\n#include \"core/CutterDescriptions.h\"\n\n/**\n * @brief Metadata container attached to each QTextBlock in the disassembly view\n *\n * Each visible line in disassembly related widgets is treated as a single text block\n */\nclass DisassemblyTextBlockUserData : public QTextBlockUserData\n{\npublic:\n    DisassemblyLine line;\n\n    explicit DisassemblyTextBlockUserData(const DisassemblyLine &line);\n};\n\n/**\n * @brief Helpers for translating UI elements (words, lines) into disassembly data\n */\nnamespace DisassemblyHelper {\n\n/**\n * @brief Identifies what kind of item was clicked or hovered\n */\nenum class TargetType {\n    VariableName,\n    TypeName,\n    XRefComment,\n    Arrow,\n    None,\n};\n\n/**\n * @brief Data used to figure out what is under the mouse\n */\nstruct TargetContext\n{\n    RVA offset;\n    RVA arrow;\n    QString word;\n    QString line;\n};\n\n/**\n * @brief What was found after checking the context\n */\nstruct TargetAction\n{\n    RVA offset;\n    TargetType type;\n};\n\n/**\n * @brief Filter to control how deep the search goes\n */\nenum TargetFilter {\n    XRefComments = 1 << 0,\n    Variables = 1 << 1,\n    Types = 1 << 2,\n    Arrows = 1 << 3,\n\n    All = XRefComments | Variables | Types | Arrows\n};\n\nDisassemblyTextBlockUserData *getUserData(const QTextBlock &block);\n\n/**\n * @brief Finds the source (from) address of an XRef based on the text word under the cursor\n * @param offset The base offset of the line which contains an XREF to it\n * @param selectedWord The specific text string being hovered (must be an address)\n * @return The source RVA of the XRef, or RVA_INVALID if not found\n */\nRVA getXRefFromWord(RVA offset, const QString &selectedWord);\n\n/**\n * @brief Checks if a disassembly line is an auto-generated XRef metadata line\n * @param offset The offset of the current disassembly line/block\n * @param line The full text content of the disassembly line/block\n * @return True if the line is an analysis-generated XRef comment\n */\nbool isXRefFromComment(RVA offset, const QString &line);\n\n/*!\n * @brief Reads the offset for the cursor position\n * @return The disassembly offset of the hovered asm text\n */\nRVA readDisassemblyOffset(QTextCursor tc);\n\n/*!\n * @brief Reads the arrow offset for the cursor position\n * @return Offset the arrow points to\n */\nRVA readDisassemblyArrow(QTextCursor tc);\n\n/**\n * @brief Gets the text and address at the current cursor position\n * @param tc The cursor to check\n * @return TargetContext containing info about the offset, word and line at the cursor\n */\nTargetContext getContextFromCursor(QTextCursor tc);\n\n/**\n * @brief Identifies what a context points to (like a variable or type)\n * @param ctx The context found at the cursor\n * @param filter Limits what kind of items to look for\n * @return The result containing the address and type found\n */\nTargetAction resolveTarget(const TargetContext &ctx, int filter = TargetFilter::All);\n}\n\n#endif // DISASSEMBLYHELPER_H\n"
  },
  {
    "path": "src/common/DisassemblyPreview.cpp",
    "content": "#include \"DisassemblyPreview.h\"\n#include \"Configuration.h\"\n#include \"widgets/GraphView.h\"\n\n#include <QCoreApplication>\n#include <QWidget>\n#include <QToolTip>\n#include <QProcessEnvironment>\n\nnamespace DH = DisassemblyHelper;\n\nQString DisassemblyPreview::getToolTipStyleSheet()\n{\n    return QString { \"QToolTip { border-width: 1px; max-width: %1px;\"\n                     \"opacity: 230; background-color: %2;\"\n                     \"color: %3; border-color: %3;}\" }\n            .arg(400)\n            .arg(Config()->getColor(\"gui.tooltip.background\").name())\n            .arg(Config()->getColor(\"gui.tooltip.foreground\").name());\n}\n\nbool DisassemblyPreview::showDisasPreview(QWidget *parent, const QPoint &pointOfEvent,\n                                          const RVA offsetFrom)\n{\n    QList<XrefDescription> refs = Core()->getXRefs(offsetFrom, false, false);\n    if (refs.length()) {\n        if (refs.length() > 1) {\n            qWarning() << QObject::tr(\n                                  \"More than one (%1) references here. Weird behaviour expected.\")\n                                  .arg(refs.length());\n        }\n\n        RVA offsetTo = refs.at(0).to; // This is the offset we want to preview\n        /*\n         * Only if the offset we point *to* is different from the one the cursor is currently\n         * on *and* the former is a valid offset, we are allowed to get a preview of offsetTo\n         */\n        if (offsetTo != offsetFrom && offsetTo != RVA_INVALID) {\n            return showDisasPreviewAt(parent, pointOfEvent, offsetTo);\n        }\n    }\n    return false;\n}\n\nbool DisassemblyPreview::showDisasPreviewAt(QWidget *parent, const QPoint &pointOfEvent,\n                                            const RVA offset)\n{\n    QStringList disasmPreview = Core()->getDisassemblyPreview(offset, 10);\n    if (!disasmPreview.isEmpty()) {\n        const QFont &fnt = Config()->getFont();\n        QString tooltip = QString { \"<html><div style=\\\"font-family: %1; font-size: %2pt; \"\n                                    \"white-space: nowrap;\\\"><div style=\\\"margin-bottom: \"\n                                    \"10px;\\\"><strong>Disassembly Preview</strong>:<br>%3<div>\" }\n                                  .arg(fnt.family())\n                                  .arg(qMax(8, fnt.pointSize() - 1))\n                                  .arg(disasmPreview.join(\"<br>\"));\n\n        QToolTip::showText(pointOfEvent, tooltip, parent, QRect {}, 3500);\n        return true;\n    }\n\n    return false;\n}\n\ntypedef struct mmio_lookup_context\n{\n    QString selected;\n    RVA mmio_address;\n} mmio_lookup_context_t;\n\nstatic bool lookup_mmio_addr_cb(void *user, const ut64 key, const void *value)\n{\n    mmio_lookup_context_t *ctx = (mmio_lookup_context_t *)user;\n    if (ctx->selected == (const char *)value) {\n        ctx->mmio_address = key;\n        return false;\n    }\n    return true;\n}\n\nbool DisassemblyPreview::showDebugValueTooltip(QWidget *parent, const QPoint &pointOfEvent,\n                                               const QString &selectedText, const RVA offset)\n{\n    if (selectedText.isEmpty())\n        return false;\n\n    if (selectedText.at(0).isLetter()) {\n        {\n            const auto registerRefs = Core()->getRegisterRefValues();\n            for (auto &reg : registerRefs) {\n                if (reg.name == selectedText) {\n                    auto msg = QString(\"reg %1 = %2\").arg(reg.name, reg.value);\n                    QToolTip::showText(pointOfEvent, msg, parent);\n                    return true;\n                }\n            }\n        }\n\n        if (offset != RVA_INVALID) {\n            auto vars = Core()->getVariables(offset);\n            for (auto &var : vars) {\n                if (var.name == selectedText) {\n                    auto msg = QString(\"var %1 = %2\").arg(var.name, var.value);\n                    QToolTip::showText(pointOfEvent, msg, parent);\n                    return true;\n                }\n            }\n        }\n\n        {\n            // Lookup MMIO address\n            mmio_lookup_context_t ctx;\n            ctx.selected = selectedText;\n            ctx.mmio_address = RVA_INVALID;\n            auto core = Core()->lock();\n            RzPlatformTarget *arch_target = core->analysis->arch_target;\n            if (arch_target && arch_target->profile) {\n                ht_up_foreach(arch_target->profile->registers_mmio, lookup_mmio_addr_cb, &ctx);\n            }\n            if (ctx.mmio_address != RVA_INVALID) {\n                int len = 8; // TODO: Determine proper len of mmio address for the cpu\n                if (char *r = rz_core_print_hexdump_or_hexdiff_str(core, RZ_OUTPUT_MODE_STANDARD,\n                                                                   ctx.mmio_address, len, false)) {\n                    auto val = QString::fromUtf8(r).trimmed().split(\"\\n\").last();\n                    auto msg = QString(\"mmio %1 %2\").arg(selectedText, val);\n                    free(r);\n                    QToolTip::showText(pointOfEvent, msg, parent);\n                    return true;\n                }\n            }\n        }\n    }\n    // Else show preview for value?\n    return false;\n}\n\nbool DisassemblyPreview::showTooltip(QWidget *parent, const QPoint &globalPos,\n                                     const DH::TargetContext &ctx, bool hasPreview)\n{\n    bool isWordEmpty = ctx.word.isEmpty();\n    if (hasPreview) {\n        auto ta = DH::resolveTarget(ctx, DH::XRefComments | DH::Arrows);\n\n        if (!isWordEmpty && ta.type == DH::TargetType::XRefComment) {\n            if (ta.offset != RVA_INVALID) {\n                showDisasPreviewAt(parent, globalPos, ta.offset);\n            }\n            // consume the event even if the text under cursor is not an address, this prevents\n            // jumping to incorrect offset (offset pointed to by the next instruction line)\n            // when double clicking on auto generated XREF comment\n            return true;\n        }\n\n        if (ta.type == DH::TargetType::Arrow && showDisasPreviewAt(parent, globalPos, ta.offset)) {\n            return true;\n        }\n\n        if (showDisasPreview(parent, globalPos, ctx.offset)) {\n            return true;\n        }\n    }\n\n    if (Config()->getShowVarTooltips() && !isWordEmpty\n        && showDebugValueTooltip(parent, globalPos, ctx.word, ctx.offset)) {\n        return true;\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "src/common/DisassemblyPreview.h",
    "content": "#ifndef DISASSEMBLYPREVIEW_H\n#define DISASSEMBLYPREVIEW_H\n\n#include <QTextBlockUserData>\n#include \"core/CutterDescriptions.h\"\n#include \"DisassemblyHelper.h\"\n\nclass QWidget;\n\n/**\n * @brief Namespace to define relevant functions\n *\n * @ingroup DisassemblyPreview\n */\nnamespace DisassemblyPreview {\n/*!\n * @brief Get the QString that defines the stylesheet for tooltip\n * @return A QString for the stylesheet\n */\nQString getToolTipStyleSheet();\n\n/*!\n * @brief Show a QToolTip that previews the disassembly that is pointed to\n * It works for GraphWidget and DisassemblyWidget\n * @return True if the tooltip is shown\n */\nbool showDisasPreview(QWidget *parent, const QPoint &pointOfEvent, const RVA offsetFromk);\n\n/**\n * @brief Show a QToolTip that previews the disassembly at a specific address\n * @return True if the tooltip is shown\n */\nbool showDisasPreviewAt(QWidget *parent, const QPoint &pointOfEvent, const RVA offset);\n\n/**\n * @brief Show a QToolTip that shows the value of the highlighted register, variable, or memory\n * @return True if the tooltip is shown\n */\nbool showDebugValueTooltip(QWidget *parent, const QPoint &pointOfEvent, const QString &selectedText,\n                           const RVA offset);\n\n/**\n * @brief Displays a tooltip or preview window based on the item under the cursor\n * @param parent The widget that owns and positions the tooltip\n * @param globalPos The screen position where the tooltip should appear\n * @param ctx The context identifying the word and address under the mouse\n * @param hasPreview The config value indicating whether preview should be shown for the widget\n * @return True if a tooltip was shown or the event was handled, false otherwise\n */\nbool showTooltip(QWidget *parent, const QPoint &globalPos,\n                 const DisassemblyHelper::TargetContext &ctx, bool hasPreview);\n}\n#endif\n"
  },
  {
    "path": "src/common/FunctionsTask.h",
    "content": "\n#ifndef FUNCTIONSTASK_H\n#define FUNCTIONSTASK_H\n\n#include \"common/AsyncTask.h\"\n#include \"core/Cutter.h\"\n\nclass FunctionsTask : public AsyncTask\n{\n    Q_OBJECT\n\npublic:\n    QString getTitle() override { return tr(\"Fetching Functions\"); }\n\nsignals:\n    void fetchFinished(const QList<FunctionDescription> &strings);\n\nprotected:\n    void runTask() override\n    {\n        auto functions = Core()->getAllFunctions();\n        emit fetchFinished(functions);\n    }\n};\n\n#endif // FUNCTIONSTASK_H\n"
  },
  {
    "path": "src/common/Helpers.cpp",
    "content": "#include \"common/Helpers.h\"\n#include \"Configuration.h\"\n\n#include <cmath>\n#include <QPlainTextEdit>\n#include <QTextEdit>\n#include <QFileInfo>\n#include <QtCore>\n#include <QCryptographicHash>\n#include <QTreeWidget>\n#include <QString>\n#include <QAbstractItemView>\n#include <QAbstractButton>\n#include <QDockWidget>\n#include <QMenu>\n#include <QComboBox>\n\nstatic QAbstractItemView::ScrollMode scrollMode()\n{\n    const bool use_scrollperpixel = true;\n    return use_scrollperpixel ? QAbstractItemView::ScrollPerPixel\n                              : QAbstractItemView::ScrollPerItem;\n}\n\nnamespace qhelpers {\n\nQString formatBytecount(const uint64_t bytecount)\n{\n    if (bytecount == 0) {\n        return \"0\";\n    }\n\n    const int exp = log(bytecount) / log(1024);\n    constexpr char suffixes[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E' };\n\n    QString str;\n    QTextStream stream(&str);\n    stream << qSetRealNumberPrecision(3) << bytecount / pow(1024, exp) << ' ' << suffixes[exp]\n           << 'B';\n    return stream.readAll();\n}\n\nvoid adjustColumns(QTreeView *tv, int columnCount, int padding)\n{\n    for (int i = 0; i != columnCount; ++i) {\n        tv->resizeColumnToContents(i);\n        if (padding > 0) {\n            int width = tv->columnWidth(i);\n            tv->setColumnWidth(i, width + padding);\n        }\n    }\n}\n\nvoid adjustColumns(QTreeWidget *tw, int padding)\n{\n    adjustColumns(tw, tw->columnCount(), padding);\n}\n\nQTreeWidgetItem *appendRow(QTreeWidget *tw, const QString &str, const QString &str2,\n                           const QString &str3, const QString &str4, const QString &str5)\n{\n    QTreeWidgetItem *tempItem = new QTreeWidgetItem();\n    // Fill dummy hidden column\n    tempItem->setText(0, \"0\");\n    tempItem->setText(1, str);\n    if (!str2.isNull())\n        tempItem->setText(2, str2);\n    if (!str3.isNull())\n        tempItem->setText(3, str3);\n    if (!str4.isNull())\n        tempItem->setText(4, str4);\n    if (!str5.isNull())\n        tempItem->setText(5, str5);\n\n    tw->insertTopLevelItem(0, tempItem);\n\n    return tempItem;\n}\n\n/**\n * @brief Select first item of a QAbstractItemView if not empty\n * @param itemView\n * @return true if first item was selected\n */\nbool selectFirstItem(QAbstractItemView *itemView)\n{\n    if (!itemView) {\n        return false;\n    }\n    auto model = itemView->model();\n    if (!model) {\n        return false;\n    }\n    if (model->hasIndex(0, 0)) {\n        itemView->setCurrentIndex(model->index(0, 0));\n        return true;\n    }\n    return false;\n}\n\nvoid setVerticalScrollMode(QAbstractItemView *tw)\n{\n    tw->setVerticalScrollMode(scrollMode());\n}\n\nvoid setCheckedWithoutSignals(QAbstractButton *button, bool checked)\n{\n    bool blocked = button->signalsBlocked();\n    button->blockSignals(true);\n    button->setChecked(checked);\n    button->blockSignals(blocked);\n}\n\nSizePolicyMinMax forceWidth(QWidget *widget, int width)\n{\n    SizePolicyMinMax r;\n    r.sizePolicy = widget->sizePolicy();\n    r.min = widget->minimumWidth();\n    r.max = widget->maximumWidth();\n\n    QSizePolicy sizePolicy = r.sizePolicy;\n    sizePolicy.setHorizontalPolicy(QSizePolicy::Fixed);\n    widget->setSizePolicy(sizePolicy);\n    widget->setMinimumWidth(width);\n    widget->setMaximumWidth(width);\n\n    return r;\n}\n\nSizePolicyMinMax forceHeight(QWidget *widget, int height)\n{\n    SizePolicyMinMax r;\n    r.sizePolicy = widget->sizePolicy();\n    r.min = widget->minimumHeight();\n    r.max = widget->maximumHeight();\n\n    QSizePolicy sizePolicy = r.sizePolicy;\n    sizePolicy.setVerticalPolicy(QSizePolicy::Fixed);\n    widget->setSizePolicy(sizePolicy);\n    widget->setMinimumHeight(height);\n    widget->setMaximumHeight(height);\n\n    return r;\n}\n\nvoid SizePolicyMinMax::restoreWidth(QWidget *widget)\n{\n    widget->setSizePolicy(sizePolicy.horizontalPolicy(), widget->sizePolicy().verticalPolicy());\n    widget->setMinimumWidth(min);\n    widget->setMaximumWidth(max);\n}\n\nvoid SizePolicyMinMax::restoreHeight(QWidget *widget)\n{\n    widget->setSizePolicy(widget->sizePolicy().horizontalPolicy(), sizePolicy.verticalPolicy());\n    widget->setMinimumHeight(min);\n    widget->setMaximumHeight(max);\n}\n\nint getMaxFullyDisplayedLines(QTextEdit *textEdit)\n{\n    QFontMetrics fontMetrics(textEdit->document()->defaultFont());\n    return (textEdit->height()\n            - (textEdit->contentsMargins().top() + textEdit->contentsMargins().bottom()\n               + (int)(textEdit->document()->documentMargin() * 2)))\n            / fontMetrics.lineSpacing();\n}\n\nint getMaxFullyDisplayedLines(QPlainTextEdit *plainTextEdit)\n{\n    QFontMetrics fontMetrics(plainTextEdit->document()->defaultFont());\n    return (plainTextEdit->height()\n            - (plainTextEdit->contentsMargins().top() + plainTextEdit->contentsMargins().bottom()\n               + (int)(plainTextEdit->document()->documentMargin() * 2)))\n            / fontMetrics.lineSpacing();\n}\n\nQByteArray applyColorToSvg(const QByteArray &data, QColor color)\n{\n    static const QRegularExpression styleRegExp(\n            \"(?:style=\\\".*fill:(.*?);.*?\\\")|(?:fill=\\\"(.*?)\\\")\");\n\n    QString replaceStr = QString(\"#%1\").arg(color.rgb() & 0xffffff, 6, 16, QLatin1Char('0'));\n    int replaceStrLen = replaceStr.length();\n\n    QString xml = QString::fromUtf8(data);\n\n    int offset = 0;\n    while (true) {\n        QRegularExpressionMatch match = styleRegExp.match(xml, offset);\n        if (!match.hasMatch()) {\n            break;\n        }\n\n        int captureIndex = match.captured(1).isNull() ? 2 : 1;\n        xml.replace(match.capturedStart(captureIndex), match.capturedLength(captureIndex),\n                    replaceStr);\n        offset = match.capturedStart(captureIndex) + replaceStrLen;\n    }\n\n    return xml.toUtf8();\n}\n\nQByteArray applyColorToSvg(const QString &filename, QColor color)\n{\n    QFile file(filename);\n    file.open(QIODevice::ReadOnly);\n\n    return applyColorToSvg(file.readAll(), color);\n}\n\n/**\n * @brief finds the theme-specific icon path and calls `setter` functor providing a pointer of an\n * object which has to be used and loaded icon\n * @param supportedIconsNames list of <object ptr, icon name>\n * @param setter functor which has to be called\n *   for example we need to set an action icon, the functor can be just [](void* o, const QIcon\n * &icon) { static_cast<QAction*>(o)->setIcon(icon); }\n */\nvoid setThemeIcons(QList<QPair<void *, QString>> supportedIconsNames,\n                   std::function<void(void *, const QIcon &)> setter)\n{\n    if (supportedIconsNames.isEmpty() || !setter) {\n        return;\n    }\n\n    const QString &iconsDirPath = QStringLiteral(\":/img/icons/\");\n    const QString &currTheme = Config()->getCurrentTheme()->name;\n    const QString &relativeThemeDir = currTheme.toLower() + \"/\";\n    QString iconPath;\n\n    foreach (const auto &p, supportedIconsNames) {\n        iconPath = iconsDirPath;\n        // Verify that indeed there is an alternative icon in this theme\n        // Otherwise, fallback to the regular icon folder\n        if (QFile::exists(iconPath + relativeThemeDir + p.second)) {\n            iconPath += relativeThemeDir;\n        }\n        setter(p.first, QIcon(iconPath + p.second));\n    }\n}\n\nvoid prependQAction(QAction *action, QMenu *menu)\n{\n    auto actions = menu->actions();\n    if (actions.empty()) {\n        menu->addAction(action);\n    } else {\n        menu->insertAction(actions.first(), action);\n    }\n}\n\nqreal devicePixelRatio(const QPaintDevice *p)\n{\n#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)\n    return p->devicePixelRatioF();\n#else\n    return p->devicePixelRatio();\n#endif\n}\n\nvoid selectIndexByData(QComboBox *widget, QVariant data, int defaultIndex)\n{\n    for (int i = 0; i < widget->count(); i++) {\n        if (widget->itemData(i) == data) {\n            widget->setCurrentIndex(i);\n            return;\n        }\n    }\n    widget->setCurrentIndex(defaultIndex);\n}\n\nvoid emitColumnChanged(QAbstractItemModel *model, int column)\n{\n    if (model && model->rowCount()) {\n        emit model->dataChanged(model->index(0, column),\n                                model->index(model->rowCount() - 1, column), { Qt::DisplayRole });\n    }\n}\n\nbool filterStringContains(const QString &string, const QSortFilterProxyModel *model)\n{\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    return string.contains(model->filterRegExp());\n#else\n    return string.contains(model->filterRegularExpression());\n#endif\n}\n\nQPointF mouseEventPos(QMouseEvent *ev)\n{\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    return ev->localPos();\n#else\n    return ev->position();\n#endif\n}\n\nQPoint mouseEventGlobalPos(QMouseEvent *ev)\n{\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    return ev->globalPos();\n#else\n    return ev->globalPosition().toPoint();\n#endif\n}\n\n} // end namespace\n"
  },
  {
    "path": "src/common/Helpers.h",
    "content": "#ifndef QHELPERS_H\n#define QHELPERS_H\n\n#include \"core/CutterCommon.h\"\n\n#include <QString>\n#include <QColor>\n#include <QSizePolicy>\n#include <functional>\n\nclass QIcon;\nclass QPlainTextEdit;\nclass QTextEdit;\nclass QString;\nclass QTreeWidget;\nclass QTreeWidgetItem;\nclass QAbstractItemView;\nclass QAbstractItemModel;\nclass QAbstractButton;\nclass QWidget;\nclass QTreeView;\nclass QAction;\nclass QMenu;\nclass QPaintDevice;\nclass QComboBox;\nclass QSortFilterProxyModel;\nclass QMouseEvent;\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n#    define CUTTER_QT_SKIP_EMPTY_PARTS QString::SkipEmptyParts\n#else\n#    define CUTTER_QT_SKIP_EMPTY_PARTS Qt::SkipEmptyParts\n#endif\n\nnamespace qhelpers {\nCUTTER_EXPORT QString formatBytecount(const uint64_t bytecount);\nCUTTER_EXPORT void adjustColumns(QTreeView *tv, int columnCount, int padding);\nCUTTER_EXPORT void adjustColumns(QTreeWidget *tw, int padding);\nCUTTER_EXPORT bool selectFirstItem(QAbstractItemView *itemView);\nCUTTER_EXPORT QTreeWidgetItem *appendRow(QTreeWidget *tw, const QString &str,\n                                         const QString &str2 = QString(),\n                                         const QString &str3 = QString(),\n                                         const QString &str4 = QString(),\n                                         const QString &str5 = QString());\n\nCUTTER_EXPORT void setVerticalScrollMode(QAbstractItemView *tw);\n\nCUTTER_EXPORT void setCheckedWithoutSignals(QAbstractButton *button, bool checked);\n\nstruct CUTTER_EXPORT SizePolicyMinMax\n{\n    QSizePolicy sizePolicy;\n    int min;\n    int max;\n\n    void restoreWidth(QWidget *widget);\n    void restoreHeight(QWidget *widget);\n};\n\nCUTTER_EXPORT SizePolicyMinMax forceWidth(QWidget *widget, int width);\nCUTTER_EXPORT SizePolicyMinMax forceHeight(QWidget *widget, int height);\n\nCUTTER_EXPORT int getMaxFullyDisplayedLines(QTextEdit *textEdit);\nCUTTER_EXPORT int getMaxFullyDisplayedLines(QPlainTextEdit *plainTextEdit);\n\nCUTTER_EXPORT QByteArray applyColorToSvg(const QByteArray &data, QColor color);\nCUTTER_EXPORT QByteArray applyColorToSvg(const QString &filename, QColor color);\n\nCUTTER_EXPORT void setThemeIcons(QList<QPair<void *, QString>> supportedIconsNames,\n                                 std::function<void(void *, const QIcon &)> setter);\n\nCUTTER_EXPORT void prependQAction(QAction *action, QMenu *menu);\nCUTTER_EXPORT qreal devicePixelRatio(const QPaintDevice *p);\n/**\n * @brief Select comboBox item by value in Qt::UserRole.\n * @param comboBox\n * @param data - value to search in combobox item data\n * @param defaultIndex - item to select in case no match\n */\nCUTTER_EXPORT void selectIndexByData(QComboBox *comboBox, QVariant data, int defaultIndex = -1);\n/**\n * @brief Emit data change signal in a model's column (DisplayRole)\n * @param model - model containing data with changes\n * @param column - column in the model\n */\nCUTTER_EXPORT void emitColumnChanged(QAbstractItemModel *model, int column);\n\nCUTTER_EXPORT bool filterStringContains(const QString &string, const QSortFilterProxyModel *model);\n\n#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)\nusing ColorFloat = float;\nusing KeyComb = QKeyCombination;\n#else\nusing ColorFloat = qreal;\nusing KeyComb = int;\n#endif\n\nCUTTER_EXPORT QPointF mouseEventPos(QMouseEvent *ev);\nCUTTER_EXPORT QPoint mouseEventGlobalPos(QMouseEvent *ev);\n\n} // qhelpers\n\n#endif // HELPERS_H\n"
  },
  {
    "path": "src/common/HighDpiPixmap.cpp",
    "content": "#include \"common/HighDpiPixmap.h\"\n\n#include <QGuiApplication>\n#include <QScreen>\n\nstatic qreal GetDevicePixelRatio(qreal devicePixelRatio)\n{\n    if (devicePixelRatio > 0) {\n        return devicePixelRatio;\n    }\n    qreal ratio = 1;\n    for (auto screen : QGuiApplication::screens()) {\n        ratio = std::max(ratio, screen->devicePixelRatio());\n    }\n    return ratio;\n}\n\nHighDpiPixmap::HighDpiPixmap(int width, int height, qreal devicePixelRatio)\n    : QPixmap(int(width * GetDevicePixelRatio(devicePixelRatio)),\n              int(height * GetDevicePixelRatio(devicePixelRatio)))\n{\n    setDevicePixelRatio(GetDevicePixelRatio(devicePixelRatio));\n}\n"
  },
  {
    "path": "src/common/HighDpiPixmap.h",
    "content": "#ifndef HIGHDPIPIXMAP_H\n#define HIGHDPIPIXMAP_H\n\n#include <QPixmap>\nclass HighDpiPixmap : public QPixmap\n{\npublic:\n    HighDpiPixmap(int width, int height, qreal devicePixelRatio = -1);\n};\n\n#endif // HIGHDPIPIXMAP_H\n"
  },
  {
    "path": "src/common/Highlighter.cpp",
    "content": "#include \"Highlighter.h\"\n#include \"core/MainWindow.h\"\n\n#include <QtGui>\n\nHighlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)\n{\n    HighlightingRule rule;\n\n    core = Core();\n\n    regFormat.setForeground(QColor(236, 100, 75));\n\n    for (const QString &pattern : this->core->regs) {\n        rule.pattern.setPattern(\"\\\\b\" + pattern + \"\\\\b\");\n        rule.pattern.setPatternOptions(QRegularExpression::CaseInsensitiveOption);\n        rule.format = regFormat;\n        highlightingRules.append(rule);\n    }\n\n    singleLineCommentFormat.setFontWeight(QFont::Bold);\n    singleLineCommentFormat.setForeground(QColor(63, 195, 128));\n    rule.pattern.setPattern(\";[^\\n]*\");\n    rule.format = singleLineCommentFormat;\n    highlightingRules.append(rule);\n\n    commentStartRegularExpression.setPattern(\"/\\\\*\");\n    commentEndRegularExpression.setPattern(\"\\\\*/\");\n}\n\nvoid Highlighter::highlightBlock(const QString &text)\n{\n    for (const HighlightingRule &rule : highlightingRules) {\n        QRegularExpression expression(rule.pattern);\n        int index = expression.match(text).capturedStart();\n        while (index >= 0) {\n            int length = expression.match(text).capturedLength();\n            setFormat(index, length, rule.format);\n            index = expression.match(text.mid(index + length)).capturedStart();\n        }\n    }\n    setCurrentBlockState(0);\n\n    int startIndex = 0;\n    if (previousBlockState() != 1)\n        startIndex = QRegularExpression(commentStartRegularExpression).match(text).capturedStart();\n\n    while (startIndex >= 0) {\n        QRegularExpressionMatch commentEndMatch =\n                QRegularExpression(commentEndRegularExpression).match(text.mid(startIndex));\n        int endIndex = commentEndMatch.capturedStart();\n        int commentLength;\n        if (endIndex == -1) {\n            setCurrentBlockState(1);\n            commentLength = text.length() - startIndex;\n        } else {\n            commentLength = endIndex - startIndex + commentEndMatch.capturedLength();\n        }\n        setFormat(startIndex, commentLength, multiLineCommentFormat);\n        startIndex = QRegularExpression(commentStartRegularExpression)\n                             .match(text.mid(startIndex + commentLength))\n                             .capturedStart();\n    }\n}\n"
  },
  {
    "path": "src/common/Highlighter.h",
    "content": "#ifndef HIGHLIGHTER_H\n#define HIGHLIGHTER_H\n\n#include \"core/Cutter.h\"\n\n#include <QSyntaxHighlighter>\n#include <QHash>\n#include <QTextCharFormat>\n#include <QRegularExpression>\n\nclass QTextDocument;\nclass MainWindow;\n\nclass Highlighter : public QSyntaxHighlighter\n{\n    Q_OBJECT\n\npublic:\n    Highlighter(QTextDocument *parent = nullptr);\n\nprotected:\n    void highlightBlock(const QString &text);\n\nprivate:\n    CutterCore *core;\n\n    struct HighlightingRule\n    {\n        QRegularExpression pattern;\n        QTextCharFormat format;\n    };\n    QVector<HighlightingRule> highlightingRules;\n\n    QRegularExpression commentStartRegularExpression;\n    QRegularExpression commentEndRegularExpression;\n\n    QTextCharFormat keywordFormat;\n    QTextCharFormat regFormat;\n    QTextCharFormat classFormat;\n    QTextCharFormat singleLineCommentFormat;\n    QTextCharFormat multiLineCommentFormat;\n    QTextCharFormat quotationFormat;\n    QTextCharFormat functionFormat;\n};\n\n#endif // HIGHLIGHTER_H\n"
  },
  {
    "path": "src/common/IOModesController.cpp",
    "content": "#include \"IOModesController.h\"\n#include \"Cutter.h\"\n\n#include <QJsonArray>\n#include <QPushButton>\n#include <QObject>\n#include <QMessageBox>\n#include <QJsonObject>\n#include <qwidget.h>\n\nIOModesController::IOModesController(QWidget *parentWindow)\n    : QObject(parentWindow), parentWindow(parentWindow)\n{\n}\n\nbool IOModesController::canWrite()\n{\n    return Core()->isIOCacheEnabled() || Core()->isWriteModeEnabled();\n}\n\nIOModesController::Mode IOModesController::getIOMode()\n{\n    if (Core()->isWriteModeEnabled()) {\n        return Mode::WRITE;\n    } else if (Core()->isIOCacheEnabled()) {\n        return Mode::CACHE;\n    } else {\n        return Mode::READ_ONLY;\n    }\n}\n\nvoid IOModesController::setIOMode(IOModesController::Mode mode)\n{\n    switch (mode) {\n    case Mode::READ_ONLY:\n        if (askCommitUnsavedChanges()) {\n            Core()->setWriteMode(false);\n        }\n        break;\n    case Mode::CACHE:\n        Core()->setIOCache(true);\n        break;\n    case Mode::WRITE:\n        if (askCommitUnsavedChanges()) {\n            Core()->setWriteMode(true);\n        }\n        break;\n    }\n}\n\nbool IOModesController::prepareForWriting()\n{\n    if (canWrite()) {\n        return true;\n    }\n\n    QMessageBox msgBox(parentWindow);\n    msgBox.setIcon(QMessageBox::Icon::Critical);\n    msgBox.setWindowTitle(QObject::tr(\"Write error\"));\n    msgBox.setText(QObject::tr(\n            \"Your file is opened in read-only mode. \"\n            \"Editing is only available when the file is opened in either Write or Cache modes.\\n\\n\"\n            \"WARNING: In Write mode, any changes will be committed to the file on disk. \"\n            \"For safety, please consider using Cache mode and then commit the changes manually \"\n            \"via File -> Commit modifications to disk.\"));\n    msgBox.addButton(QObject::tr(\"Cancel\"), QMessageBox::RejectRole);\n    QAbstractButton *reopenButton =\n            msgBox.addButton(QObject::tr(\"Reopen in Write mode\"), QMessageBox::YesRole);\n    QAbstractButton *iocacheButton =\n            msgBox.addButton(QObject::tr(\"Enable Cache mode\"), QMessageBox::YesRole);\n\n    msgBox.exec();\n\n    if (msgBox.clickedButton() == reopenButton) {\n        Core()->setWriteMode(true);\n    } else if (msgBox.clickedButton() == iocacheButton) {\n        Core()->setIOCache(true);\n    } else {\n        return false;\n    }\n    return true;\n}\n\nbool IOModesController::allChangesComitted()\n{\n    RzCoreLocked core(Core());\n    for (auto c : CutterPVector<RzIOCache>(&core->io->cache)) {\n        if (!c->written) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool IOModesController::askCommitUnsavedChanges()\n{\n    // Check if there are uncommitted changes\n    if (!allChangesComitted()) {\n        QMessageBox::StandardButton ret = QMessageBox::question(\n                parentWindow, QObject::tr(\"Uncommitted changes\"),\n                QObject::tr(\"It seems that you have changes or patches that are not committed to \"\n                            \"the file.\\n\"\n                            \"Do you want to commit them now?\"),\n                (QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard\n                                               | QMessageBox::Cancel));\n        if (ret == QMessageBox::Save) {\n            Core()->commitWriteCache();\n        } else if (ret == QMessageBox::Discard) {\n            Core()->resetWriteCache();\n            emit Core()->refreshCodeViews();\n        } else if (ret == QMessageBox::Cancel) {\n            return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "src/common/IOModesController.h",
    "content": "#ifndef IOMODESCONTROLLER_H\n#define IOMODESCONTROLLER_H\n\n#include \"core/Cutter.h\"\n#include <qwidget.h>\n\nclass IOModesController : public QObject\n\n{\n    Q_OBJECT\npublic:\n    IOModesController(QWidget *parent);\n    enum class Mode { READ_ONLY, CACHE, WRITE };\n    bool prepareForWriting();\n    bool canWrite();\n    bool allChangesComitted();\n    Mode getIOMode();\n    void setIOMode(Mode mode);\n\npublic slots:\n    bool askCommitUnsavedChanges();\n\nprivate:\n    QWidget *parentWindow;\n};\n\n#endif // IOMODESCONTROLLER_H\n"
  },
  {
    "path": "src/common/InitialOptions.h",
    "content": "\n#ifndef CUTTER_INITIALOPTIONS_H\n#define CUTTER_INITIALOPTIONS_H\n\n#include \"core/Cutter.h\"\n\n/**\n * @brief The CommandDescription struct is a pair of a Rizin command and its description\n */\nstruct CommandDescription\n{\n    QString command;\n    /// untranslated description, make sure to correctly use QT_TRANSLATE_NOOP\n    const char *description;\n    QString translatedDescription() const;\n};\n\nstruct InitialOptions\n{\n    enum class Endianness { Auto, Little, Big };\n\n    QString filename;\n    QString projectFile;\n\n    bool useVA = true;\n    RVA binLoadAddr = RVA_INVALID;\n    RVA mapAddr = RVA_INVALID;\n\n    QString arch;\n    QString cpu;\n    int bits = 0;\n    QString os;\n\n    Endianness endian;\n\n    bool writeEnabled = false;\n    bool loadBinInfo = true;\n    QString forceBinPlugin;\n\n    bool demangle = true;\n\n    QString pdbFile;\n    QString script;\n\n    QList<CommandDescription> analysisCmd = {\n        { \"aaa\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Auto analysis\") }\n    };\n\n    QString shellcode;\n};\n\n#endif // CUTTER_INITIALOPTIONS_H\n"
  },
  {
    "path": "src/common/Json.h",
    "content": "\n#ifndef CUTTER_JSON_H\n#define CUTTER_JSON_H\n\n#include \"core/Cutter.h\"\n\n#include <QJsonValue>\n\nstatic inline RVA JsonValueGetRVA(const QJsonValue &value, RVA defaultValue = RVA_INVALID)\n{\n    bool ok;\n    RVA ret = value.toVariant().toULongLong(&ok);\n    if (!ok) {\n        return defaultValue;\n    }\n    return ret;\n}\n\n#endif // CUTTER_JSON_H\n"
  },
  {
    "path": "src/common/JsonModel.cpp",
    "content": "#include \"JsonModel.h\"\n\nQTreeWidgetItem *Cutter::jsonTreeWidgetItem(const QString &key, const CutterJson &json)\n{\n    QString val;\n    switch (json.type()) {\n    case RZ_JSON_STRING:\n        val = json.toString();\n        break;\n    case RZ_JSON_BOOLEAN:\n        val = json.toBool() ? \"true\" : \"false\";\n        break;\n    case RZ_JSON_DOUBLE:\n        val = QString::number(json.lowLevelValue()->num.dbl_value);\n        break;\n    case RZ_JSON_INTEGER:\n        val = QString::number(json.toUt64());\n        break;\n    case RZ_JSON_NULL:\n        val = \"null\";\n        break;\n    case RZ_JSON_OBJECT:\n    case RZ_JSON_ARRAY:\n        break;\n    }\n    auto r = new QTreeWidgetItem(QStringList({ key, val }));\n    if (json.type() == RZ_JSON_ARRAY) {\n        size_t i = 0;\n        for (const auto &child : json) {\n            r->addChild(jsonTreeWidgetItem(QString::number(i++), child));\n        }\n    } else if (json.type() == RZ_JSON_OBJECT) {\n        for (const auto &child : json) {\n            r->addChild(jsonTreeWidgetItem(child.key(), child));\n        }\n    }\n    return r;\n}\n"
  },
  {
    "path": "src/common/JsonModel.h",
    "content": "\n#ifndef JSONMODEL_H\n#define JSONMODEL_H\n\n#include <QTreeWidgetItem>\n#include \"CutterJson.h\"\n\nnamespace Cutter {\n\nQTreeWidgetItem *jsonTreeWidgetItem(const QString &key, const CutterJson &json);\n\n};\n\n#endif // JSONMODEL_H\n"
  },
  {
    "path": "src/common/LinkedListPool.h",
    "content": "#ifndef LINKED_LIST_POOL_H\n#define LINKED_LIST_POOL_H\n\n#include <vector>\n#include <cstdint>\n#include <iterator>\n\n/**\n * @brief Pool of singly linked lists.\n *\n *  Should not be used as general purpose container. Use only for algorithms that require linked\n * lists ability to split and concatenate them. All the data is owned by LinkedListPool.\n *\n * In contrast to std::list and std::forward_list doesn't allocate each node separately.\n * LinkedListPool can reserve all the memory for multiple lists during construction. Uses\n * std::vector as backing container.\n */\ntemplate<class T>\nclass LinkedListPool\n{\n    using IndexType = size_t;\n    struct Item\n    {\n        IndexType next;\n        T value;\n    };\n\npublic:\n    /**\n     * @brief List iterator.\n     *\n     * Iterators don't get invalidated by adding items to list, but the items may be relocated.\n     */\n    class ListIterator\n    {\n        IndexType index = 0;\n        LinkedListPool<T> *pool = nullptr;\n        ListIterator(IndexType index, LinkedListPool<T> *pool) : index(index), pool(pool) {}\n\n        friend class LinkedListPool<T>;\n\n    public:\n        using iterator_category = std::forward_iterator_tag;\n        using value_type = T;\n        using difference_type = size_t;\n        using pointer = T *;\n        using reference = T &;\n        ListIterator() = default;\n        reference operator*() { return pool->data[index].value; }\n        pointer operator->() { return &pool->data[index].value; }\n\n        ListIterator &operator++()\n        {\n            index = pool->data[index].next;\n            return *this;\n        }\n        ListIterator operator++(int)\n        {\n            ListIterator tmp(*this);\n            operator++();\n            return tmp;\n        }\n        bool operator!=(const ListIterator &b) const { return index != b.index || pool != b.pool; };\n        /**\n         * @brief Test if iterator points to valid value.\n         */\n        explicit operator bool() const { return index; }\n    };\n\n    /**\n     * @brief Single list within LinkedListPool.\n     *\n     * List only refers to chain of elements. Copying it doesn't copy any element. Item data is\n     * owned by LinkedListPool.\n     *\n     * Use LinkedListPool::makeList to create non-empty list.\n     */\n    class List\n    {\n        IndexType head = 0;\n        IndexType tail = 0;\n        friend class LinkedListPool;\n        List(IndexType head, IndexType tail) : head(head), tail(tail) {}\n\n    public:\n        /**\n         * @brief Create an empty list\n         */\n        List() = default;\n\n        bool isEmpty() const { return head == 0; }\n    };\n\n    /**\n     * @brief Create a linked list pool with capacity for \\a initialCapacity list items.\n     * @param initialCapacity number of elements to preallocate.\n     */\n    LinkedListPool(size_t initialCapacity) : data(1)\n    {\n        data.reserve(initialCapacity + 1); // [0] element reserved\n    }\n\n    /**\n     * @brief Create a list containing single item.\n     *\n     * Does not invalidate any iterators, but may cause item relocation when initialCapacity is\n     * exceeded.\n     * @param value value of element that will be inserted in the created list\n     * @return List containing single value \\a value .\n     */\n    List makeList(const T &value)\n    {\n        size_t position = data.size();\n        data.push_back(Item { 0, value });\n        return { position, position };\n    }\n\n    /**\n     * @brief Split list and return second half.\n     *\n     * After performing the operation, list passed as argument and return list point to the same\n     * items. Modifying them will affect both lists.\n     *\n     * @param list The list that needs to be split.\n     * @param head Iterator to the first item in new list. Needs to be within \\a list .\n     * @return Returns suffix of \\a list.\n     */\n    List splitTail(const List &list, const ListIterator &head)\n    {\n        return List { head.index, list.tail };\n    }\n\n    /**\n     * @brief Split list and return first half.\n     *\n     * @param list The list that needs to be split.\n     * @param end Iterator to the first item that should not be included in returned list. Needs to\n     * be within \\a list .\n     * @return Returns prefix of \\a list.\n     */\n    List splitHead(const List &list, const ListIterator &end)\n    {\n        if (!end) {\n            return list;\n        }\n        if (end.index == list.head) {\n            return {};\n        }\n        auto last = list.head;\n        while (data[last].next != end.index) {\n            last = data[last].next;\n        }\n        data[last].next = 0;\n        return List { list.head, last };\n    }\n\n    /**\n     * @brief Create list iterator from list.\n     * @param list\n     * @return Iterator pointing to the first item in the list.\n     */\n    ListIterator head(const List &list) { return iteratorFromIndex(list.head); }\n\n    ListIterator end(const List &list) { return std::next(iteratorFromIndex(list.tail)); }\n\n    List append(const List &head, const List &tail)\n    {\n        if (head.isEmpty()) {\n            return tail;\n        }\n        if (tail.isEmpty()) {\n            return head;\n        }\n        List result { head.head, tail.tail };\n        data[head.tail].next = tail.head;\n        return result;\n    }\n\nprivate:\n    ListIterator iteratorFromIndex(IndexType index) { return ListIterator { index, this }; }\n\n    std::vector<Item> data;\n};\n\n#endif // LINKED_LIST_POOL\n"
  },
  {
    "path": "src/common/MdHighlighter.cpp",
    "content": "#include <QtGui>\n\n#include \"common/MdHighlighter.h\"\n\nMdHighlighter::MdHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)\n{\n    HighlightingRule rule;\n\n    keywordFormat.setForeground(Qt::gray);\n    keywordFormat.setFontWeight(QFont::Bold);\n\n    QStringList keywordPatterns;\n    keywordPatterns << \"^\\\\#{1,6}[ A-Za-z]+\\\\b\"\n                    << \"\\\\*\\\\*([^\\\\\\\\]+)\\\\*\\\\*\"\n                    << \"\\\\*([^\\\\\\\\]+)\\\\*\"\n                    << \"\\\\_([^\\\\\\\\]+)\\\\_\"\n                    << \"\\\\_\\\\_([^\\\\\\\\]+)\\\\_\\\\_\";\n\n    for (const QString &pattern : keywordPatterns) {\n        rule.pattern.setPattern(pattern);\n        rule.format = keywordFormat;\n        highlightingRules.append(rule);\n    }\n\n    singleLineCommentFormat.setFontWeight(QFont::Bold);\n    singleLineCommentFormat.setForeground(Qt::darkGreen);\n    rule.pattern.setPattern(\";[^\\n]*\");\n    rule.format = singleLineCommentFormat;\n    highlightingRules.append(rule);\n}\n\nvoid MdHighlighter::highlightBlock(const QString &text)\n{\n    for (const HighlightingRule &rule : highlightingRules) {\n        QRegularExpression expression(rule.pattern);\n        int index = expression.match(text).capturedStart();\n        while (index >= 0) {\n            int length = expression.match(text).capturedLength();\n            setFormat(index, length, rule.format);\n            index = expression.match(text.mid(index + length)).capturedStart();\n        }\n    }\n    setCurrentBlockState(0);\n}\n"
  },
  {
    "path": "src/common/MdHighlighter.h",
    "content": "#ifndef MDHIGHLIGHTER_H\n#define MDHIGHLIGHTER_H\n\n#include <QSyntaxHighlighter>\n\n#include <QHash>\n#include <QTextCharFormat>\n#include <QRegularExpression>\n\nclass QTextDocument;\n\nclass MdHighlighter : public QSyntaxHighlighter\n{\n    Q_OBJECT\n\npublic:\n    explicit MdHighlighter(QTextDocument *parent = nullptr);\n\nprotected:\n    void highlightBlock(const QString &text);\n\nprivate:\n    struct HighlightingRule\n    {\n        QRegularExpression pattern;\n        QTextCharFormat format;\n    };\n    QVector<HighlightingRule> highlightingRules;\n\n    QTextCharFormat keywordFormat;\n    QTextCharFormat classFormat;\n    QTextCharFormat singleLineCommentFormat;\n    QTextCharFormat multiLineCommentFormat;\n    QTextCharFormat quotationFormat;\n    QTextCharFormat functionFormat;\n};\n\n#endif // MDHIGHLIGHTER_H\n"
  },
  {
    "path": "src/common/Metrics.h",
    "content": "\n#ifndef METRICS_H\n#define METRICS_H\n\n#include <QtGlobal>\n\nclass QRect;\nclass QRectF;\nclass QFontMetrics;\nclass QFontMetricsF;\n\ntemplate<typename T>\nstruct Metrics\n{\n};\n\ntemplate<>\nstruct Metrics<int>\n{\n    using Rect = QRect;\n    using FontMetrics = QFontMetrics;\n};\n\ntemplate<>\nstruct Metrics<qreal>\n{\n    using Rect = QRectF;\n    using FontMetrics = QFontMetricsF;\n};\n\n#endif // METRICS_H\n"
  },
  {
    "path": "src/common/ProgressIndicator.cpp",
    "content": "\n#include \"ProgressIndicator.h\"\n\n#include <QPainter>\n\nstatic const int lineWidth = 3;\nstatic const int paddingOuter = lineWidth + 2;\nstatic const int paddingInner = 8;\nstatic const int arms = 12;\nstatic const int timerInterval = 50;\n\nProgressIndicator::ProgressIndicator(QWidget *parent) : QWidget(parent)\n{\n    updateAnimationTimer();\n}\n\nProgressIndicator::~ProgressIndicator() {}\n\nvoid ProgressIndicator::setProgressIndicatorVisible(bool visible)\n{\n    bool change = progressIndicatorVisible != visible;\n    progressIndicatorVisible = visible;\n    if (change) {\n        update();\n    }\n    updateAnimationTimer();\n}\n\nvoid ProgressIndicator::setAnimating(bool animating)\n{\n    this->animating = animating;\n    updateAnimationTimer();\n}\n\nvoid ProgressIndicator::updateAnimationTimer()\n{\n    bool shouldBeAnimating = animating && progressIndicatorVisible;\n    if (shouldBeAnimating && !animationTimerId) {\n        animationTimerId = startTimer(timerInterval);\n    } else {\n        killTimer(animationTimerId);\n        animationTimerId = 0;\n    }\n}\n\nQSize ProgressIndicator::minimumSizeHint() const\n{\n    return QSize(16, 16);\n}\n\nQSize ProgressIndicator::sizeHint() const\n{\n    return QSize(32, 32);\n}\n\nvoid ProgressIndicator::timerEvent(QTimerEvent *)\n{\n    animationState = (animationState + 1) % arms;\n    update();\n}\nvoid ProgressIndicator::paintEvent(QPaintEvent *)\n{\n    if (!getProgressIndicatorVisible()) {\n        return;\n    }\n\n    QPainter painter(this);\n    painter.setRenderHint(QPainter::Antialiasing, true);\n\n    QPen pen(palette().windowText(), lineWidth, Qt::SolidLine, Qt::RoundCap);\n    painter.setPen(pen);\n\n    QPointF origin(width() * 0.5, height() * 0.5);\n    QLineF line(paddingInner, 0.0, width() * 0.5 - paddingOuter, 0.0);\n\n    qreal angle = 360.0 / arms;\n    for (int i = 0; i < arms; i++) {\n        int state = (i + (arms - animationState)) % arms;\n        painter.setOpacity((float)state / arms);\n        painter.drawLine(line * QTransform().translate(origin.x(), origin.y()).rotate(angle * i));\n    }\n}\n"
  },
  {
    "path": "src/common/ProgressIndicator.h",
    "content": "\n#ifndef PROGRESSINDICATOR_H\n#define PROGRESSINDICATOR_H\n\n#include <QWidget>\n\nclass ProgressIndicator : public QWidget\n{\npublic:\n    ProgressIndicator(QWidget *parent = nullptr);\n    virtual ~ProgressIndicator();\n\n    QSize minimumSizeHint() const override;\n    QSize sizeHint() const override;\n\n    bool getProgressIndicatorVisible() const { return progressIndicatorVisible; }\n    void setProgressIndicatorVisible(bool visible);\n\n    bool getAnimating() const { return animating; }\n    void setAnimating(bool animating);\n\nprotected:\n    void timerEvent(QTimerEvent *event) override;\n    void paintEvent(QPaintEvent *event) override;\n\nprivate:\n    bool animating = true;\n    bool progressIndicatorVisible = true;\n\n    int animationTimerId = 0;\n    int animationState = 0;\n\n    void updateAnimationTimer();\n};\n\n#endif // PROGRESSINDICATOR_H\n"
  },
  {
    "path": "src/common/PythonAPI.cpp",
    "content": "#include \"PythonAPI.h\"\n#include \"core/Cutter.h\"\n\n#include \"CutterConfig.h\"\n\n#include <QFile>\n\nPyObject *api_version(PyObject *self, PyObject *null)\n{\n    Q_UNUSED(self)\n    Q_UNUSED(null)\n    return PyUnicode_FromString(CUTTER_VERSION_FULL);\n}\n\nPyObject *api_cmd(PyObject *self, PyObject *args)\n{\n    Q_UNUSED(self);\n    char *command;\n    char *result = (char *)\"\";\n    QString cmdRes;\n    QByteArray cmdBytes;\n    if (PyArg_ParseTuple(args, \"s:command\", &command)) {\n        cmdRes = Core()->cmd(command);\n        cmdBytes = cmdRes.toLocal8Bit();\n        result = cmdBytes.data();\n    }\n    return PyUnicode_FromString(result);\n}\n\nPyObject *api_refresh(PyObject *self, PyObject *args)\n{\n    Q_UNUSED(self);\n    Q_UNUSED(args);\n    Core()->triggerRefreshAll();\n    Py_RETURN_NONE;\n}\n\nPyObject *api_message(PyObject *self, PyObject *args, PyObject *kwargs)\n{\n    Q_UNUSED(self);\n    char *message;\n    int debug = 0;\n    static const char *kwlist[] = { \"\", \"debug\", NULL };\n    if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"s|i\", const_cast<char **>(kwlist), &message,\n                                     &debug)) {\n        return NULL;\n    }\n    Core()->message(QString(message), debug);\n    Py_RETURN_NONE;\n}\n\nPyMethodDef CutterMethods[] = {\n    { \"version\", api_version, METH_NOARGS, \"Returns Cutter current version\" },\n    { \"cmd\", api_cmd, METH_VARARGS, \"Execute a command inside Cutter\" },\n    { \"refresh\", api_refresh, METH_NOARGS, \"Refresh Cutter widgets\" },\n    { \"message\", (PyCFunction)(void *)/* don't remove this double cast! */ api_message,\n      METH_VARARGS | METH_KEYWORDS, \"Print message\" },\n    { NULL, NULL, 0, NULL }\n};\n\nPyModuleDef CutterModule = {\n    PyModuleDef_HEAD_INIT, \"_cutter\", NULL, -1, CutterMethods, NULL, NULL, NULL, NULL\n};\n\nPyObject *PyInit_api()\n{\n    return PyModule_Create(&CutterModule);\n}\n"
  },
  {
    "path": "src/common/PythonAPI.h",
    "content": "#ifndef PYTHONAPI_H\n#define PYTHONAPI_H\n\n#define Py_LIMITED_API 0x03050000\n#include <Python.h>\n\nPyObject *PyInit_api();\n\n#endif // PYTHONAPI_H\n"
  },
  {
    "path": "src/common/PythonManager.cpp",
    "content": "#include <cassert>\n\n#include \"PythonAPI.h\"\n#include \"PythonManager.h\"\n#include \"Cutter.h\"\n\n#include <QDebug>\n#include <QFile>\n#include <QDebug>\n#include <QCoreApplication>\n#include <QDir>\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n#    include <shiboken.h>\n#    include <pyside.h>\n#    ifdef HAVE_PYSIDECLEANUP\n// This header is introduced in PySide 6\n#        include <pysidecleanup.h>\n#    endif\n#    include <signalmanager.h>\n#endif\n\n#include \"QtResImporter.h\"\n\nstatic PythonManager *uniqueInstance = nullptr;\n\nPythonManager *PythonManager::getInstance()\n{\n    if (!uniqueInstance) {\n        uniqueInstance = new PythonManager();\n    }\n    return uniqueInstance;\n}\n\nPythonManager::PythonManager() {}\n\nPythonManager::~PythonManager() {}\n\nvoid PythonManager::initPythonHome()\n{\n#if defined(APPIMAGE) || defined(MACOS_PYTHON_FRAMEWORK_BUNDLED)\n    if (customPythonHome.isNull()) {\n        auto pythonHomeDir = QDir(QCoreApplication::applicationDirPath());\n#    ifdef APPIMAGE\n        // Executable is in appdir/bin\n        pythonHomeDir.cdUp();\n        qInfo() << \"Setting PYTHONHOME =\" << pythonHomeDir.absolutePath() << \" for AppImage.\";\n#    else // MACOS_PYTHON_FRAMEWORK_BUNDLED\n        // @executable_path/../Frameworks/Python.framework/Versions/Current\n        pythonHomeDir.cd(\"../Frameworks/Python.framework/Versions/Current\");\n        qInfo() << \"Setting PYTHONHOME =\" << pythonHomeDir.absolutePath()\n                << \" for macOS Application Bundle.\";\n#    endif\n        customPythonHome = pythonHomeDir.absolutePath();\n    }\n#endif\n\n    if (!customPythonHome.isNull()) {\n        qInfo() << \"PYTHONHOME =\" << customPythonHome;\n        pythonHome = Py_DecodeLocale(customPythonHome.toLocal8Bit().constData(), nullptr);\n        Py_SetPythonHome(pythonHome);\n    }\n}\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\nextern \"C\" PyObject *PyInit_CutterBindings();\n#endif\n\nvoid PythonManager::initialize()\n{\n    initPythonHome();\n\n    PyImport_AppendInittab(\"_cutter\", &PyInit_api);\n    PyImport_AppendInittab(\"_qtres\", &PyInit_qtres);\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n    PyImport_AppendInittab(\"CutterBindings\", &PyInit_CutterBindings);\n#endif\n    Py_Initialize();\n    // This function is deprecated does nothing starting from Python 3.9\n#if (PY_MAJOR_VERSION <= 3) && (PY_MICRO_VERSION < 9)\n    PyEval_InitThreads();\n#endif\n    pyThreadStateCounter = 1; // we have the thread now => 1\n\n    RegQtResImporter();\n\n    saveThread();\n}\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\nstatic void pySideDestructionVisitor(SbkObject *pyObj, void *data)\n{\n    void **realData = reinterpret_cast<void **>(data);\n    auto pyQApp = reinterpret_cast<SbkObject *>(realData[0]);\n    auto pyQObjectType = reinterpret_cast<PyTypeObject *>(realData[1]);\n\n    if (pyObj == pyQApp || !PyObject_TypeCheck(pyObj, pyQObjectType)) {\n        return;\n    }\n    if (!Shiboken::Object::hasOwnership(pyObj) || !Shiboken::Object::isValid(pyObj, false)) {\n        return;\n    }\n\n    const char *reprStr = \"\";\n    PyObject *repr = PyObject_Repr(reinterpret_cast<PyObject *>(pyObj));\n    PyObject *reprBytes;\n    if (repr) {\n        reprBytes = PyUnicode_AsUTF8String(repr);\n        reprStr = PyBytes_AsString(reprBytes);\n    }\n    qWarning() << \"Warning: QObject from Python remaining (leaked from plugin?):\" << reprStr;\n    if (repr) {\n        Py_DecRef(reprBytes);\n        Py_DecRef(repr);\n    }\n\n    Shiboken::Object::setValidCpp(pyObj, false);\n    Py_BEGIN_ALLOW_THREADS Shiboken::callCppDestructor<QObject>(\n            Shiboken::Object::cppPointer(pyObj, pyQObjectType));\n    Py_END_ALLOW_THREADS\n};\n#endif\n\nvoid PythonManager::shutdown()\n{\n    emit willShutDown();\n\n    restoreThread();\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n    // This is necessary to prevent a segfault when the CutterCore instance is deleted after the\n    // Shiboken::BindingManager\n    Core()->setProperty(\"_PySideInvalidatePtr\", QVariant());\n\n    // see PySide::destroyQCoreApplication()\n#    if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)\n    PySide::SignalManager::instance().clear();\n#    endif\n    Shiboken::BindingManager &bm = Shiboken::BindingManager::instance();\n    SbkObject *pyQApp = bm.retrieveWrapper(QCoreApplication::instance());\n    PyTypeObject *pyQObjectType = Shiboken::Conversions::getPythonTypeObject(\"QObject*\");\n    void *data[2] = { pyQApp, pyQObjectType };\n    bm.visitAllPyObjects(&pySideDestructionVisitor, &data);\n\n    PySide::runCleanupFunctions();\n#endif\n\n    if (pythonHome) {\n        PyMem_Free(pythonHome);\n    }\n\n    Py_Finalize();\n}\n\nvoid PythonManager::addPythonPath(char *path)\n{\n    restoreThread();\n\n    PyObject *sysModule = PyImport_ImportModule(\"sys\");\n    if (!sysModule) {\n        return;\n    }\n    PyObject *pythonPath = PyObject_GetAttrString(sysModule, \"path\");\n    if (!pythonPath) {\n        return;\n    }\n    PyObject *append = PyObject_GetAttrString(pythonPath, \"append\");\n    if (!append) {\n        return;\n    }\n    PyObject_CallFunction(append, \"(s)\", path);\n\n    saveThread();\n}\n\nvoid PythonManager::restoreThread()\n{\n    pyThreadStateCounter++;\n    if (pyThreadStateCounter == 1 && pyThreadState) {\n        PyEval_RestoreThread(pyThreadState);\n    }\n}\n\nvoid PythonManager::saveThread()\n{\n    pyThreadStateCounter--;\n    assert(pyThreadStateCounter >= 0);\n    if (pyThreadStateCounter == 0) {\n        pyThreadState = PyEval_SaveThread();\n    }\n}\n"
  },
  {
    "path": "src/common/PythonManager.h",
    "content": "#ifndef PYTHONMANAGER_H\n#define PYTHONMANAGER_H\n\n#ifdef CUTTER_ENABLE_PYTHON\n\n#    include <QObject>\n\ntypedef struct _ts PyThreadState;\ntypedef struct _object PyObject;\n\nclass PythonManager : public QObject\n{\n    Q_OBJECT\n\npublic:\n    static PythonManager *getInstance();\n\n    PythonManager();\n    ~PythonManager();\n\n    void setPythonHome(const QString &pythonHome) { customPythonHome = pythonHome; }\n\n    void initPythonHome();\n    void initialize();\n    void shutdown();\n\n    void addPythonPath(char *path);\n\n    void restoreThread();\n    void saveThread();\n\n    /**\n     * @brief RAII Helper class to call restoreThread() and saveThread() automatically\n     *\n     * As long as an object of this class is in scope, the Python thread will remain restored.\n     */\n    class ThreadHolder\n    {\n    public:\n        ThreadHolder() { getInstance()->restoreThread(); }\n        ~ThreadHolder() { getInstance()->saveThread(); }\n    };\n\nsignals:\n    void willShutDown();\n\nprivate:\n    QString customPythonHome;\n    wchar_t *pythonHome = nullptr;\n    PyThreadState *pyThreadState = nullptr;\n    int pyThreadStateCounter = 0;\n};\n\n#    define Python() (PythonManager::getInstance())\n\n#endif // CUTTER_ENABLE_PYTHON\n\n#endif // PYTHONMANAGER_H\n"
  },
  {
    "path": "src/common/QtResImporter.cpp",
    "content": "#include \"PythonAPI.h\"\n#include \"QtResImporter.h\"\n\n#include <QFile>\n#include <QDebug>\n\nbool QtResExists(const char *name, QFile &file)\n{\n    QString fname = QString::asprintf(\":/python/%s.py\", name);\n    file.setFileName(fname);\n    return file.exists();\n}\n\nPyObject *QtResGetCode(const char *name)\n{\n    QFile moduleFile;\n\n    if (!QtResExists(name, moduleFile)) {\n        return nullptr;\n    }\n\n    moduleFile.open(QIODevice::ReadOnly);\n    QByteArray data = moduleFile.readAll();\n    moduleFile.close();\n\n    PyObject *codeObject = Py_CompileString(\n            data.constData(), moduleFile.fileName().toLocal8Bit().constData(), Py_file_input);\n    if (!codeObject) {\n        qWarning() << \"Couldn't compile \" << moduleFile.fileName();\n    }\n    return codeObject;\n}\n\nPyObject *QtResImport(const char *name)\n{\n    PyObject *codeObject = QtResGetCode(name);\n    if (!codeObject) {\n        return nullptr;\n    }\n    PyObject *module = PyImport_ExecCodeModule(name, codeObject);\n    if (!module) {\n        PyErr_Print();\n    }\n    Py_DECREF(codeObject);\n    return module;\n}\n\nPyObject *qtres_exists(PyObject *self, PyObject *args)\n{\n    Q_UNUSED(self)\n    char *name;\n    QFile resFile;\n    if (!PyArg_ParseTuple(args, \"s\", &name))\n        return NULL;\n    return PyBool_FromLong(QtResExists(name, resFile));\n}\n\nPyObject *qtres_get_code(PyObject *self, PyObject *args)\n{\n    Q_UNUSED(self)\n    char *name;\n    if (!PyArg_ParseTuple(args, \"s\", &name))\n        return NULL;\n    PyObject *ret = QtResGetCode(name);\n    if (ret)\n        return ret;\n    Py_RETURN_NONE;\n}\n\nPyMethodDef QtResMethods[] = { { \"exists\", qtres_exists, METH_VARARGS, NULL },\n                               { \"get_code\", qtres_get_code, METH_VARARGS, NULL },\n                               { NULL, NULL, 0, NULL } };\n\nPyModuleDef QtResModule = {\n    PyModuleDef_HEAD_INIT, \"_qtres\", NULL, -1, QtResMethods, NULL, NULL, NULL, NULL\n};\n\nPyObject *PyInit_qtres()\n{\n    return PyModule_Create(&QtResModule);\n}\n"
  },
  {
    "path": "src/common/QtResImporter.h",
    "content": "#ifndef QTRESIMPORTER_H\n#define QTRESIMPORTER_H\n\nPyObject *PyInit_qtres();\n\nPyObject *QtResImport(const char *name);\n\n#define RegQtResImporter() Py_DecRef(QtResImport(\"reg_qtres_importer\"))\n\n#endif // QTRESIMPORTER_H\n"
  },
  {
    "path": "src/common/RefreshDeferrer.cpp",
    "content": "\n#include \"RefreshDeferrer.h\"\n#include \"widgets/CutterDockWidget.h\"\n\nRefreshDeferrer::RefreshDeferrer(RefreshDeferrerAccumulator *acc, QObject *parent)\n    : QObject(parent), acc(acc)\n{\n}\n\nRefreshDeferrer::~RefreshDeferrer()\n{\n    delete acc;\n}\n\nbool RefreshDeferrer::attemptRefresh(RefreshDeferrerParams params)\n{\n    if (dockWidget->isVisibleToUser()) {\n        if (acc) {\n            acc->ignoreParams(params);\n        }\n        return true;\n    } else {\n        dirty = true;\n        if (acc) {\n            acc->accumulate(params);\n        }\n        return false;\n    }\n}\n\nvoid RefreshDeferrer::registerFor(CutterDockWidget *dockWidget)\n{\n    this->dockWidget = dockWidget;\n    connect(dockWidget, &CutterDockWidget::becameVisibleToUser, this, [this]() {\n        if (dirty) {\n            emit refreshNow(acc ? acc->result() : nullptr);\n            if (acc) {\n                acc->clear();\n            }\n            dirty = false;\n        }\n    });\n}\n"
  },
  {
    "path": "src/common/RefreshDeferrer.h",
    "content": "\n#ifndef REFRESHDEFERRER_H\n#define REFRESHDEFERRER_H\n\n#include <QObject>\n\nclass CutterDockWidget;\nclass RefreshDeferrer;\n\nusing RefreshDeferrerParams = void *;\nusing RefreshDeferrerParamsResult = void *;\n\n/**\n * @brief Abstract class for accumulating params in RefreshDeferrer\n */\nclass RefreshDeferrerAccumulator\n{\n    friend class RefreshDeferrer;\n\npublic:\n    virtual ~RefreshDeferrerAccumulator() = default;\n\nprotected:\n    /**\n     * @brief Add a new param to the accumulator\n     */\n    virtual void accumulate(RefreshDeferrerParams params) = 0;\n\n    /**\n     * @brief Ignore the incoming params. Useful for freeing if necessary.\n     */\n    virtual void ignoreParams(RefreshDeferrerParams params) = 0;\n\n    /**\n     * @brief Clear the current accumulator\n     */\n    virtual void clear() = 0;\n\n    /**\n     * @brief Return the final result of the accumulation\n     */\n    virtual RefreshDeferrerParamsResult result() = 0;\n};\n\n/**\n * @brief Accumulator which simply replaces the current value by an incoming new one\n * @tparam T The type of the param to store\n *\n * This accumulator takes the ownership of all params passed to it and deletes them automatically if\n * not needed anymore!\n */\ntemplate<class T>\nclass ReplacingRefreshDeferrerAccumulator : public RefreshDeferrerAccumulator\n{\nprivate:\n    T *value = nullptr;\n    bool replaceIfNull;\n\npublic:\n    /**\n     * \\param Determines whether, if nullptr is passed, the current value should be replaced or\n     * kept.\n     */\n    explicit ReplacingRefreshDeferrerAccumulator(bool replaceIfNull = true)\n        : replaceIfNull(replaceIfNull)\n    {\n    }\n\n    ~ReplacingRefreshDeferrerAccumulator() override { delete value; }\n\nprotected:\n    void accumulate(RefreshDeferrerParams params) override\n    {\n        if (!replaceIfNull && !params) {\n            return;\n        }\n        delete value;\n        value = static_cast<T *>(params);\n    }\n\n    void ignoreParams(RefreshDeferrerParams params) override { delete static_cast<T *>(params); }\n\n    void clear() override\n    {\n        delete value;\n        value = nullptr;\n    }\n\n    virtual RefreshDeferrerParamsResult result() override { return value; }\n};\n\n/**\n * @brief Helper class for deferred refreshing in Widgets\n *\n * This class can handle the logic necessary to defer the refreshing of widgets when they are not\n * visible. It contains an optional RefreshDeferrerAccumulator, which can be used to accumulate\n * incoming events while refreshing is deferred.\n *\n * Example (don't write it like this in practice, use the convenience methods in CutterDockWidget):\n * ```\n * // in the constructor of a widget\n * this->refreshDeferrer = new RefreshDeferrer(new ReplacingRefreshDeferrerAccumulator(false),\n * this); this->refreshDeferrer->registerFor(this); connect(this->refreshDeferrer,\n * &RefreshDeferrer::refreshNow, this, [this](MyParam *param) {\n *      // We attempted a refresh some time before, but it got deferred.\n *      // Now the RefreshDeferrer tells us to do the refresh and gives us the accumulated param.\n *      this->doRefresh(*param);\n * }\n *\n * // ...\n *\n * void MyWidget::doRefresh(MyParam param)\n * {\n *      if (!this->refreshDeferrer->attemptRefresh(new MyParam(param))) {\n *          // We shouldn't refresh right now.\n *          // The RefreshDeferrer takes over the param we passed it in attemptRefresh()\n *          // and gives it to the ReplacingRefreshDeferrerAccumulator.\n *          return;\n *      }\n *      // do the actual refresh depending on param\n * }\n * ```\n *\n */\nclass RefreshDeferrer : public QObject\n{\n    Q_OBJECT\n\nprivate:\n    CutterDockWidget *dockWidget = nullptr;\n    RefreshDeferrerAccumulator *acc;\n    bool dirty = false;\n\npublic:\n    /**\n     * @param acc The accumulator (can be nullptr). The RefreshDeferrer takes the ownership!\n     */\n    explicit RefreshDeferrer(RefreshDeferrerAccumulator *acc, QObject *parent = nullptr);\n    ~RefreshDeferrer() override;\n\n    bool attemptRefresh(RefreshDeferrerParams params);\n    void registerFor(CutterDockWidget *dockWidget);\n\nsignals:\n    void refreshNow(const RefreshDeferrerParamsResult paramsResult);\n};\n\n#endif // REFRESHDEFERRER_H\n"
  },
  {
    "path": "src/common/ResourcePaths.cpp",
    "content": "#include \"common/ResourcePaths.h\"\n\n#include <QLibraryInfo>\n#include <QDir>\n#include <QFileInfo>\n#include <QApplication>\n#include <QDebug>\n#include <QStandardPaths>\n\n#ifdef APPIMAGE\nstatic QDir appimageRoot()\n{\n    auto dir = QDir(QCoreApplication::applicationDirPath()); // appimage/usr/bin\n    dir.cdUp(); // /usr\n    dir.cdUp(); // /\n    return dir;\n}\n#endif\n\nstatic QString substitutePath(QString path)\n{\n#ifdef APPIMAGE\n    if (path.startsWith(\"/usr\")) {\n        return appimageRoot().absoluteFilePath(\".\" + path);\n    }\n#endif\n    return path;\n}\n\n/**\n * @brief Substitute or filter paths returned by standardLocations based on Cutter package kind.\n * @param paths list of paths to process\n * @return\n */\nstatic QStringList substitutePaths(const QStringList &paths)\n{\n    QStringList result;\n    result.reserve(paths.size());\n    for (auto &path : paths) {\n        // consider ignoring some of system folders for portable packages here or standardLocations\n        // if it depends on path type\n        result.push_back(substitutePath(path));\n    }\n    return result;\n}\n\nQStringList Cutter::locateAll(QStandardPaths::StandardLocation type, const QString &fileName,\n                              QStandardPaths::LocateOptions options)\n{\n    // This function is reimplemented here instead of forwarded to Qt becauase existence check needs\n    // to be done after substitutions\n    QStringList result;\n    for (auto path : standardLocations(type)) {\n        QString filePath = path + QLatin1Char('/') + fileName;\n        bool exists = false;\n        if (options & QStandardPaths::LocateDirectory) {\n            exists = QDir(filePath).exists();\n        } else {\n            exists = QFileInfo(filePath).isFile();\n        }\n        if (exists) {\n            result.append(filePath);\n        }\n    }\n    return result;\n}\n\nQStringList Cutter::standardLocations(QStandardPaths::StandardLocation type)\n{\n    return substitutePaths(QStandardPaths::standardLocations(type));\n}\n\nQString Cutter::writableLocation(QStandardPaths::StandardLocation type)\n{\n    return substitutePath(QStandardPaths::writableLocation(type));\n}\n\nQStringList Cutter::getTranslationsDirectories()\n{\n    auto result = locateAll(QStandardPaths::AppDataLocation, \"translations\",\n                            QStandardPaths::LocateDirectory);\n    result << QLibraryInfo::location(QLibraryInfo::TranslationsPath);\n    return result;\n}\n"
  },
  {
    "path": "src/common/ResourcePaths.h",
    "content": "#ifndef RESOURCE_PATHS_H\n#define RESOURCE_PATHS_H\n\n#include <QStandardPaths>\n\n/**\n * \\file ResourcePaths.h\n * Set of functions for obtaining various paths. Some of the functions are wrappers around\n * QStandardPaths functions having the same name but with modifications specific to way\n * Cutter is packaged.\n */\n\nnamespace Cutter {\nQStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName,\n                      QStandardPaths::LocateOptions options = QStandardPaths::LocateFile);\nQStringList standardLocations(QStandardPaths::StandardLocation type);\nQString writableLocation(QStandardPaths::StandardLocation type);\n\n/**\n * @brief Get list of available translation directories (depends on configuration and OS)\n * @return list of directories\n */\nQStringList getTranslationsDirectories();\n}\n\n#endif\n"
  },
  {
    "path": "src/common/RichTextPainter.cpp",
    "content": "/* x64dbg RichTextPainter */\n#include \"RichTextPainter.h\"\n#include \"CachedFontMetrics.h\"\n#include \"common/Configuration.h\"\n#include <QPainter>\n#include <QTextBlock>\n#include <QTextFragment>\n\n// TODO: fix performance (possibly use QTextLayout?)\n\ntemplate<typename T>\nvoid RichTextPainter::paintRichText(QPainter *painter, T x, T y, T w, T h, T xinc,\n                                    const List &richText, CachedFontMetrics<T> *fontMetrics)\n{\n    QPen pen;\n    QPen highlightPen;\n    QBrush brush(Qt::cyan);\n    for (const CustomRichText_t &curRichText : richText) {\n        T textWidth = fontMetrics->width(curRichText.text);\n        T backgroundWidth = textWidth;\n        if (backgroundWidth + xinc > w)\n            backgroundWidth = w - xinc;\n        if (backgroundWidth <= 0) // stop drawing when going outside the specified width\n            break;\n        switch (curRichText.flags) {\n        case FlagNone: // defaults\n            pen.setColor(ConfigColor(\"btext\").name());\n            painter->setPen(pen);\n            break;\n        case FlagColor: // color only\n            pen.setColor(curRichText.textColor);\n            painter->setPen(pen);\n            break;\n        case FlagBackground: // background only\n            if (backgroundWidth > 0 && curRichText.textBackground.alpha()) {\n                brush.setColor(curRichText.textBackground);\n                painter->fillRect(QRectF(x + xinc, y, backgroundWidth, h), brush);\n            }\n            break;\n        case FlagAll: // color+background\n            if (backgroundWidth > 0 && curRichText.textBackground.alpha()) {\n                brush.setColor(curRichText.textBackground);\n                painter->fillRect(QRectF(x + xinc, y, backgroundWidth, h), brush);\n            }\n            pen.setColor(curRichText.textColor);\n            painter->setPen(pen);\n            break;\n        }\n        int flags = 0;\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n        flags = Qt::TextBypassShaping;\n#endif\n\n        painter->drawText(typename Metrics<T>::Rect(x + xinc, y, w - xinc, h), flags,\n                          curRichText.text);\n        if (curRichText.highlight && curRichText.highlightColor.alpha()) {\n            highlightPen.setColor(curRichText.highlightColor);\n            highlightPen.setWidth(curRichText.highlightWidth);\n            painter->setPen(highlightPen);\n            T highlightOffsetX = curRichText.highlightConnectPrev ? -1 : 1;\n            painter->drawLine(x + xinc + highlightOffsetX, y + h - 1,\n                              x + xinc + backgroundWidth - 1, y + h - 1);\n        }\n        xinc += textWidth;\n    }\n}\n\ntemplate void RichTextPainter::paintRichText<qreal>(QPainter *painter, qreal x, qreal y, qreal w,\n                                                    qreal h, qreal xinc, const List &richText,\n                                                    CachedFontMetrics<qreal> *fontMetrics);\n\n/**\n * @brief RichTextPainter::htmlRichText Convert rich text in x64dbg to HTML, for use by other\n * applications\n * @param richText The rich text to be converted to HTML format\n * @param textHtml The HTML source. Any previous content will be preserved and new content will be\n * appended at the end.\n * @param textPlain The plain text. Any previous content will be preserved and new content will be\n * appended at the end.\n */\nvoid RichTextPainter::htmlRichText(const List &richText, QString &textHtml, QString &textPlain)\n{\n    for (const CustomRichText_t &curRichText : richText) {\n        if (curRichText.text == \" \") { // blank\n            textHtml += \" \";\n            textPlain += \" \";\n            continue;\n        }\n        switch (curRichText.flags) {\n        case FlagNone: // defaults\n            textHtml += \"<span>\";\n            break;\n        case FlagColor: // color only\n            textHtml += QString(\"<span style=\\\"color:%1\\\">\").arg(curRichText.textColor.name());\n            break;\n        case FlagBackground: // background only\n            if (curRichText.textBackground\n                != Qt::transparent) // QColor::name() returns \"#000000\" for transparent color.\n                                    // That's not desired. Leave it blank.\n                textHtml += QString(\"<span style=\\\"background-color:%1\\\">\")\n                                    .arg(curRichText.textBackground.name());\n            else\n                textHtml += QString(\"<span>\");\n            break;\n        case FlagAll: // color+background\n            if (curRichText.textBackground\n                != Qt::transparent) // QColor::name() returns \"#000000\" for transparent color.\n                                    // That's not desired. Leave it blank.\n                textHtml += QString(\"<span style=\\\"color:%1; background-color:%2\\\">\")\n                                    .arg(curRichText.textColor.name(),\n                                         curRichText.textBackground.name());\n            else\n                textHtml += QString(\"<span style=\\\"color:%1\\\">\").arg(curRichText.textColor.name());\n            break;\n        }\n        if (curRichText.highlight) // Underline highlighted token\n            textHtml += \"<u>\";\n        textHtml += curRichText.text.toHtmlEscaped();\n        if (curRichText.highlight)\n            textHtml += \"</u>\";\n        textHtml += \"</span>\"; // Close the tag\n        textPlain += curRichText.text;\n    }\n}\n\nRichTextPainter::List RichTextPainter::fromTextDocument(const QTextDocument &doc)\n{\n    List r;\n\n    for (QTextBlock block = doc.begin(); block != doc.end(); block = block.next()) {\n        for (QTextBlock::iterator it = block.begin(); it != block.end(); ++it) {\n            QTextFragment fragment = it.fragment();\n            QTextCharFormat format = fragment.charFormat();\n\n            CustomRichText_t text;\n            text.text = fragment.text();\n            text.textColor = format.foreground().color();\n            text.textBackground = format.background().color();\n\n            bool hasForeground = format.hasProperty(QTextFormat::ForegroundBrush);\n            bool hasBackground = format.hasProperty(QTextFormat::BackgroundBrush);\n\n            if (hasForeground && !hasBackground) {\n                text.flags = FlagColor;\n            } else if (!hasForeground && hasBackground) {\n                text.flags = FlagBackground;\n            } else if (hasForeground && hasBackground) {\n                text.flags = FlagAll;\n            } else {\n                text.flags = FlagNone;\n            }\n\n            r.push_back(text);\n        }\n    }\n\n    return r;\n}\n\nRichTextPainter::List RichTextPainter::cropped(const RichTextPainter::List &richText, int maxCols,\n                                               const QString &indicator, bool *croppedOut)\n{\n    List r;\n    r.reserve(richText.size());\n\n    int cols = 0;\n    bool cropped = false;\n    for (const auto &text : richText) {\n        int textLength = text.text.size();\n        if (cols + textLength <= maxCols) {\n            r.push_back(text);\n            cols += textLength;\n        } else if (cols == maxCols) {\n            break;\n        } else {\n            CustomRichText_t croppedText = text;\n            croppedText.text.truncate(maxCols - cols);\n            r.push_back(croppedText);\n            cropped = true;\n            break;\n        }\n    }\n\n    if (cropped && !indicator.isEmpty()) {\n        int indicatorCropLength = indicator.length();\n        if (indicatorCropLength > maxCols) {\n            indicatorCropLength = maxCols;\n        }\n\n        while (!r.empty()) {\n            auto &text = r.back();\n\n            if (text.text.length() >= indicatorCropLength) {\n                text.text.replace(text.text.length() - indicatorCropLength, indicatorCropLength,\n                                  indicator);\n                break;\n            }\n\n            indicatorCropLength -= text.text.length();\n            r.pop_back();\n        }\n    }\n\n    if (croppedOut) {\n        *croppedOut = cropped;\n    }\n    return r;\n}\n"
  },
  {
    "path": "src/common/RichTextPainter.h",
    "content": "/* x64dbg RichTextPainter */\n#ifndef RICHTEXTPAINTER_H\n#define RICHTEXTPAINTER_H\n\n#include \"common/Metrics.h\"\n\n#include <QString>\n#include <QTextDocument>\n#include <QColor>\n#include <vector>\n\nclass QFontMetricsF;\ntemplate<typename T>\nclass CachedFontMetrics;\nclass QPainter;\n\nclass RichTextPainter\n{\npublic:\n    // structures\n    enum CustomRichTextFlags { FlagNone, FlagColor, FlagBackground, FlagAll };\n\n    struct CustomRichText_t\n    {\n        QString text;\n        QColor textColor;\n        QColor textBackground;\n        CustomRichTextFlags flags;\n        bool highlight = false;\n        QColor highlightColor;\n        int highlightWidth = 2;\n        bool highlightConnectPrev = false;\n    };\n\n    typedef std::vector<CustomRichText_t> List;\n\n    // functions\n    template<typename T = qreal>\n    static void paintRichText(QPainter *painter, T x, T y, T w, T h, T xinc, const List &richText,\n                              CachedFontMetrics<T> *fontMetrics);\n    static void htmlRichText(const List &richText, QString &textHtml, QString &textPlain);\n\n    static List fromTextDocument(const QTextDocument &doc);\n\n    static List cropped(const List &richText, int maxCols, const QString &indicator = nullptr,\n                        bool *croppedOut = nullptr);\n};\n\n#endif // RICHTEXTPAINTER_H\n"
  },
  {
    "path": "src/common/RizinTask.cpp",
    "content": "\n#include \"RizinTask.h\"\n#include <rz_core.h>\n\nRizinTask::~RizinTask()\n{\n    if (task) {\n        rz_core_task_decref(task);\n    }\n}\n\nvoid RizinTask::startTask()\n{\n    rz_core_task_enqueue(&Core()->core_->tasks, task);\n}\n\nvoid RizinTask::breakTask()\n{\n    rz_core_task_break(&Core()->core_->tasks, task->id);\n}\n\nvoid RizinTask::joinTask()\n{\n    rz_core_task_join(&Core()->core_->tasks, nullptr, task->id);\n}\n\nvoid RizinTask::taskFinished()\n{\n    emit finished();\n}\n\n// RizinCmdTask\n\nRizinCmdTask::RizinCmdTask(const QString &cmd, bool transient)\n{\n    auto core = Core()->lock();\n    task = rz_core_cmd_task_new(\n            core, cmd.toLocal8Bit().constData(),\n            static_cast<RzCoreCmdTaskFinished>(&RizinCmdTask::taskFinishedCallback), this);\n    task->transient = transient;\n    rz_core_task_incref(task);\n}\n\nvoid RizinCmdTask::taskFinishedCallback(const char *, void *user)\n{\n    reinterpret_cast<RizinCmdTask *>(user)->taskFinished();\n}\n\nQString RizinCmdTask::getResult()\n{\n    const char *res = rz_core_cmd_task_get_result(task);\n    if (!res) {\n        return nullptr;\n    }\n    return QString::fromUtf8(res);\n}\n\nCutterJson RizinCmdTask::getResultJson()\n{\n    const char *res = rz_core_cmd_task_get_result(task);\n    if (!res) {\n        return CutterJson();\n    }\n    char *copy = static_cast<char *>(rz_mem_alloc(strlen(res) + 1));\n    strcpy(copy, res);\n    return Core()->parseJson(\"task\", copy, nullptr);\n}\n\nconst char *RizinCmdTask::getResultRaw()\n{\n    return rz_core_cmd_task_get_result(task);\n}\n\n// RizinFunctionTask\n\nRizinFunctionTask::RizinFunctionTask(std::function<void *(RzCore *)> fcn, bool transient)\n    : fcn(fcn), res(nullptr)\n{\n    auto core = Core()->lock();\n    task = rz_core_function_task_new(\n            core, static_cast<RzCoreTaskFunction>(&RizinFunctionTask::runner), this);\n    task->transient = transient;\n    rz_core_task_incref(task);\n}\n\nvoid *RizinFunctionTask::runner(RzCore *core, void *user)\n{\n    RizinFunctionTask *task = reinterpret_cast<RizinFunctionTask *>(user);\n    task->res = task->fcn(core);\n    task->taskFinished();\n    return nullptr;\n}\n"
  },
  {
    "path": "src/common/RizinTask.h",
    "content": "\n#ifndef RZTASK_H\n#define RZTASK_H\n\n#include \"core/Cutter.h\"\n\nclass CUTTER_EXPORT RizinTask : public QObject\n{\n    Q_OBJECT\n\nprotected:\n    RzCoreTask *task;\n\n    RizinTask() {}\n    void taskFinished();\n\npublic:\n    using Ptr = QSharedPointer<RizinTask>;\n\n    virtual ~RizinTask();\n\n    void startTask();\n    void breakTask();\n    void joinTask();\n\nsignals:\n    void finished();\n};\n\nclass CUTTER_EXPORT RizinCmdTask : public RizinTask\n{\n    Q_OBJECT\n\nprivate:\n    static void taskFinishedCallback(const char *, void *user);\n\npublic:\n    explicit RizinCmdTask(const QString &cmd, bool transient = true);\n\n    QString getResult();\n    CutterJson getResultJson();\n    const char *getResultRaw();\n};\n\nclass CUTTER_EXPORT RizinFunctionTask : public RizinTask\n{\n    Q_OBJECT\n\nprivate:\n    std::function<void *(RzCore *)> fcn;\n    void *res;\n\n    static void *runner(RzCore *core, void *user);\n\npublic:\n    explicit RizinFunctionTask(std::function<void *(RzCore *)> fcn, bool transient = true);\n\n    void *getResult() { return res; }\n};\n\n#endif // RZTASK_H\n"
  },
  {
    "path": "src/common/RunScriptTask.cpp",
    "content": "#include \"core/Cutter.h\"\n#include \"common/RunScriptTask.h\"\n#include \"core/MainWindow.h\"\n\nRunScriptTask::RunScriptTask() : AsyncTask() {}\n\nRunScriptTask::~RunScriptTask() {}\n\nvoid RunScriptTask::interrupt()\n{\n    AsyncTask::interrupt();\n    rz_cons_singleton()->context->breaked = true;\n}\n\nvoid RunScriptTask::runTask()\n{\n    if (!this->fileName.isNull()) {\n        log(tr(\"Executing script...\"));\n        Core()->functionTask([&](RzCore *core) {\n            rz_core_run_script(core, this->fileName.toUtf8().constData());\n            return nullptr;\n        });\n        if (isInterrupted()) {\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "src/common/RunScriptTask.h",
    "content": "#ifndef RUNSCRIPTTHREAD_H\n#define RUNSCRIPTTHREAD_H\n\n#include \"common/AsyncTask.h\"\n#include \"core/Cutter.h\"\n\nclass RunScriptTask : public AsyncTask\n{\n    Q_OBJECT\n\npublic:\n    explicit RunScriptTask();\n    ~RunScriptTask();\n\n    QString getTitle() override { return tr(\"Run Script\"); }\n\n    void setFileName(const QString &fileName) { this->fileName = fileName; }\n\n    void interrupt() override;\n\nprotected:\n    void runTask() override;\n\nprivate:\n    QString fileName;\n};\n\n#endif // RUNSCRIPTTHREAD_H\n"
  },
  {
    "path": "src/common/SelectionHighlight.cpp",
    "content": "\n#include \"SelectionHighlight.h\"\n#include \"Configuration.h\"\n#include \"Colors.h\"\n\n#include <QList>\n#include <QTextEdit>\n#include <QColor>\n#include <QTextCursor>\n#include <QPlainTextEdit>\n#include <QRegularExpression>\n\nQList<QTextEdit::ExtraSelection> createSameWordsSelections(QPlainTextEdit *textEdit,\n                                                           const QString &word)\n{\n    QList<QTextEdit::ExtraSelection> selections;\n    QTextEdit::ExtraSelection highlightSelection;\n    QTextDocument *document = textEdit->document();\n    QColor highlightWordBgColor = ConfigColor(\"wordHighlightBg\");\n    QColor highlightWordFgColor = ConfigColor(\"wordHighlightFg\");\n\n    auto applyHighlight = [&] {\n        QColor originalColor = highlightSelection.cursor.charFormat().foreground().color();\n        highlightSelection.format.setForeground(\n                Colors::overlayColor(originalColor, highlightWordFgColor));\n        highlightSelection.format.setBackground(highlightWordBgColor);\n    };\n\n    if (word.isEmpty()) {\n        return QList<QTextEdit::ExtraSelection>();\n    }\n\n    highlightSelection.cursor = textEdit->textCursor();\n\n    if (word == \"{\" || word == \"}\") {\n        int val;\n        if (word == \"{\") {\n            val = 0;\n        } else {\n            val = 1;\n        }\n        selections.append(highlightSelection);\n\n        while (!highlightSelection.cursor.isNull() && !highlightSelection.cursor.atEnd()) {\n            if (word == \"{\") {\n                highlightSelection.cursor =\n                        document->find(QRegularExpression(\"{|}\"), highlightSelection.cursor);\n            } else {\n                highlightSelection.cursor =\n                        document->find(QRegularExpression(\"{|}\"), highlightSelection.cursor,\n                                       QTextDocument::FindBackward);\n            }\n\n            if (!highlightSelection.cursor.isNull()) {\n                if (highlightSelection.cursor.selectedText() == word) {\n                    val++;\n                } else {\n                    val--;\n                }\n                if (val == 0) {\n                    applyHighlight();\n                    selections.append(highlightSelection);\n                    break;\n                }\n            }\n        }\n        return selections;\n    }\n\n    highlightSelection.cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);\n\n    while (!highlightSelection.cursor.isNull() && !highlightSelection.cursor.atEnd()) {\n        highlightSelection.cursor =\n                document->find(word, highlightSelection.cursor, QTextDocument::FindWholeWords);\n\n        if (!highlightSelection.cursor.isNull()) {\n            applyHighlight();\n            selections.append(highlightSelection);\n        }\n    }\n    return selections;\n}\n\nQTextEdit::ExtraSelection createLineHighlight(const QTextCursor &cursor, QColor highlightColor)\n{\n    QTextEdit::ExtraSelection highlightSelection;\n    highlightSelection.cursor = cursor;\n    highlightSelection.format.setBackground(highlightColor);\n    highlightSelection.format.setProperty(QTextFormat::FullWidthSelection, true);\n    highlightSelection.cursor.clearSelection();\n    return highlightSelection;\n}\n\nQTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor)\n{\n    QColor highlightColor = ConfigColor(\"lineHighlight\");\n    return createLineHighlight(cursor, highlightColor);\n}\n\nQTextEdit::ExtraSelection createLineHighlightPC(const QTextCursor &cursor)\n{\n    QColor highlightColor = ConfigColor(\"highlightPC\");\n    return createLineHighlight(cursor, highlightColor);\n}\n\nQTextEdit::ExtraSelection createLineHighlightBP(const QTextCursor &cursor)\n{\n    QColor highlightColor = ConfigColor(\"gui.breakpoint_background\");\n    return createLineHighlight(cursor, highlightColor);\n}\n"
  },
  {
    "path": "src/common/SelectionHighlight.h",
    "content": "#ifndef CUTTER_SELECTIONHIGHLIGHT_H\n#define CUTTER_SELECTIONHIGHLIGHT_H\n\n#include <QTextEdit>\n\nclass QPlainTextEdit;\nclass QString;\n\n/**\n * @brief createSameWordsSelections se\n * @param textEdit\n * @param word\n * @return\n */\nQList<QTextEdit::ExtraSelection> createSameWordsSelections(QPlainTextEdit *textEdit,\n                                                           const QString &word);\n\n/**\n * @brief createLineHighlight\n * @param cursor - a Cursor object represents the line to be highlighted\n * @param highlightColor - the color to be used for highlighting. The color is decided by the callee\n * for different usages (BP, PC, Current line, ...)\n * @return ExtraSelection with highlighted line\n */\nQTextEdit::ExtraSelection createLineHighlight(const QTextCursor &cursor, QColor highlightColor);\n\n/**\n * @brief This function responsible to highlight the currently selected line\n * @param cursor - a Cursor object represents the line to be highlighted\n * @return ExtraSelection with highlighted line\n */\nQTextEdit::ExtraSelection createLineHighlightSelection(const QTextCursor &cursor);\n\n/**\n * @brief This function responsible to highlight the program counter line\n * @param cursor - a Cursor object represents the line to be highlighted\n * @return ExtraSelection with highlighted line\n */\nQTextEdit::ExtraSelection createLineHighlightPC(const QTextCursor &cursor);\n\n/**\n * @brief This function responsible to highlight a line with breakpoint\n * @param cursor - a Cursor object represents the line to be highlighted\n * @return ExtraSelection with highlighted line\n */\nQTextEdit::ExtraSelection createLineHighlightBP(const QTextCursor &cursor);\n\n#endif // CUTTER_SELECTIONHIGHLIGHT_H\n"
  },
  {
    "path": "src/common/SettingsUpgrade.cpp",
    "content": "#include \"SettingsUpgrade.h\"\n#include <QApplication>\n#include <QMessageBox>\n\n#include \"Configuration.h\"\n#include \"common/ColorThemeWorker.h\"\n\n/**\n * @brief Migrate Settings used before Cutter 1.8\n *\n * @return whether any settings have been migrated\n */\nstatic bool migrateSettingsPre18(QSettings &newSettings)\n{\n    if (newSettings.value(\"settings_migrated\", false).toBool()) {\n        return false;\n    }\n    QSettings oldSettings(QSettings::NativeFormat, QSettings::Scope::UserScope, \"Cutter\", \"Cutter\");\n    QStringList allKeys = oldSettings.allKeys();\n    if (allKeys.isEmpty()) {\n        return false;\n    }\n    qInfo() << \"Migrating Settings from pre-1.8\";\n    for (const QString &key : allKeys) {\n        newSettings.setValue(key, oldSettings.value(key));\n    }\n    oldSettings.clear();\n    QFile settingsFile(oldSettings.fileName());\n    settingsFile.remove();\n    newSettings.setValue(\"settings_migrated\", true);\n    return true;\n}\n\n#define CUTTER_SETTINGS_VERSION_CURRENT 7\n#define CUTTER_SETTINGS_VERSION_KEY \"version\"\n\n/*\n * How Settings migrations work:\n *\n * Every time settings are changed in a way that needs migration,\n * CUTTER_SETTINGS_VERSION_CURRENT is raised by 1 and a function migrateSettingsToX\n * is implemented and added to initializeSettings().\n * This function takes care of migrating from EXACTLY version X-1 to X.\n */\n\nstatic void migrateSettingsTo1(QSettings &settings)\n{\n    settings.remove(\"settings_migrated\"); // now handled by version\n    settings.remove(\"updated_custom_themes\"); // now handled by theme_version\n}\n\nstatic void migrateSettingsTo2(QSettings &settings)\n{\n    QStringList docks = settings.value(\"docks\").toStringList(); // get current list of docks\n    // replace occurences of \"PseudocodeWidget\" with \"DecompilerWidget\"\n    settings.setValue(\"docks\", docks.replaceInStrings(\"PseudocodeWidget\", \"DecompilerWidget\"));\n}\n\nstatic void migrateSettingsTo3(QSettings &settings)\n{\n    auto defaultGeometry = settings.value(\"geometry\").toByteArray();\n    auto defaultState = settings.value(\"state\").toByteArray();\n\n    auto debugGeometry = settings.value(\"debug.geometry\").toByteArray();\n    auto debugState = settings.value(\"debug.state\").toByteArray();\n\n    const auto docks = settings.value(\"docks\", QStringList()).toStringList();\n    auto unsyncList = settings.value(\"unsync\", QStringList()).toStringList();\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n    QSet<QString> unsyncDocks = unsyncList.toSet();\n#else\n    QSet<QString> unsyncDocks(unsyncList.begin(), unsyncList.end());\n#endif\n\n    QVariantMap viewProperties;\n    for (auto &dock : docks) {\n        QVariantMap properties;\n        bool synchronized = true;\n        if (unsyncDocks.contains(dock)) {\n            synchronized = false;\n        }\n        properties.insert(\"synchronized\", synchronized);\n        viewProperties.insert(dock, properties);\n    }\n\n    settings.beginWriteArray(\"layouts\", 2);\n    settings.setArrayIndex(0);\n    settings.setValue(\"name\", \"Default\");\n    settings.setValue(\"geometry\", defaultGeometry);\n    settings.setValue(\"state\", defaultState);\n    settings.setValue(\"docks\", viewProperties);\n\n    settings.setArrayIndex(1);\n    settings.setValue(\"name\", \"Debug\");\n    settings.setValue(\"geometry\", debugGeometry);\n    settings.setValue(\"state\", debugState);\n    settings.setValue(\"docks\", viewProperties);\n\n    settings.endArray();\n\n    settings.remove(\"pos\"); // Pos and size already stored within geometry\n    settings.remove(\"size\");\n    // keep geometry but with slightly different usecase\n    settings.remove(\"state\");\n    settings.remove(\"debug.geometry\");\n    settings.remove(\"debug.state\");\n    settings.remove(\"docks\");\n    settings.remove(\"unsync\");\n}\n\nstatic void renameAsmOption(QSettings &settings, const QString &oldName, const QString &newName)\n{\n    if (settings.contains(oldName)) {\n        auto value = settings.value(oldName);\n        settings.remove(oldName);\n        settings.setValue(newName, value);\n    }\n}\n\nstatic void migrateSettingsTo4(QSettings &settings)\n{\n    renameAsmOption(settings, \"asm.var.subonly\", \"asm.sub.varonly\");\n    renameAsmOption(settings, \"asm.bytespace\", \"asm.bytes.space\");\n}\n\nstatic void migrateSettingsTo5(QSettings &settings)\n{\n    renameAsmOption(settings, \"asm.var.sub\", \"asm.sub.var\");\n}\n\nstatic void migrateSettingsTo6(QSettings &settings)\n{\n    settings.remove(\"dir.projects\");\n}\n\nstatic void migrateSettingsTo7(QSettings &settings)\n{\n    auto list = settings.value(\"recentFileList\").toStringList();\n    for (auto &file : list) {\n        file.prepend(\"file://\");\n    }\n    settings.setValue(\"recentFileList\", list);\n}\n\nvoid Cutter::initializeSettings()\n{\n    QSettings::setDefaultFormat(QSettings::IniFormat);\n    QSettings settings;\n\n    int settingsVersion = settings.value(CUTTER_SETTINGS_VERSION_KEY, 0).toInt();\n    if (settingsVersion == 0) {\n        migrateSettingsPre18(settings);\n    }\n\n    if (settings.allKeys().length() > 0) {\n        if (settingsVersion > CUTTER_SETTINGS_VERSION_CURRENT) {\n            qWarning() << \"Settings have a higher version than current! Skipping migration.\";\n        } else if (settingsVersion >= 0) {\n            for (int v = settingsVersion + 1; v <= CUTTER_SETTINGS_VERSION_CURRENT; v++) {\n                qInfo() << \"Migrating Settings to Version\" << v;\n                switch (v) {\n                case 1:\n                    migrateSettingsTo1(settings);\n                    break;\n                case 2:\n                    migrateSettingsTo2(settings);\n                    break;\n                case 3:\n                    migrateSettingsTo3(settings);\n                    break;\n                case 4:\n                    migrateSettingsTo4(settings);\n                    break;\n                case 5:\n                    migrateSettingsTo5(settings);\n                    break;\n                case 6:\n                    migrateSettingsTo6(settings);\n                    break;\n                case 7:\n                    migrateSettingsTo7(settings);\n                    break;\n                default:\n                    break;\n                }\n            }\n        }\n    }\n    settings.setValue(CUTTER_SETTINGS_VERSION_KEY, CUTTER_SETTINGS_VERSION_CURRENT);\n}\n\n#define THEME_VERSION_CURRENT 2\n#define THEME_VERSION_KEY \"theme_version\"\n\nstatic void removeObsoleteOptionsFromCustomThemes()\n{\n    const QStringList options = Core()->getThemeKeys() << ColorThemeWorker::cutterSpecificOptions;\n    QStringList themes = Core()->getColorThemes();\n    for (const auto &themeName : themes) {\n        if (!ThemeWorker().isCustomTheme(themeName)) {\n            continue;\n        }\n        ColorThemeWorker::Theme sch = ThemeWorker().getTheme(themeName);\n        ColorThemeWorker::Theme updatedTheme;\n        for (auto it = sch.constBegin(); it != sch.constEnd(); ++it) {\n            if (options.contains(it.key())) {\n                updatedTheme.insert(it.key(), it.value());\n            }\n        }\n        ThemeWorker().save(updatedTheme, themeName);\n    }\n}\n\nstatic void syncCustomThemes()\n{\n    const QStringList options = Core()->getThemeKeys() << ColorThemeWorker::cutterSpecificOptions;\n    QStringList themes = Core()->getColorThemes();\n    ColorThemeWorker::Theme lightTheme = ThemeWorker().getTheme(\"cutter\"); // default light theme\n    ColorThemeWorker::Theme darkTheme = ThemeWorker().getTheme(\"ayu\"); // default dark theme\n\n    // note that there was no entry for angui.navbar.str (as it is a typo) so the\n    // default color for it will most likely be black instead of the color defined\n    // in config, unless changed by the user\n    QHash<QString, QString> renames = {\n        { \"angui.navbar.str\", \"gui.navbar.str\" },\n        { \"wordHighlight\", \"wordHighlightBg\" },\n        { \"gui.navbar.empty\", \"gui.navbar.unexplored\" },\n    };\n    const QStringList forceDefaultKeys = { \"gui.navbar.signature\", \"gui.navbar.data\" };\n\n    for (const auto &themeName : themes) {\n        if (!ThemeWorker().isCustomTheme(themeName)) {\n            continue;\n        }\n\n        ColorThemeWorker::Theme originalTheme = ThemeWorker().getTheme(themeName);\n        ColorThemeWorker::Theme updatedTheme;\n\n        for (auto it = renames.begin(); it != renames.end(); ++it) {\n            if (originalTheme.contains(it.key())) {\n                updatedTheme.insert(it.value(), originalTheme.value(it.key()));\n            }\n        }\n\n        QColor bg = originalTheme.value(\"gui.background\", QColor(Qt::white));\n        const QStringList renameValues = renames.values();\n        for (const auto &option : options) {\n            if (renameValues.contains(option)) {\n                continue;\n            }\n            if (originalTheme.contains(option) && !forceDefaultKeys.contains(option)) {\n                updatedTheme.insert(option, originalTheme.value(option));\n            } else {\n                QColor color =\n                        bg.lightness() > 128 ? lightTheme.value(option) : darkTheme.value(option);\n                updatedTheme.insert(option, color);\n            }\n        }\n\n        ThemeWorker().save(updatedTheme, themeName);\n        if (Config()->getColorTheme() == themeName) {\n            Config()->setColorTheme(themeName);\n        }\n    }\n}\n\nvoid Cutter::migrateThemes()\n{\n    QSettings settings;\n    int themeVersion = settings.value(THEME_VERSION_KEY, 0).toInt();\n    if (themeVersion >= THEME_VERSION_CURRENT) {\n        qWarning() << \"Themes have a higher version than current! Skipping migration.\";\n        return;\n    }\n    for (int v = themeVersion + 1; v <= THEME_VERSION_CURRENT; v++) {\n        qInfo() << \"Migrating Themes to Version\" << v;\n        switch (v) {\n        case 1:\n            removeObsoleteOptionsFromCustomThemes();\n            break;\n        case 2:\n            syncCustomThemes();\n            break;\n        default:\n            break;\n        }\n    }\n\n    settings.setValue(THEME_VERSION_KEY, THEME_VERSION_CURRENT);\n}\n\nstatic const char PRE_RIZIN_ORG[] = \"RadareOrg\";\nstatic const char PRE_RIZIN_APP[] = \"Cutter\";\nconst int LAST_R2_CUTTER_SETTING_VERSION = 6;\n\nbool Cutter::shouldOfferSettingImport()\n{\n    QSettings::setDefaultFormat(QSettings::IniFormat);\n    QSettings settings;\n\n    if (settings.contains(\"firstExecution\")) {\n        return false;\n    }\n    QSettings r2CutterSettings(QSettings::IniFormat, QSettings::Scope::UserScope, PRE_RIZIN_ORG,\n                               PRE_RIZIN_APP);\n    QString f = r2CutterSettings.fileName();\n    if (r2CutterSettings.value(\"firstExecution\", true) != QVariant(false)) {\n        return false; // no Cutter <= 1.12 settings to import\n    }\n    int version = r2CutterSettings.value(\"version\", -1).toInt();\n    if (version < 1 || version > LAST_R2_CUTTER_SETTING_VERSION) {\n        return false; // version too new maybe it's from r2Cutter fork instead of pre-rizin Cutter.\n    }\n    return true;\n}\n\nstatic void importOldSettings()\n{\n    // QSettings\n    QSettings::setDefaultFormat(QSettings::IniFormat);\n    QSettings r2CutterSettings(QSettings::IniFormat, QSettings::Scope::UserScope, PRE_RIZIN_ORG,\n                               PRE_RIZIN_APP);\n    QSettings newSettings;\n    for (auto key : r2CutterSettings.allKeys()) {\n        newSettings.setValue(key, r2CutterSettings.value(key));\n    }\n\n    // Color Themes\n    char *szThemes = rz_str_home(\".local/share/radare2/cons\");\n    QString r2ThemesPath = szThemes;\n    rz_mem_free(szThemes);\n    QDir r2ThemesDir(r2ThemesPath);\n    if (QFileInfo(r2ThemesPath).isDir()) {\n        QDir rzThemesDir(ThemeWorker().getCustomThemesPath());\n        if (!rzThemesDir.exists()) {\n            QDir().mkpath(rzThemesDir.absolutePath());\n        }\n        for (auto f : r2ThemesDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {\n            auto dst = rzThemesDir.absoluteFilePath(f.fileName());\n            if (QDir(dst).exists()) {\n                qInfo() << \"Theme\" << dst << \"already exists. Not overwriting with\"\n                        << f.absoluteFilePath();\n                continue;\n            }\n            qInfo() << \"Copying Theme\" << f.absoluteFilePath() << \"to\" << dst;\n            QFile::copy(f.absoluteFilePath(), dst);\n        }\n    }\n}\n\nvoid Cutter::showSettingImportDialog(int &argc, char **argv)\n{\n    // Creating temporary QApplication because this happens before anything else in Cutter is\n    // initialized\n    QApplication temporaryApp(argc, argv);\n    QSettings r2CutterSettings(QSettings::IniFormat, QSettings::Scope::UserScope, PRE_RIZIN_ORG,\n                               PRE_RIZIN_APP);\n    QString oldFile = r2CutterSettings.fileName();\n    // Can't use message translations because settings have not been imported\n    auto result =\n            QMessageBox::question(nullptr, \"Setting import\",\n                                  QString(\"Do you want to import settings from %1?\").arg(oldFile));\n    if (result == QMessageBox::Yes) {\n        importOldSettings();\n    }\n}\n"
  },
  {
    "path": "src/common/SettingsUpgrade.h",
    "content": "#ifndef COMMON_SETTINGS_UPGRADE_H\n#define COMMON_SETTINGS_UPGRADE_H\n\n#include <QSettings>\n#include <core/Cutter.h>\n\nnamespace Cutter {\nvoid initializeSettings();\n/**\n * @brief Check if Cutter should offer importing settings from version that can't be directly\n * updated.\n * @return True if this is first time running Cutter and r2 based Cutter <= 1.12 settings exist.\n */\nbool shouldOfferSettingImport();\n/**\n * @brief Ask user if Cutter should import settings from pre-rizin Cutter.\n *\n * This function assume that QApplication isn't running yet.\n */\nvoid showSettingImportDialog(int &argc, char **argv);\nvoid migrateThemes();\n}\n\n#endif // COMMON_SETTINGS_UPGRADE_H\n"
  },
  {
    "path": "src/common/StringsTask.h",
    "content": "\n#ifndef STRINGSASYNCTASK_H\n#define STRINGSASYNCTASK_H\n\n#include \"common/AsyncTask.h\"\n#include \"core/Cutter.h\"\n\nclass StringsTask : public AsyncTask\n{\n    Q_OBJECT\n\npublic:\n    QString getTitle() override { return tr(\"Searching for Strings\"); }\n\nsignals:\n    void stringSearchFinished(const QList<StringDescription> &strings);\n\nprotected:\n    void runTask() override\n    {\n        auto strings = Core()->getAllStrings();\n        emit stringSearchFinished(strings);\n    }\n};\n\n#endif // STRINGSASYNCTASK_H\n"
  },
  {
    "path": "src/common/SvgIconEngine.cpp",
    "content": "\n#include \"SvgIconEngine.h\"\n\n#include <QSvgRenderer>\n#include <QPainter>\n#include <QFile>\n\n#include \"Helpers.h\"\n\nSvgIconEngine::SvgIconEngine(const QString &filename)\n{\n    QFile file(filename);\n    file.open(QFile::ReadOnly);\n    this->svgData = file.readAll();\n}\n\nSvgIconEngine::SvgIconEngine(const QString &filename, const QColor &tintColor)\n    : SvgIconEngine(filename)\n{\n    this->svgData = qhelpers::applyColorToSvg(svgData, tintColor);\n}\n\nSvgIconEngine::SvgIconEngine(const QString &filename, QPalette::ColorRole colorRole)\n    : SvgIconEngine(filename, QPalette().color(colorRole))\n{\n}\n\nvoid SvgIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode /*mode*/,\n                          QIcon::State /*state*/)\n{\n    QSvgRenderer renderer(svgData);\n    renderer.render(painter, rect);\n}\n\nQIconEngine *SvgIconEngine::clone() const\n{\n    return new SvgIconEngine(*this);\n}\n\nQPixmap SvgIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)\n{\n    QImage img(size, QImage::Format_ARGB32);\n    img.fill(qRgba(0, 0, 0, 0));\n    QPixmap pix = QPixmap::fromImage(img, Qt::NoFormatConversion);\n    {\n        QPainter painter(&pix);\n        QRect r(QPoint(0, 0), size);\n        this->paint(&painter, r, mode, state);\n    }\n    return pix;\n}\n"
  },
  {
    "path": "src/common/SvgIconEngine.h",
    "content": "\n#ifndef SVGICONENGINE_H\n#define SVGICONENGINE_H\n\n#include <QIconEngine>\n#include <QPalette>\n\nclass SvgIconEngine : public QIconEngine\n{\nprivate:\n    QByteArray svgData;\n\npublic:\n    explicit SvgIconEngine(const QString &filename);\n\n    SvgIconEngine(const QString &filename, const QColor &tintColor);\n    SvgIconEngine(const QString &filename, QPalette::ColorRole colorRole);\n\n    void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;\n    QIconEngine *clone() const override;\n    QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;\n};\n\n#endif // SVGICONENGINE_H\n"
  },
  {
    "path": "src/common/SyntaxHighlighter.cpp",
    "content": "\n#include \"SyntaxHighlighter.h\"\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n\n#    include \"Configuration.h\"\n\n#    include <KSyntaxHighlighting/Theme>\n\nSyntaxHighlighter::SyntaxHighlighter(QTextDocument *document)\n    : KSyntaxHighlighting::SyntaxHighlighter(document)\n{\n    connect(Config(), &Configuration::kSyntaxHighlightingThemeChanged, this,\n            &SyntaxHighlighter::updateTheme);\n    updateTheme();\n}\n\nvoid SyntaxHighlighter::updateTheme()\n{\n    setTheme(Config()->getKSyntaxHighlightingTheme());\n    rehighlight();\n}\n\n#endif\n\nFallbackSyntaxHighlighter::FallbackSyntaxHighlighter(QTextDocument *parent)\n    : QSyntaxHighlighter(parent), commentStartExpression(\"/\\\\*\"), commentEndExpression(\"\\\\*/\")\n{\n    HighlightingRule rule;\n    QStringList keywordPatterns;\n\n    // C language keywords\n    keywordPatterns << \"\\\\bauto\\\\b\"\n                    << \"\\\\bdouble\\\\b\"\n                    << \"\\\\bint\\\\b\"\n                    << \"\\\\bstruct\\\\b\"\n                    << \"\\\\bbreak\\\\b\"\n                    << \"\\\\belse\\\\b\"\n                    << \"\\\\blong\\\\b\"\n                    << \"\\\\switch\\\\b\"\n                    << \"\\\\bcase\\\\b\"\n                    << \"\\\\benum\\\\b\"\n                    << \"\\\\bregister\\\\b\"\n                    << \"\\\\btypedef\\\\b\"\n                    << \"\\\\bchar\\\\b\"\n                    << \"\\\\bextern\\\\b\"\n                    << \"\\\\breturn\\\\b\"\n                    << \"\\\\bunion\\\\b\"\n                    << \"\\\\bconst\\\\b\"\n                    << \"\\\\bfloat\\\\b\"\n                    << \"\\\\bshort\\\\b\"\n                    << \"\\\\bunsigned\\\\b\"\n                    << \"\\\\bcontinue\\\\b\"\n                    << \"\\\\bfor\\\\b\"\n                    << \"\\\\bsigned\\\\b\"\n                    << \"\\\\bvoid\\\\b\"\n                    << \"\\\\bdefault\\\\b\"\n                    << \"\\\\bgoto\\\\b\"\n                    << \"\\\\bsizeof\\\\b\"\n                    << \"\\\\bvolatile\\\\b\"\n                    << \"\\\\bdo\\\\b\"\n                    << \"\\\\bif\\\\b\"\n                    << \"\\\\static\\\\b\"\n                    << \"\\\\while\\\\b\";\n\n    QTextCharFormat keywordFormat;\n    keywordFormat.setForeground(QColor(80, 200, 215));\n\n    for (const auto &pattern : keywordPatterns) {\n        rule.pattern.setPattern(pattern);\n        rule.format = keywordFormat;\n        highlightingRules.append(rule);\n    }\n\n    // Functions\n    rule.pattern.setPattern(\"\\\\b[A-Za-z0-9_]+(?=\\\\()\");\n    rule.format.clearBackground();\n    rule.format.clearForeground();\n    rule.format.setFontItalic(true);\n    rule.format.setForeground(Qt::darkCyan);\n    highlightingRules.append(rule);\n\n    // single-line comment\n    rule.pattern.setPattern(\"//[^\\n]*\");\n    rule.format.clearBackground();\n    rule.format.clearForeground();\n    rule.format.setForeground(Qt::gray);\n    highlightingRules.append(rule);\n\n    // quotation\n    rule.pattern.setPattern(\"\\\".*\\\"\");\n    rule.format.clearBackground();\n    rule.format.clearForeground();\n    rule.format.setForeground(Qt::darkGreen);\n    highlightingRules.append(rule);\n\n    multiLineCommentFormat.setForeground(Qt::gray);\n}\n\nvoid FallbackSyntaxHighlighter::highlightBlock(const QString &text)\n{\n    for (const auto &it : highlightingRules) {\n        auto matchIterator = it.pattern.globalMatch(text);\n        while (matchIterator.hasNext()) {\n            const auto match = matchIterator.next();\n            setFormat(match.capturedStart(), match.capturedLength(), it.format);\n        }\n    }\n\n    setCurrentBlockState(0);\n\n    int startIndex = 0;\n    if (previousBlockState() != 1) {\n        startIndex = text.indexOf(commentStartExpression);\n    }\n\n    while (startIndex >= 0) {\n        const auto match = commentEndExpression.match(text, startIndex);\n        const int endIndex = match.capturedStart();\n        int commentLength = 0;\n\n        if (endIndex == -1) {\n            setCurrentBlockState(1);\n            commentLength = text.length() - startIndex;\n        } else {\n            commentLength = endIndex - startIndex + match.capturedLength();\n        }\n\n        setFormat(startIndex, commentLength, multiLineCommentFormat);\n        startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);\n    }\n}\n"
  },
  {
    "path": "src/common/SyntaxHighlighter.h",
    "content": "#ifndef SYNTAXHIGHLIGHTER_H\n#define SYNTAXHIGHLIGHTER_H\n\n#include \"CutterCommon.h\"\n#include <QSyntaxHighlighter>\n#include <QVector>\n#include <QTextDocument>\n#include <QRegularExpression>\n#include <QTextCharFormat>\n\n#ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING\n\n#    include <KSyntaxHighlighting/SyntaxHighlighter>\n\nclass SyntaxHighlighter : public KSyntaxHighlighting::SyntaxHighlighter\n{\n    Q_OBJECT\n\npublic:\n    SyntaxHighlighter(QTextDocument *document);\n\nprivate slots:\n    void updateTheme();\n};\n\n#endif\n\n/**\n * SyntaxHighlighter to be used when KSyntaxHighlighting is not available\n */\nclass CUTTER_EXPORT FallbackSyntaxHighlighter : public QSyntaxHighlighter\n{\n    Q_OBJECT\n\npublic:\n    FallbackSyntaxHighlighter(QTextDocument *parent = nullptr);\n    virtual ~FallbackSyntaxHighlighter() = default;\n\nprotected:\n    void highlightBlock(const QString &text) override;\n\nprivate:\n    struct HighlightingRule\n    {\n        QRegularExpression pattern;\n        QTextCharFormat format;\n    };\n\n    QVector<HighlightingRule> highlightingRules;\n\n    QRegularExpression commentStartExpression;\n    QRegularExpression commentEndExpression;\n\n    QTextCharFormat multiLineCommentFormat;\n};\n\n#endif\n"
  },
  {
    "path": "src/common/TempConfig.cpp",
    "content": "\n#include <cassert>\n\n#include \"core/Cutter.h\"\n#include \"TempConfig.h\"\n\nTempConfig::~TempConfig()\n{\n    for (auto i = resetValues.constBegin(); i != resetValues.constEnd(); ++i) {\n        switch (i.value().type()) {\n        case QVariant::String:\n            Core()->setConfig(i.key(), i.value().toString());\n            break;\n        case QVariant::Int:\n            Core()->setConfig(i.key(), i.value().toInt());\n            break;\n        case QVariant::Bool:\n            Core()->setConfig(i.key(), i.value().toBool());\n            break;\n        default:\n            assert(false);\n            break;\n        }\n    }\n}\n\nTempConfig &TempConfig::set(const QString &key, const QString &value)\n{\n    if (!resetValues.contains(key)) {\n        resetValues[key] = Core()->getConfig(key);\n    }\n\n    Core()->setConfig(key, value);\n    return *this;\n}\n\nTempConfig &TempConfig::set(const QString &key, const char *value)\n{\n    if (!resetValues.contains(key)) {\n        resetValues[key] = Core()->getConfig(key);\n    }\n\n    Core()->setConfig(key, value);\n    return *this;\n}\n\nTempConfig &TempConfig::set(const QString &key, int value)\n{\n    if (!resetValues.contains(key)) {\n        resetValues[key] = Core()->getConfigi(key);\n    }\n\n    Core()->setConfig(key, value);\n    return *this;\n}\n\nTempConfig &TempConfig::set(const QString &key, bool value)\n{\n    if (!resetValues.contains(key)) {\n        resetValues[key] = Core()->getConfigb(key);\n    }\n\n    Core()->setConfig(key, value);\n    return *this;\n}\n"
  },
  {
    "path": "src/common/TempConfig.h",
    "content": "\n#ifndef TEMPCONFIG_H\n#define TEMPCONFIG_H\n\n#include \"core/CutterCommon.h\"\n\n#include <QString>\n#include <QVariant>\n\n/**\n * @brief Class for temporary modifying Rizin `e` configuration.\n *\n * Modified values will be restored at the end of scope. This is useful when using a Rizin command\n * that can only be configured using `e` configuration and doesn't accept arguments. TempConfig::set\n * calls can be chained. If a command or Rizin method accepts arguments directly it is preferred to\n * use those instead of temporary modifying global configuration.\n *\n * \\code\n * {\n *     TempConfig tempConfig;\n *     tempConfig.set(\"asm.arch\", \"x86\").set(\"asm.comments\", false);\n *     // config automatically restored at the end of scope\n * }\n * \\endcode\n */\nclass CUTTER_EXPORT TempConfig\n{\npublic:\n    TempConfig() = default;\n    ~TempConfig();\n\n    TempConfig &set(const QString &key, const QString &value);\n    TempConfig &set(const QString &key, const char *value);\n    TempConfig &set(const QString &key, int value);\n    TempConfig &set(const QString &key, bool value);\n\nprivate:\n    TempConfig(const TempConfig &) = delete;\n    TempConfig &operator=(const TempConfig &) = delete;\n    QMap<QString, QVariant> resetValues;\n};\n\n#endif // TEMPCONFIG_H\n"
  },
  {
    "path": "src/common/UpdateWorker.cpp",
    "content": "#include \"UpdateWorker.h\"\n\n#if CUTTER_UPDATE_WORKER_AVAILABLE\n#    include <QUrl>\n#    include <QFile>\n#    include <QTimer>\n#    include <QEventLoop>\n#    include <QDataStream>\n#    include <QJsonObject>\n#    include <QApplication>\n#    include <QJsonDocument>\n#    include <QDesktopServices>\n#    include <QtNetwork/QNetworkReply>\n#    include <QtNetwork/QNetworkRequest>\n#    include <QStandardPaths>\n\n#    include <QProgressDialog>\n#    include <QPushButton>\n#    include <QFileDialog>\n#    include <QMessageBox>\n#    include \"common/Configuration.h\"\n#    include \"CutterConfig.h\"\n#endif\n\n#if CUTTER_UPDATE_WORKER_AVAILABLE\nUpdateWorker::UpdateWorker(QObject *parent) : QObject(parent), pending(false)\n{\n    connect(&t, &QTimer::timeout, this, [this]() {\n        if (pending) {\n            disconnect(checkReply, nullptr, this, nullptr);\n            checkReply->close();\n            checkReply->deleteLater();\n            emit checkComplete(QVersionNumber(),\n                               tr(\"Time limit exceeded during version check. Please check your \"\n                                  \"internet connection and try again.\"));\n        }\n    });\n}\n\nvoid UpdateWorker::checkCurrentVersion(time_t timeoutMs)\n{\n    QUrl url(\"https://api.github.com/repos/rizinorg/cutter/releases/latest\");\n    QNetworkRequest request;\n    request.setUrl(url);\n\n    t.setInterval(timeoutMs);\n    t.setSingleShot(true);\n    t.start();\n\n    checkReply = nm.get(request);\n    connect(checkReply, &QNetworkReply::finished, this, &UpdateWorker::serveVersionCheckReply);\n    pending = true;\n}\n\nvoid UpdateWorker::showUpdateDialog(bool showDontCheckForUpdatesButton)\n{\n    QMessageBox mb;\n    mb.setWindowTitle(tr(\"Version control\"));\n    mb.setText(tr(\"There is an update available for Cutter.<br/>\") + \"<b>\" + tr(\"Current version:\")\n               + \"</b> \" CUTTER_VERSION_FULL \"<br/>\" + \"<b>\" + tr(\"Latest version:\") + \"</b> \"\n               + latestVersion.toString() + \"<br/><br/>\"\n               + tr(\"To update, please check the link:<br/>\")\n               + QString(\"<a href=\\\"https://github.com/rizinorg/cutter/releases/tag/v%1\\\">\"\n                         \"https://github.com/rizinorg/cutter/releases/tag/v%1</a><br/>\")\n                         .arg(latestVersion.toString()));\n    if (showDontCheckForUpdatesButton) {\n        mb.setStandardButtons(QMessageBox::Reset | QMessageBox::Ok);\n        mb.button(QMessageBox::Reset)->setText(tr(\"Don't check for updates automatically\"));\n    } else {\n        mb.setStandardButtons(QMessageBox::Ok);\n    }\n    mb.setDefaultButton(QMessageBox::Ok);\n    int ret = mb.exec();\n    if (ret == QMessageBox::Reset) {\n        Config()->setAutoUpdateEnabled(false);\n    }\n}\n\nvoid UpdateWorker::serveVersionCheckReply()\n{\n    pending = false;\n    QString versionReplyStr = \"\";\n    QString errStr = \"\";\n    if (checkReply->error()) {\n        errStr = checkReply->errorString();\n    } else {\n        versionReplyStr = QJsonDocument::fromJson(checkReply->readAll())\n                                  .object()\n                                  .value(\"tag_name\")\n                                  .toString();\n        versionReplyStr.remove('v');\n    }\n    QVersionNumber versionReply = QVersionNumber::fromString(versionReplyStr);\n    if (!versionReply.isNull()) {\n        latestVersion = versionReply;\n    }\n    checkReply->close();\n    checkReply->deleteLater();\n    emit checkComplete(versionReply, errStr);\n}\n\nQVersionNumber UpdateWorker::currentVersionNumber()\n{\n    return QVersionNumber(CUTTER_VERSION_MAJOR, CUTTER_VERSION_MINOR, CUTTER_VERSION_PATCH);\n}\n#endif // CUTTER_UPDATE_WORKER_AVAILABLE\n"
  },
  {
    "path": "src/common/UpdateWorker.h",
    "content": "#ifndef UPDATEWORKER_H\n#define UPDATEWORKER_H\n\n#include <QtGlobal>\n\n#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))\n#    define CUTTER_UPDATE_WORKER_AVAILABLE 1\n#else\n#    define CUTTER_UPDATE_WORKER_AVAILABLE 0\n#endif\n\n#if CUTTER_UPDATE_WORKER_AVAILABLE\n#    include <QDir>\n#    include <QTimer>\n#    include <QObject>\n#    include <QtNetwork/QNetworkAccessManager>\n\n#    include <QVersionNumber>\n#endif\n\n#if CUTTER_UPDATE_WORKER_AVAILABLE\nclass QNetworkReply;\n\n/**\n * @class UpdateWorker\n * @brief The UpdateWorker class is a class providing API to check for current Cutter version.\n */\n\nclass UpdateWorker : public QObject\n{\n    Q_OBJECT\npublic:\n    explicit UpdateWorker(QObject *parent = nullptr);\n\n    /**\n     * @fn void UpdateWorker::checkCurrentVersion(time_t timeoutMs)\n     *\n     * Sends request to determine current version of Cutter.\n     * If there is no response in @a timeoutMs milliseconds, emits\n     * @fn UpdateWorker::checkComplete(const QString& currVerson, const QString& errorMsg)\n     * with timeout error message.\n     *\n     *\n     * @sa checkComplete(const QString& verson, const QString& errorMsg)\n     */\n\n    void checkCurrentVersion(time_t timeoutMs);\n\n    /**\n     * @fn void UpdateWorker::showUpdateDialog()\n     *\n     * Shows dialog that allows user to download latest version of Cutter from website.\n     * This dialog also has \"Don't check for updates\" button which disables on-start update\n     * checks if @a showDontCheckForUpdatesButton is true.\n     */\n    void showUpdateDialog(bool showDontCheckForUpdatesButton);\n\n    /**\n     * @return the version of this Cutter binary, derived from CUTTER_VERSION_MAJOR,\n     * CUTTER_VERSION_MINOR and CUTTER_VERSION_PATCH.\n     */\n    static QVersionNumber currentVersionNumber();\n\nsignals:\n    /**\n     * @fn UpdateWorker::checkComplete(const QString& verson, const QString& errorMsg)\n     *\n     * The signal is emitted when check has been done with an empty @a errorMsg string.\n     * In case of an error @a currVerson is null and @a errorMsg contains description\n     * of error.\n     */\n    void checkComplete(const QVersionNumber &currVerson, const QString &errorMsg);\n\nprivate slots:\n    void serveVersionCheckReply();\n\nprivate:\n    QNetworkAccessManager nm;\n    QVersionNumber latestVersion;\n    QTimer t;\n    bool pending;\n    QNetworkReply *checkReply;\n};\n\n#endif // CUTTER_UPDATE_WORKER_AVAILABLE\n#endif // UPDATEWORKER_H\n"
  },
  {
    "path": "src/core/Basefind.cpp",
    "content": "#include \"Basefind.h\"\n\nbool Basefind::threadCallback(const RzBaseFindThreadInfo *info, void *user)\n{\n    auto th = reinterpret_cast<Basefind *>(user);\n    return th->updateProgress(info);\n}\n\nBasefind::Basefind(CutterCore *core)\n    : core(core),\n      scores(nullptr),\n      continue_run(true)\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n      ,\n      mutex(QMutex::Recursive)\n#endif\n{\n    memset(&options, 0, sizeof(RzBaseFindOpt));\n}\n\nBasefind::~Basefind()\n{\n    cancel();\n    wait();\n    rz_list_free(scores);\n}\n\nbool Basefind::setOptions(const RzBaseFindOpt *opts)\n{\n    mutex.lock();\n    options.max_threads = opts->max_threads;\n    options.pointer_size = opts->pointer_size;\n    options.start_address = opts->start_address;\n    options.end_address = opts->end_address;\n    options.alignment = opts->alignment;\n    options.min_score = opts->min_score;\n    options.min_string_len = opts->min_string_len;\n    mutex.unlock();\n\n    if (options.start_address >= options.end_address) {\n        qWarning() << tr(\"Start address is >= end address\");\n        return false;\n    } else if (options.alignment < RZ_BASEFIND_BASE_ALIGNMENT) {\n        qWarning() << tr(\"Alignment must be at least \")\n                   << QString::asprintf(\"0x%x\", RZ_BASEFIND_BASE_ALIGNMENT);\n        return false;\n    } else if (options.min_score < 1) {\n        qWarning() << tr(\"Min score must be at least 1\");\n        return false;\n    } else if (options.min_string_len < 1) {\n        qWarning() << tr(\"Min string length must be at least 1\");\n        return false;\n    }\n    return true;\n}\n\nvoid Basefind::run()\n{\n    qRegisterMetaType<BasefindCoreStatusDescription>();\n\n    mutex.lock();\n    rz_list_free(scores);\n    scores = nullptr;\n    continue_run = true;\n    mutex.unlock();\n\n    core->coreMutex.lock();\n    options.callback = threadCallback;\n    options.user = this;\n    scores = rz_basefind(core->core_, &options);\n    core->coreMutex.unlock();\n\n    emit complete();\n}\n\nvoid Basefind::cancel()\n{\n    mutex.lock();\n    continue_run = false;\n    mutex.unlock();\n}\n\nQList<BasefindResultDescription> Basefind::results()\n{\n    QList<BasefindResultDescription> pairs;\n    RzListIter *it;\n    RzBaseFindScore *pair;\n    CutterRzListForeach (scores, it, RzBaseFindScore, pair) {\n        BasefindResultDescription desc;\n        desc.candidate = pair->candidate;\n        desc.score = pair->score;\n        pairs.push_back(desc);\n    }\n    return pairs;\n}\n\nbool Basefind::updateProgress(const RzBaseFindThreadInfo *info)\n{\n    mutex.lock();\n\n    BasefindCoreStatusDescription status;\n    status.index = info->thread_idx;\n    status.percentage = info->percentage;\n\n    emit progress(status);\n    bool ret = continue_run;\n    mutex.unlock();\n    return ret;\n}\n"
  },
  {
    "path": "src/core/Basefind.h",
    "content": "#ifndef CUTTER_BASEFIND_CORE_H\n#define CUTTER_BASEFIND_CORE_H\n\n#include <QThread>\n#include <QMutex>\n\n#include \"Cutter.h\"\n#include \"CutterDescriptions.h\"\n#include <rz_basefind.h>\n\nclass CutterCore;\n\nclass Basefind : public QThread\n{\n    Q_OBJECT\n\npublic:\n    explicit Basefind(CutterCore *core);\n    virtual ~Basefind();\n\n    void run();\n    bool setOptions(const RzBaseFindOpt *opts);\n    QList<BasefindResultDescription> results();\n\npublic slots:\n    void cancel();\n\nsignals:\n    void progress(BasefindCoreStatusDescription status);\n    void complete();\n\nprivate:\n    CutterCore *const core;\n    RzList *scores;\n    bool continue_run;\n    RzBaseFindOpt options;\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n    QMutex mutex;\n#else\n    QRecursiveMutex mutex;\n#endif\n\n    bool updateProgress(const RzBaseFindThreadInfo *info);\n    static bool threadCallback(const RzBaseFindThreadInfo *info, void *user);\n};\n\n#endif // CUTTER_BASEFIND_CORE_H\n"
  },
  {
    "path": "src/core/Cutter.cpp",
    "content": "#include <QJsonArray>\n#include <QJsonObject>\n#include <QRegularExpression>\n#include <QDir>\n#include <QCoreApplication>\n#include <QVector>\n#include <QStringList>\n#include <QStandardPaths>\n\n#include <cassert>\n#include <memory>\n\n#include \"CutterDescriptions.h\"\n#include \"common/TempConfig.h\"\n#include \"common/BasicInstructionHighlighter.h\"\n#include \"common/Configuration.h\"\n#include \"common/AsyncTask.h\"\n#include \"common/RizinTask.h\"\n#include \"dialogs/MarkDialog.h\"\n#include \"dialogs/RizinTaskDialog.h\"\n#include \"common/Json.h\"\n#include \"core/Cutter.h\"\n#include \"Decompiler.h\"\n\n#include <rz_asm.h>\n#include <rz_cmd.h>\n#include <rz_socket.h>\n#include <sdb.h>\n\nstatic CutterCore *uniqueInstance;\n\n#define RZ_JSON_KEY(name) static const QString name = QStringLiteral(#name)\n\nnamespace RJsonKey {\nRZ_JSON_KEY(addr);\nRZ_JSON_KEY(address);\nRZ_JSON_KEY(addrs);\nRZ_JSON_KEY(addr_end);\nRZ_JSON_KEY(arrow);\nRZ_JSON_KEY(baddr);\nRZ_JSON_KEY(bind);\nRZ_JSON_KEY(blocks);\nRZ_JSON_KEY(blocksize);\nRZ_JSON_KEY(bytes);\nRZ_JSON_KEY(calltype);\nRZ_JSON_KEY(cc);\nRZ_JSON_KEY(classname);\nRZ_JSON_KEY(code);\nRZ_JSON_KEY(comment);\nRZ_JSON_KEY(comments);\nRZ_JSON_KEY(cost);\nRZ_JSON_KEY(data);\nRZ_JSON_KEY(description);\nRZ_JSON_KEY(ebbs);\nRZ_JSON_KEY(edges);\nRZ_JSON_KEY(enabled);\nRZ_JSON_KEY(entropy);\nRZ_JSON_KEY(fcn_addr);\nRZ_JSON_KEY(fcn_name);\nRZ_JSON_KEY(fields);\nRZ_JSON_KEY(file);\nRZ_JSON_KEY(flag);\nRZ_JSON_KEY(flags);\nRZ_JSON_KEY(flagname);\nRZ_JSON_KEY(format);\nRZ_JSON_KEY(from);\nRZ_JSON_KEY(functions);\nRZ_JSON_KEY(graph);\nRZ_JSON_KEY(haddr);\nRZ_JSON_KEY(hw);\nRZ_JSON_KEY(in_functions);\nRZ_JSON_KEY(index);\nRZ_JSON_KEY(jump);\nRZ_JSON_KEY(laddr);\nRZ_JSON_KEY(lang);\nRZ_JSON_KEY(len);\nRZ_JSON_KEY(length);\nRZ_JSON_KEY(license);\nRZ_JSON_KEY(methods);\nRZ_JSON_KEY(name);\nRZ_JSON_KEY(realname);\nRZ_JSON_KEY(nargs);\nRZ_JSON_KEY(nbbs);\nRZ_JSON_KEY(nlocals);\nRZ_JSON_KEY(offset);\nRZ_JSON_KEY(opcode);\nRZ_JSON_KEY(opcodes);\nRZ_JSON_KEY(ordinal);\nRZ_JSON_KEY(libname);\nRZ_JSON_KEY(outdegree);\nRZ_JSON_KEY(paddr);\nRZ_JSON_KEY(path);\nRZ_JSON_KEY(perm);\nRZ_JSON_KEY(pid);\nRZ_JSON_KEY(plt);\nRZ_JSON_KEY(prot);\nRZ_JSON_KEY(ref);\nRZ_JSON_KEY(refs);\nRZ_JSON_KEY(reg);\nRZ_JSON_KEY(rwx);\nRZ_JSON_KEY(section);\nRZ_JSON_KEY(sections);\nRZ_JSON_KEY(size);\nRZ_JSON_KEY(stackframe);\nRZ_JSON_KEY(status);\nRZ_JSON_KEY(string);\nRZ_JSON_KEY(strings);\nRZ_JSON_KEY(symbols);\nRZ_JSON_KEY(text);\nRZ_JSON_KEY(to);\nRZ_JSON_KEY(trace);\nRZ_JSON_KEY(type);\nRZ_JSON_KEY(uid);\nRZ_JSON_KEY(vaddr);\nRZ_JSON_KEY(value);\nRZ_JSON_KEY(vsize);\n}\n\n#undef RZ_JSON_KEY\n\nstatic void updateOwnedCharPtr(char *&variable, const QString &newValue)\n{\n    auto data = newValue.toUtf8();\n    RZ_FREE(variable)\n    variable = strdup(data.data());\n}\n\nstatic bool reg_sync(RzCore *core, RzRegisterType type, bool write)\n{\n    if (rz_core_is_debug(core)) {\n        return rz_debug_reg_sync(core->dbg, type, write);\n    }\n    return true;\n}\n\nRzCoreLocked::RzCoreLocked(CutterCore *core) : core(core)\n{\n    core->coreMutex.lock();\n    assert(core->coreLockDepth >= 0);\n    core->coreLockDepth++;\n    if (core->coreLockDepth == 1) {\n        assert(core->coreBed);\n        rz_cons_sleep_end(core->coreBed);\n        core->coreBed = nullptr;\n    }\n}\n\nRzCoreLocked::~RzCoreLocked()\n{\n    assert(core->coreLockDepth > 0);\n    core->coreLockDepth--;\n    if (core->coreLockDepth == 0) {\n        core->coreBed = rz_cons_sleep_begin();\n    }\n    core->coreMutex.unlock();\n}\n\nRzCoreLocked::operator RzCore *() &\n{\n    return core->core_;\n}\n\nRzCore *RzCoreLocked::operator->() &\n{\n    return core->core_;\n}\n\n#define CORE_LOCK() RzCoreLocked core(this)\n\nstatic void cutterREventCallback(RzEvent *, int type, void *user, void *data)\n{\n    auto core = reinterpret_cast<CutterCore *>(user);\n    core->handleREvent(type, data);\n}\n\nCutterCore::CutterCore(QObject *parent)\n    : QObject(parent)\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n      ,\n      coreMutex(QMutex::Recursive)\n#endif\n{\n    if (uniqueInstance) {\n        throw std::logic_error(\"Only one instance of CutterCore must exist\");\n    }\n    uniqueInstance = this;\n}\n\nCutterCore *CutterCore::instance()\n{\n    return uniqueInstance;\n}\n\nvoid CutterCore::initialize(bool loadPlugins)\n{\n    rz_cons_new(); // initialize console\n    core_ = rz_core_new();\n\n#if defined(MACOS_RZ_BUNDLED)\n    auto app_path = QDir(QCoreApplication::applicationDirPath());\n    app_path.cdUp();\n    app_path.cd(\"Resources\");\n    qInfo() << \"Setting Rizin prefix =\" << app_path.absolutePath()\n            << \" for macOS Application Bundle.\";\n    rz_path_set_prefix(core_->sys_path, app_path.absolutePath().toUtf8().constData());\n#endif\n\n    char **env = rz_sys_get_environ();\n    core_->io->envprofile = rz_run_get_environ_profile(env);\n    rz_core_task_sync_begin(&core_->tasks);\n    coreBed = rz_cons_sleep_begin();\n    CORE_LOCK();\n\n    rz_event_hook(core_->analysis->ev, RZ_EVENT_ALL, cutterREventCallback, this);\n\n    if (loadPlugins) {\n        setConfig(\"cfg.plugins\", true);\n        rz_core_loadlibs(this->core_, RZ_CORE_LOADLIBS_ALL);\n    } else {\n        setConfig(\"cfg.plugins\", false);\n    }\n    // IMPLICIT rz_bin_iobind (core_->bin, core_->io);\n\n    // Otherwise Rizin may ask the user for input and Cutter would freeze\n    setConfig(\"scr.interactive\", false);\n\n    // Temporary workaround for https://github.com/rizinorg/rizin/issues/2741\n    // Otherwise sometimes disassembly is truncated.\n    // The blocksize here is a rather arbitrary value larger than the default 0x100.\n    rz_core_block_size(core, 0x400);\n\n    // Initialize graph node highlighter\n    bbHighlighter = new BasicBlockHighlighter();\n\n    // Initialize Async tasks manager\n    asyncTaskManager = new AsyncTaskManager(this);\n}\n\nCutterCore::~CutterCore()\n{\n    delete bbHighlighter;\n    rz_cons_sleep_end(coreBed);\n    rz_core_task_sync_end(&core_->tasks);\n    rz_core_free(this->core_);\n    rz_cons_free();\n    assert(uniqueInstance == this);\n    uniqueInstance = nullptr;\n}\n\nRzCoreLocked CutterCore::lock()\n{\n    return RzCoreLocked(this);\n}\n\nRzCoreLocked CutterCore::core()\n{\n    return lock();\n}\n\nQDir CutterCore::getCutterRCDefaultDirectory() const\n{\n    return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);\n}\n\nQVector<QString> CutterCore::getCutterRCFilePaths() const\n{\n    QVector<QString> result;\n    result.push_back(QFileInfo(QDir::home(), \".cutterrc\").absoluteFilePath());\n    QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation);\n    for (auto &location : locations) {\n        result.push_back(QFileInfo(QDir(location), \".cutterrc\").absoluteFilePath());\n    }\n    result.push_back(QFileInfo(getCutterRCDefaultDirectory(), \"rc\")\n                             .absoluteFilePath()); // File in config editor is from this path\n    return result;\n}\n\nvoid CutterCore::loadCutterRC()\n{\n    CORE_LOCK();\n    const auto result = getCutterRCFilePaths();\n    for (auto &cutterRCFilePath : result) {\n        auto cutterRCFileInfo = QFileInfo(cutterRCFilePath);\n        if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {\n            continue;\n        }\n        qInfo() << tr(\"Loading initialization file from \") << cutterRCFilePath;\n        rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());\n        rz_cons_flush();\n    }\n}\n\nvoid CutterCore::loadDefaultCutterRC()\n{\n    CORE_LOCK();\n    auto cutterRCFilePath = QFileInfo(getCutterRCDefaultDirectory(), \"rc\").absoluteFilePath();\n    const auto cutterRCFileInfo = QFileInfo(cutterRCFilePath);\n    if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {\n        return;\n    }\n    qInfo() << tr(\"Loading initialization file from \") << cutterRCFilePath;\n    rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());\n    rz_cons_flush();\n}\n\nQList<QString> CutterCore::sdbList(QString path)\n{\n    CORE_LOCK();\n    QList<QString> list = QList<QString>();\n    Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);\n    if (root && root->ns) {\n        for (const auto &nsi : CutterRzList<SdbNs>(root->ns)) {\n            list << nsi->name;\n        }\n    }\n    return list;\n}\n\nusing PVectorPtr = std::unique_ptr<RzPVector, decltype(&rz_pvector_free)>;\nstatic PVectorPtr makePVectorPtr(RzPVector *vec)\n{\n    return { vec, rz_pvector_free };\n}\n\nstatic bool foreach_keys_cb(void *user, const SdbKv *kv)\n{\n    auto list = reinterpret_cast<QList<QString> *>(user);\n    *list << kv->base.key;\n    return true;\n}\n\nQList<QString> CutterCore::sdbListKeys(QString path)\n{\n    CORE_LOCK();\n    QList<QString> list = QList<QString>();\n    Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);\n    if (root) {\n        sdb_foreach(root, foreach_keys_cb, &list);\n    }\n    return list;\n}\n\nQString CutterCore::sdbGet(QString path, QString key)\n{\n    CORE_LOCK();\n    Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);\n    if (db) {\n        const char *val = sdb_const_get(db, key.toUtf8().constData());\n        if (val && *val)\n            return val;\n    }\n    return QString();\n}\n\nbool CutterCore::sdbSet(QString path, QString key, QString val)\n{\n    CORE_LOCK();\n    Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 1);\n    if (!db)\n        return false;\n    return sdb_set(db, key.toUtf8().constData(), val.toUtf8().constData());\n}\n\nQString CutterCore::sanitizeStringForCommand(QString s)\n{\n    static const QRegularExpression regexp(\";|@\");\n    return s.replace(regexp, QStringLiteral(\"_\"));\n}\n\nQString CutterCore::cmd(const char *str)\n{\n    CORE_LOCK();\n\n    RVA offset = core->offset;\n    char *res = rz_core_cmd_str(core, str);\n    QString o = fromOwnedCharPtr(res);\n\n    if (offset != core->offset) {\n        updateSeek();\n    }\n    return o;\n}\n\nQString CutterCore::getFunctionExecOut(const std::function<bool(RzCore *)> &fcn, const RVA addr)\n{\n    CORE_LOCK();\n\n    RVA offset = core->offset;\n    seekSilent(addr);\n    QString o = {};\n    rz_cons_push();\n    bool is_pipe = core->is_pipe;\n    core->is_pipe = true;\n\n    if (!fcn(core)) {\n        core->is_pipe = is_pipe;\n        rz_cons_pop();\n        goto clean_return;\n    }\n\n    core->is_pipe = is_pipe;\n    rz_cons_filter();\n    o = rz_cons_get_buffer();\n\n    rz_cons_pop();\n    rz_cons_echo(NULL);\n\nclean_return:\n    if (offset != core->offset) {\n        seekSilent(offset);\n    }\n    return o;\n}\n\nbool CutterCore::isRedirectableDebugee()\n{\n    if (!currentlyDebugging || currentlyAttachedToPID != -1) {\n        return false;\n    }\n\n    // We are only able to redirect locally debugged unix processes\n    RzCoreLocked core(Core());\n    RzList *descs = rz_id_storage_list(core->io->files);\n    RzListIter *it;\n    RzIODesc *desc;\n    CutterRzListForeach (descs, it, RzIODesc, desc) {\n        QString URI = QString(desc->uri);\n        if (URI.contains(\"ptrace\") || URI.contains(\"mach\")) {\n            return true;\n        }\n    }\n    return false;\n}\n\nbool CutterCore::isDebugTaskInProgress()\n{\n    if (!debugTask.isNull()) {\n        return true;\n    }\n\n    return false;\n}\n\nvoid CutterCore::setProfileDirectives(const QString &directives)\n{\n    QString file = getConfig(\"dbg.profile\");\n    if (file.isEmpty()) {\n        char *temp_path = rz_file_temp(\"rz-run\");\n        file = QString::fromUtf8(temp_path);\n        free(temp_path);\n        setConfig(\"dbg.profile\", file);\n    }\n\n    QByteArray fileNameBytes = file.toUtf8();\n    QByteArray directiveBytes = directives.toUtf8();\n\n    const char *pathPtr = fileNameBytes.constData();\n    rz_file_dump(pathPtr, (const ut8 *)directiveBytes.data(), (int)directiveBytes.size(), 0);\n    rz_file_dump(pathPtr, (const ut8 *)\"\\n\", 1, 1);\n}\n\nvoid CutterCore::setRegisterProfile(const QString &profile)\n{\n    CORE_LOCK();\n    rz_reg_set_profile_string(core->dbg->reg, profile.toUtf8().constData());\n    emit registersChanged();\n}\n\nQString CutterCore::convertGDBProfile(const QString &profilePath)\n{\n    return QString::fromUtf8(rz_reg_parse_gdb_profile(profilePath.toUtf8().constData()));\n}\n\nQString CutterCore::getRegisterProfile()\n{\n    CORE_LOCK();\n    RzReg *reg = core->dbg->reg;\n    if (reg && reg->reg_profile_str) {\n        return QString::fromUtf8(reg->reg_profile_str);\n    }\n    return QString();\n}\n\nbool CutterCore::asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task)\n{\n    if (!task.isNull()) {\n        return false;\n    }\n\n    CORE_LOCK();\n    RVA offset = core->offset;\n    task = QSharedPointer<RizinTask>(new RizinFunctionTask(std::move(fcn), true));\n    connect(task.data(), &RizinTask::finished, task.data(), [this, offset, task]() {\n        CORE_LOCK();\n\n        if (offset != core->offset) {\n            updateSeek();\n        }\n    });\n\n    return true;\n}\n\nvoid CutterCore::functionTask(std::function<void *(RzCore *)> fcn)\n{\n    auto task = std::unique_ptr<RizinTask>(new RizinFunctionTask(std::move(fcn), true));\n    task->startTask();\n    task->joinTask();\n}\n\nQString CutterCore::cmdRawAt(const char *cmd, RVA address)\n{\n    QString res;\n    RVA oldOffset = getOffset();\n    seekSilent(address);\n\n    res = cmdRaw(cmd);\n\n    seekSilent(oldOffset);\n    return res;\n}\n\nQString CutterCore::cmdRaw(const char *cmd)\n{\n    QString res;\n    CORE_LOCK();\n    return rz_core_cmd_str(core, cmd);\n}\n\nCutterJson CutterCore::cmdj(const char *str)\n{\n    char *res;\n    {\n        CORE_LOCK();\n        res = rz_core_cmd_str(core, str);\n    }\n\n    return parseJson(\"cmdj\", res, str);\n}\n\nQString CutterCore::cmdTask(const QString &str)\n{\n    RizinCmdTask task(str);\n    task.startTask();\n    task.joinTask();\n    return task.getResult();\n}\n\nCutterJson CutterCore::parseJson(const char *name, char *res, const char *cmd)\n{\n    if (RZ_STR_ISEMPTY(res)) {\n        return CutterJson();\n    }\n\n    RzJson *doc = rz_json_parse(res);\n\n    if (!doc) {\n        if (cmd) {\n            RZ_LOG_ERROR(\"%s: Failed to parse JSON for command \\\"%s\\\"\\n%s\\n\", name, cmd, res);\n        } else {\n            RZ_LOG_ERROR(\"%s: Failed to parse JSON %s\\n\", name, res);\n        }\n        RZ_LOG_ERROR(\"%s: %s\\n\", name, res);\n    }\n\n    return CutterJson(doc, QSharedPointer<CutterJsonOwner>::create(doc, res));\n}\n\nQStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType promptType)\n{\n    RzLineBuffer buf;\n    int c = snprintf(buf.data, sizeof(buf.data), \"%s\", cmd.toUtf8().constData());\n    if (c < 0) {\n        return {};\n    }\n    buf.index = buf.length = std::min((int)(sizeof(buf.data) - 1), c);\n\n    CORE_LOCK();\n    RzLineNSCompletionResult *compr = rz_core_autocomplete_rzshell(core, &buf, promptType);\n\n    QStringList r;\n    auto optslen = rz_pvector_len(&compr->options);\n    r.reserve(optslen);\n    for (size_t i = 0; i < optslen; i++) {\n        r.push_back(QString::fromUtf8(\n                reinterpret_cast<const char *>(rz_pvector_at(&compr->options, i))));\n    }\n    rz_line_ns_completion_result_free(compr);\n\n    return r;\n}\n\n/**\n * @brief CutterCore::loadFile\n * Load initial file.\n * @param path File path\n * @param baddr Base (RzBin) address\n * @param mapaddr Map address\n * @param perms\n * @param va\n * @param loadbin Load RzBin information\n * @param forceBinPlugin\n * @return\n */\nbool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int va, bool loadbin,\n                          const QString &forceBinPlugin)\n{\n    CORE_LOCK();\n    RzCoreFile *f;\n    rz_config_set_i(core->config, \"io.va\", va);\n\n    f = rz_core_file_open(core, path.toUtf8().constData(), perms, mapaddr);\n    if (!f) {\n        eprintf(\"rz_core_file_open failed\\n\");\n        return false;\n    }\n\n    if (!forceBinPlugin.isNull()) {\n        rz_bin_force_plugin(rz_core_get_bin(core), forceBinPlugin.toUtf8().constData());\n    }\n\n    if (loadbin && va) {\n        if (!rz_core_bin_load(core, path.toUtf8().constData(), baddr)) {\n            eprintf(\"CANNOT GET RBIN INFO\\n\");\n        }\n\n#if HAVE_MULTIPLE_RBIN_FILES_INSIDE_SELECT_WHICH_ONE\n        if (!rz_core_file_open(core, path.toUtf8(), RZ_IO_READ | (rw ? RZ_IO_WRITE : 0, mapaddr))) {\n            eprintf(\"Cannot open file\\n\");\n        } else {\n            // load RzBin information\n            // XXX only for sub-bins\n            rz_core_bin_load(core, path.toUtf8(), baddr);\n            rz_bin_select_idx(core->bin, NULL, idx);\n        }\n#endif\n    }\n\n    auto iod = core->io ? core->io->desc : NULL;\n    auto debug =\n            core->file && iod && (core->file->fd == iod->fd) && iod->plugin && iod->plugin->isdbg;\n\n    if (!debug && rz_flag_get(core->flags, \"entry0\")) {\n        ut64 addr = rz_num_math(core->num, \"entry0\");\n        rz_core_seek_and_save(core, addr, true);\n    }\n\n    if (perms & RZ_PERM_W) {\n        RzPVector *maps = rz_io_maps(core->io);\n        for (auto map : CutterPVector<RzIOMap>(maps)) {\n            map->perm |= RZ_PERM_W;\n        }\n    }\n\n    rz_cons_flush();\n    fflush(stdout);\n    return true;\n}\n\nbool CutterCore::tryFile(QString path, bool rw)\n{\n    if (path.isEmpty()) {\n        // opening no file is always possible\n        return true;\n    }\n    CORE_LOCK();\n    // clear info from previous fails\n    rz_cons_break_clear();\n    RzCoreFile *cf;\n    int flags = RZ_PERM_R;\n    if (rw)\n        flags = RZ_PERM_RW;\n    cf = rz_core_file_open(core, path.toUtf8().constData(), flags, 0LL);\n    if (!cf) {\n        return false;\n    }\n\n    rz_core_file_close(cf);\n\n    return true;\n}\n\n/**\n * @brief Maps a file using Rizin API\n * @param path Path to file\n * @param mapaddr Map Address\n * @return bool\n */\nbool CutterCore::mapFile(QString path, RVA mapaddr)\n{\n    CORE_LOCK();\n    RVA addr = mapaddr != RVA_INVALID ? mapaddr : 0;\n    ut64 baddr = rz_bin_get_baddr(core->bin);\n    if (rz_core_file_open(core, path.toUtf8().constData(), RZ_PERM_RX, addr)) {\n        rz_core_bin_load(core, path.toUtf8().constData(), baddr);\n    } else {\n        return false;\n    }\n    return true;\n}\n\nvoid CutterCore::renameFunction(const RVA offset, const QString &newName)\n{\n    CORE_LOCK();\n    rz_core_analysis_function_rename(core, offset, newName.toStdString().c_str());\n    emit functionRenamed(offset, newName);\n}\n\nvoid CutterCore::delFunction(RVA addr)\n{\n    CORE_LOCK();\n    rz_core_analysis_undefine(core, addr);\n    emit functionsChanged();\n}\n\nvoid CutterCore::renameFlag(QString old_name, QString new_name)\n{\n    CORE_LOCK();\n    RzFlagItem *flag = rz_flag_get(core->flags, old_name.toStdString().c_str());\n    if (!flag)\n        return;\n    rz_flag_rename(core->flags, flag, new_name.toStdString().c_str());\n    emit flagsChanged();\n}\n\nvoid CutterCore::renameFunctionVariable(QString newName, QString oldName, RVA functionAddress)\n{\n    CORE_LOCK();\n    RzAnalysisFunction *function = rz_analysis_get_function_at(core->analysis, functionAddress);\n    RzAnalysisVar *variable =\n            rz_analysis_function_get_var_byname(function, oldName.toUtf8().constData());\n    if (variable) {\n        rz_analysis_var_rename(variable, newName.toUtf8().constData(), true);\n    }\n    emit refreshCodeViews();\n}\n\nvoid CutterCore::delFlag(RVA addr)\n{\n    CORE_LOCK();\n    rz_flag_unset_off(core->flags, addr);\n    emit flagsChanged();\n}\n\nvoid CutterCore::delFlag(const QString &name)\n{\n    CORE_LOCK();\n    rz_flag_unset_name(core->flags, name.toStdString().c_str());\n    emit flagsChanged();\n}\n\nCutterRzIter<RzAnalysisBytes> CutterCore::getRzAnalysisBytesSingle(RVA addr)\n{\n    CORE_LOCK();\n    ut8 buf[128];\n    rz_io_read_at_mapped(core->io, addr, buf, sizeof(buf));\n\n    // Warning! only safe to use with stack buffer, due to instruction count being 1\n    auto result =\n            CutterRzIter<RzAnalysisBytes>(rz_core_analysis_bytes(core, addr, buf, sizeof(buf), 1));\n    return result;\n}\n\nQString CutterCore::getInstructionBytes(RVA addr)\n{\n    auto ab = getRzAnalysisBytesSingle(addr);\n    return ab ? ab->bytes : \"\";\n}\n\nQString CutterCore::getInstructionOpcode(RVA addr)\n{\n    auto ab = getRzAnalysisBytesSingle(addr);\n    return ab ? ab->opcode : \"\";\n}\n\nvoid CutterCore::editInstruction(RVA addr, const QString &inst, bool fillWithNops)\n{\n    CORE_LOCK();\n    if (fillWithNops) {\n        rz_core_write_assembly_fill(core, addr, inst.trimmed().toStdString().c_str());\n    } else {\n        rz_core_write_assembly(core, addr, inst.trimmed().toStdString().c_str());\n    }\n    emit instructionChanged(addr);\n}\n\nvoid CutterCore::nopInstruction(RVA addr)\n{\n    CORE_LOCK();\n    {\n        auto seek = seekTemp(addr);\n        rz_core_hack(core, \"nop\");\n    }\n    emit instructionChanged(addr);\n}\n\nvoid CutterCore::jmpReverse(RVA addr)\n{\n    CORE_LOCK();\n    {\n        auto seek = seekTemp(addr);\n        rz_core_hack(core, \"recj\");\n    }\n    emit instructionChanged(addr);\n}\n\nvoid CutterCore::editBytes(RVA addr, const QString &bytes)\n{\n    CORE_LOCK();\n    rz_core_write_hexpair(core, addr, bytes.toUtf8().constData());\n    emit instructionChanged(addr);\n}\n\nvoid CutterCore::editBytesEndian(RVA addr, const QString &bytes)\n{\n    CORE_LOCK();\n    ut64 value = rz_num_math(core->num, bytes.toUtf8().constData());\n    if (core->num->nc.errors) {\n        return;\n    }\n    rz_core_write_value_at(core, addr, value, 0);\n    emit stackChanged();\n}\n\nvoid CutterCore::setToCode(RVA addr)\n{\n    CORE_LOCK();\n    rz_meta_del(core->analysis, RZ_META_TYPE_STRING, core->offset, 1);\n    rz_meta_del(core->analysis, RZ_META_TYPE_DATA, core->offset, 1);\n    emit instructionChanged(addr);\n}\n\nvoid CutterCore::setAsString(RVA addr, int size, StringTypeFormats type)\n{\n    if (RVA_INVALID == addr) {\n        return;\n    }\n\n    RzStrEnc encoding;\n    switch (type) {\n    case StringTypeFormats::None: {\n        encoding = RZ_STRING_ENC_GUESS;\n        break;\n    }\n    case StringTypeFormats::ASCII_LATIN1: {\n        encoding = RZ_STRING_ENC_8BIT;\n        break;\n    }\n    case StringTypeFormats::UTF8: {\n        encoding = RZ_STRING_ENC_UTF8;\n        break;\n    }\n    default:\n        return;\n    }\n\n    CORE_LOCK();\n    seekAndShow(addr);\n    rz_core_meta_string_add(core, addr, size, encoding, nullptr);\n    emit instructionChanged(addr);\n}\n\nvoid CutterCore::removeString(RVA addr)\n{\n    CORE_LOCK();\n    rz_meta_del(core->analysis, RZ_META_TYPE_STRING, addr, 1);\n    emit instructionChanged(addr);\n}\n\nQString CutterCore::getString(RVA addr)\n{\n    CORE_LOCK();\n    return getString(addr, core->blocksize,\n                     rz_str_guess_encoding_from_buffer(core->block, core->blocksize));\n}\n\nQString CutterCore::getString(RVA addr, uint64_t len, RzStrEnc encoding, bool escape_nl)\n{\n    CORE_LOCK();\n    RzStrStringifyOpt opt = {};\n    opt.buffer = core->block;\n    opt.length = len;\n    opt.encoding = encoding;\n    opt.escape_nl = escape_nl;\n    auto seek = seekTemp(addr);\n    return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, NULL));\n}\n\nQString CutterCore::getMetaString(RVA addr)\n{\n    CORE_LOCK();\n    return rz_meta_get_string(core->analysis, RZ_META_TYPE_STRING, addr);\n}\n\nvoid CutterCore::setToData(RVA addr, int size, int repeat)\n{\n    if (size <= 0 || repeat <= 0) {\n        return;\n    }\n\n    CORE_LOCK();\n    RVA address = addr;\n    for (int i = 0; i < repeat; ++i, address += size) {\n        rz_meta_set(core->analysis, RZ_META_TYPE_DATA, address, size, nullptr);\n    }\n    emit instructionChanged(addr);\n}\n\nint CutterCore::sizeofDataMeta(RVA addr)\n{\n    ut64 size = 0;\n    CORE_LOCK();\n    rz_meta_get_at(core->analysis, addr, RZ_META_TYPE_DATA, &size);\n    return (int)size;\n}\n\nvoid CutterCore::setComment(RVA addr, const QString &cmt)\n{\n    CORE_LOCK();\n    rz_meta_set_string(core->analysis, RZ_META_TYPE_COMMENT, addr, cmt.toStdString().c_str());\n    emit commentsChanged(addr);\n}\n\nvoid CutterCore::delComment(RVA addr)\n{\n    CORE_LOCK();\n    rz_meta_del(core->analysis, RZ_META_TYPE_COMMENT, addr, 1);\n    emit commentsChanged(addr);\n}\n\n/**\n * @brief Gets the comment present at a specific address\n * @param addr The address to be checked\n * @return String containing comment\n */\nQString CutterCore::getCommentAt(RVA addr)\n{\n    CORE_LOCK();\n    return rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, addr);\n}\n\nvoid CutterCore::setImmediateBase(const QString &rzBaseName, RVA offset)\n{\n    if (offset == RVA_INVALID) {\n        offset = getOffset();\n    }\n    CORE_LOCK();\n    int base = (int)rz_num_base_of_string(core->num, rzBaseName.toUtf8().constData());\n    rz_analysis_hint_set_immbase(core->analysis, offset, base);\n    emit instructionChanged(offset);\n}\n\nvoid CutterCore::setCurrentBits(int bits, RVA offset)\n{\n    if (offset == RVA_INVALID) {\n        offset = getOffset();\n    }\n\n    CORE_LOCK();\n    rz_analysis_hint_set_bits(core->analysis, offset, bits);\n    emit instructionChanged(offset);\n}\n\nvoid CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset)\n{\n    if (offset == RVA_INVALID) {\n        offset = getOffset();\n    }\n\n    {\n        CORE_LOCK();\n        auto seek = seekTemp(offset);\n        rz_core_analysis_hint_set_offset(core, structureOffset.toUtf8().constData());\n    }\n    emit instructionChanged(offset);\n}\n\nvoid CutterCore::seekSilent(ut64 offset)\n{\n    CORE_LOCK();\n    if (offset == RVA_INVALID) {\n        return;\n    }\n    rz_core_seek(core, offset, true);\n}\n\nvoid CutterCore::seek(ut64 offset)\n{\n    // Slower than using the API, but the API is not complete\n    // which means we either have to duplicate code from rizin\n    // here, or refactor rizin API.\n    CORE_LOCK();\n    if (offset == RVA_INVALID) {\n        return;\n    }\n\n    RVA o_offset = core->offset;\n    rz_core_seek_and_save(core, offset, true);\n    if (o_offset != core->offset) {\n        updateSeek();\n    }\n}\n\nvoid CutterCore::showMemoryWidget()\n{\n    emit showMemoryWidgetRequested();\n}\n\nvoid CutterCore::seekAndShow(ut64 offset)\n{\n    seek(offset);\n    showMemoryWidget();\n}\n\nvoid CutterCore::seekAndShow(QString offset)\n{\n    seek(offset);\n    showMemoryWidget();\n}\n\nvoid CutterCore::seek(QString thing)\n{\n    CORE_LOCK();\n    ut64 addr = rz_num_math(core->num, thing.toUtf8().constData());\n    if (core->num->nc.errors) {\n        return;\n    }\n    rz_core_seek_and_save(core, addr, true);\n    updateSeek();\n}\n\nvoid CutterCore::seekPrev()\n{\n    CORE_LOCK();\n    rz_core_seek_undo(core);\n    updateSeek(SeekHistoryType::Undo);\n}\n\nvoid CutterCore::seekNext()\n{\n    CORE_LOCK();\n    rz_core_seek_redo(core);\n    updateSeek(SeekHistoryType::Redo);\n}\n\nvoid CutterCore::updateSeek(SeekHistoryType type)\n{\n    emit seekChanged(getOffset(), type);\n}\n\nRVA CutterCore::prevOpAddr(RVA startAddr, int count)\n{\n    CORE_LOCK();\n    return rz_core_prevop_addr_force(core, startAddr, count);\n}\n\nRVA CutterCore::nextOpAddr(RVA startAddr, int count)\n{\n    CORE_LOCK();\n    auto seek = seekTemp(startAddr);\n    auto consumed =\n            rz_core_analysis_ops_size(core, core->offset, core->block, (int)core->blocksize, count);\n\n    RVA addr = startAddr + consumed;\n    return addr;\n}\n\nRVA CutterCore::getOffset()\n{\n    return core_->offset;\n}\n\nvoid CutterCore::applySignature(const QString &filepath)\n{\n    CORE_LOCK();\n    int old_cnt, new_cnt;\n    const char *arch = rz_config_get(core->config, \"asm.arch\");\n    ut8 expected_arch = rz_core_flirt_arch_from_name(arch);\n    if (expected_arch == RZ_FLIRT_SIG_ARCH_ANY && filepath.endsWith(\".sig\", Qt::CaseInsensitive)) {\n        QMessageBox::warning(nullptr, tr(\"Signatures\"),\n                             tr(\"Cannot apply signature file because the requested arch is not \"\n                                \"supported by .sig \"\n                                \"files\"));\n        return;\n    }\n    old_cnt = rz_flag_count(core->flags, \"flirt\");\n    if (rz_sign_flirt_apply(core->analysis, filepath.toStdString().c_str(), expected_arch)) {\n        new_cnt = rz_flag_count(core->flags, \"flirt\");\n        QMessageBox::information(nullptr, tr(\"Signatures\"),\n                                 tr(\"Found %1 matching signatures!\").arg(new_cnt - old_cnt));\n        return;\n    }\n    QMessageBox::warning(\n            nullptr, tr(\"Signatures\"),\n            tr(\"Failed to apply signature file!\\nPlease check the console for more details.\"));\n}\n\nvoid CutterCore::createSignature(const QString &filepath)\n{\n    CORE_LOCK();\n    ut32 n_modules = 0;\n    if (!rz_core_flirt_create_file(core, filepath.toStdString().c_str(), &n_modules)) {\n        QMessageBox::warning(\n                nullptr, tr(\"Signatures\"),\n                tr(\"Cannot create signature file (check the console for more details).\"));\n        return;\n    }\n    QMessageBox::information(nullptr, tr(\"Signatures\"),\n                             tr(\"Written %1 signatures to %2.\").arg(n_modules).arg(filepath));\n}\n\nut64 CutterCore::math(const QString &expr)\n{\n    CORE_LOCK();\n    return rz_num_math(core ? core->num : NULL, expr.toUtf8().constData());\n}\n\nut64 CutterCore::num(const QString &expr)\n{\n    CORE_LOCK();\n    return rz_num_get(core ? core->num : NULL, expr.toUtf8().constData());\n}\n\nQString CutterCore::itoa(ut64 num, int rdx)\n{\n    return QString::number(num, rdx);\n}\n\nvoid CutterCore::setConfig(const char *k, const char *v)\n{\n    CORE_LOCK();\n    rz_config_set(core->config, k, v);\n}\n\nvoid CutterCore::setConfig(const QString &k, const char *v)\n{\n    CORE_LOCK();\n    rz_config_set(core->config, k.toUtf8().constData(), v);\n}\n\nvoid CutterCore::setConfig(const char *k, const QString &v)\n{\n    CORE_LOCK();\n    rz_config_set(core->config, k, v.toUtf8().constData());\n}\n\nvoid CutterCore::setConfig(const char *k, int v)\n{\n    CORE_LOCK();\n    rz_config_set_i(core->config, k, static_cast<ut64>(v));\n}\n\nvoid CutterCore::setConfig(const char *k, bool v)\n{\n    CORE_LOCK();\n    rz_config_set_i(core->config, k, v ? 1 : 0);\n}\n\nint CutterCore::getConfigi(const char *k)\n{\n    CORE_LOCK();\n    return static_cast<int>(rz_config_get_i(core->config, k));\n}\n\nut64 CutterCore::getConfigut64(const char *k)\n{\n    CORE_LOCK();\n    return rz_config_get_i(core->config, k);\n}\n\nbool CutterCore::getConfigb(const char *k)\n{\n    CORE_LOCK();\n    return rz_config_get_i(core->config, k) != 0;\n}\n\nQString CutterCore::getConfigDescription(const char *k)\n{\n    CORE_LOCK();\n    RzConfigNode *node = rz_config_node_get(core->config, k);\n    return node ? QString(node->desc) : QString(\"Unrecognized configuration key\");\n}\n\nvoid CutterCore::triggerRefreshAll()\n{\n    emit refreshAll();\n}\n\nvoid CutterCore::triggerAsmOptionsChanged()\n{\n    emit asmOptionsChanged();\n}\n\nvoid CutterCore::triggerGraphOptionsChanged()\n{\n    emit graphOptionsChanged();\n}\n\nvoid CutterCore::message(const QString &msg, bool debug)\n{\n    if (msg.isEmpty())\n        return;\n    if (debug) {\n        qDebug() << msg;\n        emit newDebugMessage(msg);\n        return;\n    }\n    emit newMessage(msg);\n}\n\nQString CutterCore::getConfig(const char *k)\n{\n    CORE_LOCK();\n    return { rz_config_get(core->config, k) };\n}\n\nQStringList CutterCore::getConfigOptions(const char *k)\n{\n    CORE_LOCK();\n    RzConfigNode *node = rz_config_node_get(core->config, k);\n    if (!(node && node->options)) {\n        return {};\n    }\n    QStringList list;\n    for (const auto &s : CutterRzList<char>(node->options)) {\n        list << s;\n    }\n    return list;\n}\n\nvoid CutterCore::setConfig(const char *k, const QVariant &v)\n{\n    switch (v.type()) {\n    case QVariant::Type::Bool:\n        setConfig(k, v.toBool());\n        break;\n    case QVariant::Type::Int:\n        setConfig(k, v.toInt());\n        break;\n    default:\n        setConfig(k, v.toString());\n        break;\n    }\n}\n\nvoid CutterCore::setCPU(QString arch, QString cpu, int bits)\n{\n    if (arch != nullptr) {\n        setConfig(\"asm.arch\", arch);\n    }\n    if (cpu != nullptr) {\n        setConfig(\"asm.cpu\", cpu);\n    }\n    setConfig(\"asm.bits\", bits);\n}\n\nvoid CutterCore::setEndianness(bool big)\n{\n    setConfig(\"cfg.bigendian\", big);\n}\n\nQByteArray CutterCore::assemble(const QString &code)\n{\n    CORE_LOCK();\n    RzAsmCode *ac = rz_asm_massemble(core->rasm, code.toUtf8().constData());\n    QByteArray res;\n    if (ac && ac->bytes) {\n        res = QByteArray(reinterpret_cast<const char *>(ac->bytes), ac->len);\n    }\n    rz_asm_code_free(ac);\n    return res;\n}\n\nQString CutterCore::disassemble(const QByteArray &data)\n{\n    CORE_LOCK();\n    RzAsmCode *ac = rz_asm_mdisassemble(core->rasm, reinterpret_cast<const ut8 *>(data.constData()),\n                                        data.length());\n    QString code;\n    if (ac && ac->assembly) {\n        code = QString::fromUtf8(ac->assembly);\n    }\n    rz_asm_code_free(ac);\n    return code;\n}\n\nQString CutterCore::disassembleSingleInstruction(RVA addr)\n{\n    auto ab = getRzAnalysisBytesSingle(addr);\n    return QString(ab->disasm).simplified();\n}\n\nRzAnalysisFunction *CutterCore::functionIn(ut64 addr)\n{\n    CORE_LOCK();\n    RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr);\n    if (fcn) {\n        return fcn;\n    }\n    RzList *fcns = rz_analysis_get_functions_in(core->analysis, addr);\n    fcn = !rz_list_empty(fcns) ? reinterpret_cast<RzAnalysisFunction *>(rz_list_first_val(fcns))\n                               : nullptr;\n    rz_list_free(fcns);\n    return fcn;\n}\n\nRzAnalysisFunction *CutterCore::functionAt(ut64 addr)\n{\n    CORE_LOCK();\n    return rz_analysis_get_function_at(core->analysis, addr);\n}\n\n/**\n * @brief finds the start address of a function in a given address\n * @param addr - an address which belongs to a function\n * @returns if function exists, return its start address. Otherwise return RVA_INVALID\n */\nRVA CutterCore::getFunctionStart(RVA addr)\n{\n    CORE_LOCK();\n    RzAnalysisFunction *fcn = Core()->functionIn(addr);\n    return fcn ? fcn->addr : RVA_INVALID;\n}\n\n/**\n * @brief finds the end address of a function in a given address\n * @param addr - an address which belongs to a function\n * @returns if function exists, return its end address. Otherwise return RVA_INVALID\n */\nRVA CutterCore::getFunctionEnd(RVA addr)\n{\n    CORE_LOCK();\n    RzAnalysisFunction *fcn = Core()->functionIn(addr);\n    return fcn ? fcn->addr : RVA_INVALID;\n}\n\n/**\n * @brief finds the last instruction of a function in a given address\n * @param addr - an address which belongs to a function\n * @returns if function exists, return the address of its last instruction. Otherwise return\n * RVA_INVALID\n */\nRVA CutterCore::getLastFunctionInstruction(RVA addr)\n{\n    CORE_LOCK();\n    RzAnalysisFunction *fcn = Core()->functionIn(addr);\n    if (!fcn) {\n        return RVA_INVALID;\n    }\n    RzAnalysisBlock *lastBB = (RzAnalysisBlock *)rz_pvector_tail(fcn->bbs);\n    return lastBB ? rz_analysis_block_get_op_addr(lastBB, lastBB->ninstr - 1) : RVA_INVALID;\n}\n\nQString CutterCore::flagAt(RVA addr, bool getClosestFlag)\n{\n    CORE_LOCK();\n    // rz_flag_get_at and rz_flag_get_i can return different\n    // flags for addresses containing multiple flags, so we must use rz_flag_get_i here\n    // instead of setting rz_flag_get_at's \"closest\" argument to false\n    RzFlagItem *f = getClosestFlag ? rz_flag_get_at(core->flags, addr, true)\n                                   : rz_flag_get_i(core->flags, addr);\n    if (!f) {\n        return {};\n    }\n    return core->flags->realnames && f->realname ? f->realname : f->name;\n}\n\nvoid CutterCore::createFunctionAt(RVA addr)\n{\n    createFunctionAt(addr, \"\");\n}\n\nvoid CutterCore::createFunctionAt(RVA addr, QString name)\n{\n    if (!name.isEmpty() && !name.isNull()) {\n        static const QRegularExpression regExp(\"[^a-zA-Z0-9_.]\");\n        name.remove(regExp);\n    }\n\n    CORE_LOCK();\n    bool analyze_recursively = rz_config_get_i(core->config, \"analysis.calls\");\n    rz_core_analysis_function_add(core, name.toStdString().c_str(), addr, analyze_recursively);\n    emit functionsChanged();\n}\n\nRVA CutterCore::getOffsetJump(RVA addr)\n{\n    auto ab = getRzAnalysisBytesSingle(addr);\n    return ab && ab->op ? ab->op->jump : RVA_INVALID;\n}\n\nQList<Decompiler *> CutterCore::getDecompilers()\n{\n    return decompilers;\n}\n\nDecompiler *CutterCore::getDecompilerById(const QString &id)\n{\n    for (Decompiler *dec : decompilers) {\n        if (dec->getId() == id) {\n            return dec;\n        }\n    }\n    return nullptr;\n}\n\nbool CutterCore::registerDecompiler(Decompiler *decompiler)\n{\n    if (getDecompilerById(decompiler->getId())) {\n        return false;\n    }\n    decompiler->setParent(this);\n    decompilers.push_back(decompiler);\n    return true;\n}\n\nCutterJson CutterCore::getSignatureInfo()\n{\n    CORE_LOCK();\n    RzBinFile *cur = rz_bin_cur(core->bin);\n    RzBinPlugin *plg = rz_bin_file_cur_plugin(cur);\n    if (!plg || !plg->signature) {\n        return {};\n    }\n    char *signature = plg->signature(cur, true);\n    if (!signature) {\n        return {};\n    }\n    return parseJson(\"signature\", signature, nullptr);\n}\n\nbool CutterCore::existsFileInfo()\n{\n    CORE_LOCK();\n    RzBinObject *bobj = rz_bin_cur_object(core->bin);\n    if (!bobj) {\n        return false;\n    }\n    const RzBinInfo *info = rz_bin_object_get_info(bobj);\n    if (!(info && info->rclass)) {\n        return false;\n    }\n    return strncmp(\"pe\", info->rclass, 2) == 0 || strncmp(\"elf\", info->rclass, 3) == 0;\n}\n\n// Utility function to check if a telescoped item exists and add it with prefixes to the desc\nstatic inline const QString appendVar(QString &dst, const QString val, const QString prepend_val,\n                                      const QString append_val)\n{\n    if (!val.isEmpty()) {\n        dst += prepend_val + val + append_val;\n    }\n    return val;\n}\n\nRefDescription CutterCore::formatRefDesc(const QSharedPointer<AddrRefs> &refItem)\n{\n    RefDescription desc;\n\n    if (refItem->addr == RVA_INVALID) {\n        return desc;\n    }\n\n    QString str = refItem->string;\n    if (!str.isEmpty()) {\n        desc.ref = str;\n        desc.refColor = ConfigColor(\"comment\");\n    } else {\n        QSharedPointer<const AddrRefs> cursor(refItem);\n        QString type, string;\n        while (true) {\n            desc.ref += \" ->\";\n            appendVar(desc.ref, cursor->reg, \" @\", \"\");\n            appendVar(desc.ref, cursor->mapname, \" (\", \")\");\n            appendVar(desc.ref, cursor->section, \" (\", \")\");\n            appendVar(desc.ref, cursor->fcn, \" \", \"\");\n            type = appendVar(desc.ref, cursor->type, \" \", \"\");\n            appendVar(desc.ref, cursor->perms, \" \", \"\");\n            appendVar(desc.ref, cursor->asm_op, \" \\\"\", \"\\\"\");\n            string = appendVar(desc.ref, cursor->string, \" \", \"\");\n            if (!string.isNull()) {\n                // There is no point in adding ascii and addr info after a string\n                break;\n            }\n            if (cursor->has_value) {\n                appendVar(desc.ref, RzAddressString(cursor->value), \" \", \"\");\n            }\n            if (!cursor->ref) {\n                break;\n            }\n            cursor = cursor->ref;\n        }\n\n        // Set the ref's color according to the last item type\n        if (type == \"ascii\" || !string.isEmpty()) {\n            desc.refColor = ConfigColor(\"comment\");\n        } else if (type == \"program\") {\n            desc.refColor = ConfigColor(\"fname\");\n        } else if (type == \"library\") {\n            desc.refColor = ConfigColor(\"floc\");\n        } else if (type == \"stack\") {\n            desc.refColor = ConfigColor(\"offset\");\n        }\n    }\n\n    return desc;\n}\n\nRzReg *CutterCore::getReg()\n{\n    CORE_LOCK();\n    if (currentlyDebugging && currentlyEmulating) {\n        return core->analysis->reg;\n    } else if (currentlyDebugging) {\n        return core->dbg->reg;\n    }\n    return core->analysis->reg;\n}\n\nQList<RegisterRef> CutterCore::getRegisterRefs(int depth)\n{\n    QList<RegisterRef> ret;\n    if (!currentlyDebugging) {\n        return ret;\n    }\n\n    CORE_LOCK();\n    RzList *ritems = rz_core_reg_filter_items_sync(core, getReg(), reg_sync, nullptr);\n    if (!ritems) {\n        return ret;\n    }\n    RzListIter *it;\n    RzRegItem *ri;\n    CutterRzListForeach (ritems, it, RzRegItem, ri) {\n        RegisterRef reg;\n        reg.value = rz_reg_get_value(getReg(), ri);\n        reg.ref = getAddrRefs(reg.value, depth);\n        reg.name = ri->name;\n        ret.append(reg);\n    }\n    rz_list_free(ritems);\n    return ret;\n}\n\nQList<AddrRefs> CutterCore::getStack(int size, int depth)\n{\n    QList<AddrRefs> stack;\n    if (!currentlyDebugging) {\n        return stack;\n    }\n\n    CORE_LOCK();\n    RVA addr = rz_core_reg_getv_by_role_or_name(core, \"SP\");\n    if (addr == RVA_INVALID) {\n        return stack;\n    }\n\n    int base = core->analysis->bits;\n    for (int i = 0; i < size; i += base / 8) {\n        if ((base == 32 && addr + i >= UT32_MAX) || (base == 16 && addr + i >= UT16_MAX)) {\n            break;\n        }\n\n        stack.append(getAddrRefs(addr + i, depth));\n    }\n\n    return stack;\n}\n\nAddrRefs CutterCore::getAddrRefs(RVA addr, int depth)\n{\n    AddrRefs refs;\n    if (depth < 1 || addr == UT64_MAX) {\n        refs.addr = RVA_INVALID;\n        return refs;\n    }\n\n    CORE_LOCK();\n    int bits = core->rasm->bits;\n    QByteArray buf = QByteArray();\n    ut64 type = rz_core_analysis_address(core, addr);\n\n    refs.addr = addr;\n\n    // Search for the section the addr is in, avoid duplication for heap/stack with type\n    if (!(type & RZ_ANALYSIS_ADDR_TYPE_HEAP || type & RZ_ANALYSIS_ADDR_TYPE_STACK)) {\n        // Attempt to find the address within a map\n        RzDebugMap *map = rz_debug_map_get(core->dbg, addr);\n        if (map && map->name && map->name[0]) {\n            refs.mapname = map->name;\n        }\n\n        RzBinSection *sect = rz_bin_get_section_at(rz_bin_cur_object(core->bin), addr, true);\n        if (sect && sect->name[0]) {\n            refs.section = sect->name;\n        }\n    }\n\n    // Check if the address points to a register\n    RzFlagItem *fi = rz_flag_get_i(core->flags, addr);\n    if (fi) {\n        RzRegItem *r = rz_reg_get(getReg(), fi->name, -1);\n        if (r) {\n            refs.reg = r->name;\n        }\n    }\n\n    // Attempt to find the address within a function\n    RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);\n    if (fcn) {\n        refs.fcn = fcn->name;\n    }\n\n    // Update type and permission information\n    if (type != 0) {\n        if (type & RZ_ANALYSIS_ADDR_TYPE_HEAP) {\n            refs.type = \"heap\";\n        } else if (type & RZ_ANALYSIS_ADDR_TYPE_STACK) {\n            refs.type = \"stack\";\n        } else if (type & RZ_ANALYSIS_ADDR_TYPE_PROGRAM) {\n            refs.type = \"program\";\n        } else if (type & RZ_ANALYSIS_ADDR_TYPE_LIBRARY) {\n            refs.type = \"library\";\n        } else if (type & RZ_ANALYSIS_ADDR_TYPE_ASCII) {\n            refs.type = \"ascii\";\n        } else if (type & RZ_ANALYSIS_ADDR_TYPE_SEQUENCE) {\n            refs.type = \"sequence\";\n        }\n\n        QString perms = \"\";\n        if (type & RZ_ANALYSIS_ADDR_TYPE_READ) {\n            perms += \"r\";\n        }\n        if (type & RZ_ANALYSIS_ADDR_TYPE_WRITE) {\n            perms += \"w\";\n        }\n        if (type & RZ_ANALYSIS_ADDR_TYPE_EXEC) {\n            RzAsmOp op;\n            buf.resize(32);\n            perms += \"x\";\n            // Instruction disassembly\n            rz_io_read_at_mapped(core->io, addr, (unsigned char *)buf.data(), buf.size());\n            rz_asm_set_pc(core->rasm, addr);\n            rz_asm_disassemble(core->rasm, &op, (unsigned char *)buf.data(), buf.size());\n            refs.asm_op = rz_asm_op_get_asm(&op);\n        }\n\n        if (!perms.isEmpty()) {\n            refs.perms = perms;\n        }\n    }\n\n    // Try to telescope further if depth permits it\n    if ((type & RZ_ANALYSIS_ADDR_TYPE_READ)) {\n        buf.resize(64);\n        ut32 *n32 = (ut32 *)buf.data();\n        ut64 *n64 = (ut64 *)buf.data();\n        rz_io_read_at_mapped(core->io, addr, (unsigned char *)buf.data(), buf.size());\n        ut64 n = (bits == 64) ? *n64 : *n32;\n        // The value of the next address will serve as an indication that there's more to\n        // telescope if we have reached the depth limit\n        refs.value = n;\n        refs.has_value = true;\n        if (depth && n != addr && !(type & RZ_ANALYSIS_ADDR_TYPE_EXEC)) {\n            // Make sure we aren't telescoping the same address\n            AddrRefs ref = getAddrRefs(n, depth - 1);\n            if (!ref.type.isNull()) {\n                // If the dereference of the current pointer is an ascii character we\n                // might have a string in this address\n                if (ref.type.contains(\"ascii\")) {\n                    buf.resize(128);\n                    rz_io_read_at_mapped(core->io, addr, (unsigned char *)buf.data(), buf.size());\n                    QString strVal = QString(buf);\n                    // Indicate that the string is longer than the printed value\n                    if (strVal.size() == buf.size()) {\n                        strVal += \"...\";\n                    }\n                    refs.string = strVal;\n                }\n                refs.ref = QSharedPointer<AddrRefs>::create(ref);\n            }\n        }\n    }\n    return refs;\n}\n\nQVector<Chunk> CutterCore::getHeapChunks(RVA arena_addr)\n{\n    CORE_LOCK();\n    QVector<Chunk> chunks_vector;\n    ut64 m_arena;\n\n    if (!arena_addr) {\n        // if arena_addr is zero get base address of main arena\n        RzList *arenas = rz_heap_arenas_list(core);\n        if (arenas->length == 0) {\n            rz_list_free(arenas);\n            return chunks_vector;\n        }\n        m_arena = ((RzArenaListItem *)rz_list_first_val(arenas))->addr;\n        rz_list_free(arenas);\n    } else {\n        m_arena = arena_addr;\n    }\n\n    // Get chunks using api and store them in a chunks_vector\n    RzList *chunks = rz_heap_chunks_list(core, m_arena);\n    RzListIter *iter;\n    RzHeapChunkListItem *data;\n    CutterRzListForeach (chunks, iter, RzHeapChunkListItem, data) {\n        Chunk chunk;\n        chunk.offset = data->addr;\n        chunk.size = (int)data->size;\n        chunk.status = QString(data->status);\n        chunks_vector.append(chunk);\n    }\n\n    rz_list_free(chunks);\n    return chunks_vector;\n}\n\nint CutterCore::getArchBits()\n{\n    CORE_LOCK();\n    return core->dbg->bits;\n}\n\nQVector<Arena> CutterCore::getArenas()\n{\n    CORE_LOCK();\n    QVector<Arena> arena_vector;\n\n    // get arenas using API and store them in arena_vector\n    RzList *arenas = rz_heap_arenas_list(core);\n    RzListIter *iter;\n    RzArenaListItem *data;\n    CutterRzListForeach (arenas, iter, RzArenaListItem, data) {\n        Arena arena;\n        arena.offset = data->addr;\n        arena.type = QString(data->type);\n        arena.last_remainder = data->arena->last_remainder;\n        arena.top = data->arena->top;\n        arena.next = data->arena->next;\n        arena.next_free = data->arena->next_free;\n        arena.system_mem = data->arena->system_mem;\n        arena.max_system_mem = data->arena->max_system_mem;\n        arena_vector.append(arena);\n    }\n\n    rz_list_free(arenas);\n    return arena_vector;\n}\n\nRzHeapChunkSimple *CutterCore::getHeapChunk(ut64 addr)\n{\n    CORE_LOCK();\n    return rz_heap_chunk(core, addr);\n}\n\nQVector<RzHeapBin *> CutterCore::getHeapBins(ut64 arena_addr)\n{\n    CORE_LOCK();\n    QVector<RzHeapBin *> bins_vector;\n\n    MallocState *arena = rz_heap_get_arena(core, arena_addr);\n    if (!arena) {\n        return bins_vector;\n    }\n\n    // get small, large, unsorted bins\n    for (int i = 0; i <= RZ_GLIBC_NBINS - 2; i++) {\n        RzHeapBin *bin = rz_heap_bin_content(core, arena, i, arena_addr);\n        if (!bin) {\n            continue;\n        }\n        if (!rz_list_length(bin->chunks)) {\n            rz_heap_bin_free(bin);\n            continue;\n        }\n        bins_vector.append(bin);\n    }\n    // get fastbins\n    for (int i = 0; i < 10; i++) {\n        RzHeapBin *bin = rz_heap_fastbin_content(core, arena, i);\n        if (!bin) {\n            continue;\n        }\n        if (!rz_list_length(bin->chunks)) {\n            rz_heap_bin_free(bin);\n            continue;\n        }\n        bins_vector.append(bin);\n    }\n    // get tcache bins\n    RzList *tcache_bins = rz_heap_tcache_content(core, arena_addr);\n    RzListIter *iter;\n    RzHeapBin *bin;\n    CutterRzListForeach (tcache_bins, iter, RzHeapBin, bin) {\n        if (!bin) {\n            continue;\n        }\n        if (!rz_list_length(bin->chunks)) {\n            rz_heap_bin_free(bin);\n            continue;\n        }\n        bins_vector.append(bin);\n    }\n    return bins_vector;\n}\n\nbool CutterCore::writeHeapChunk(RzHeapChunkSimple *chunk_simple)\n{\n    CORE_LOCK();\n    return rz_heap_write_chunk(core, chunk_simple);\n}\n\nQList<VariableDescription> CutterCore::getVariables(RVA at)\n{\n    QList<VariableDescription> ret;\n    CORE_LOCK();\n    RzAnalysisFunction *fcn = functionIn(at);\n    if (!fcn) {\n        return ret;\n    }\n    for (auto var : CutterPVector<RzAnalysisVar>(&fcn->vars)) {\n        VariableDescription desc;\n        desc.storageType = var->storage.type;\n        if (!var->name || !var->type) {\n            continue;\n        }\n        desc.name = QString::fromUtf8(var->name);\n        char *tn = rz_type_as_string(core->analysis->typedb, var->type);\n        if (!tn) {\n            continue;\n        }\n        desc.type = QString::fromUtf8(tn);\n        rz_mem_free(tn);\n\n        if (char *v = rz_core_analysis_var_display(core, var, false)) {\n            desc.value = QString::fromUtf8(v).trimmed();\n            rz_mem_free(v);\n        }\n\n        ret.push_back(desc);\n    }\n    return ret;\n}\n\nQList<GlobalDescription> CutterCore::getAllGlobals()\n{\n    CORE_LOCK();\n    RzListIter *it;\n\n    QList<GlobalDescription> ret;\n\n    RzAnalysisVarGlobal *glob;\n    if (core && core->analysis && core->analysis->typedb) {\n        const RzList *globals = rz_analysis_var_global_get_all(core->analysis);\n        CutterRzListForeach (globals, it, RzAnalysisVarGlobal, glob) {\n            const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);\n            if (!gtype) {\n                continue;\n            }\n            GlobalDescription global;\n            global.addr = glob->addr;\n            global.name = QString(glob->name);\n            global.type = QString(gtype);\n            ret << global;\n        }\n    }\n\n    return ret;\n}\n\nQVector<RegisterRefValueDescription> CutterCore::getRegisterRefValues()\n{\n    QVector<RegisterRefValueDescription> result;\n    CORE_LOCK();\n    RzList *ritems = rz_core_reg_filter_items_sync(core, getReg(), reg_sync, nullptr);\n    if (!ritems) {\n        return result;\n    }\n    RzListIter *it;\n    RzRegItem *ri;\n    CutterRzListForeach (ritems, it, RzRegItem, ri) {\n        RegisterRefValueDescription desc;\n        desc.name = ri->name;\n        ut64 value = rz_reg_get_value(getReg(), ri);\n        desc.value = \"0x\" + QString::number(value, 16);\n        desc.ref = rz_core_analysis_hasrefs(core, value, RZ_OUTPUT_MODE_STANDARD);\n        result.push_back(desc);\n    }\n    rz_list_free(ritems);\n    return result;\n}\n\nQString CutterCore::getRegisterName(QString registerRole)\n{\n    if (!currentlyDebugging) {\n        return \"\";\n    }\n    CORE_LOCK();\n    return rz_reg_get_name_by_type(getReg(), registerRole.toUtf8().constData());\n}\n\nRVA CutterCore::getProgramCounterValue()\n{\n    if (currentlyDebugging) {\n        CORE_LOCK();\n        return rz_core_reg_getv_by_role_or_name(core, \"PC\");\n    }\n    return RVA_INVALID;\n}\n\nvoid CutterCore::setRegister(QString regName, QString regValue)\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n    CORE_LOCK();\n    ut64 val = rz_num_math(core->num, regValue.toUtf8().constData());\n    rz_core_reg_assign_sync(core, getReg(), reg_sync, regName.toUtf8().constData(), val);\n    emit registersChanged();\n    emit refreshCodeViews();\n}\n\nvoid CutterCore::setCurrentDebugThread(int tid)\n{\n    if (!asyncTask(\n                [=](RzCore *core) {\n                    rz_debug_select(core->dbg, core->dbg->pid, tid);\n                    return (void *)NULL;\n                },\n                debugTask)) {\n        return;\n    }\n\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        emit registersChanged();\n        emit refreshCodeViews();\n        emit stackChanged();\n        syncAndSeekProgramCounter();\n        emit switchedThread();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::setCurrentDebugProcess(int pid)\n{\n    if (!currentlyDebugging\n        || !asyncTask(\n                [=](RzCore *core) {\n                    rz_debug_select(core->dbg, pid, core->dbg->tid);\n                    core->dbg->main_pid = pid;\n                    return (void *)NULL;\n                },\n                debugTask)) {\n        return;\n    }\n\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        emit registersChanged();\n        emit refreshCodeViews();\n        emit stackChanged();\n        emit flagsChanged();\n        syncAndSeekProgramCounter();\n        emit switchedProcess();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::startDebug()\n{\n    if (!currentlyDebugging) {\n        offsetPriorDebugging = getOffset();\n    }\n    currentlyOpenFile = getConfig(\"file.path\");\n\n    if (!asyncTask(\n                [](RzCore *core) {\n                    rz_core_file_reopen_debug(core, \"\");\n                    return nullptr;\n                },\n                debugTask)) {\n        return;\n    }\n\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        delete debugTaskDialog;\n        debugTask.clear();\n\n        emit registersChanged();\n        if (!currentlyDebugging) {\n            setConfig(\"asm.flags\", false);\n            currentlyDebugging = true;\n            emit toggleDebugView();\n            emit refreshCodeViews();\n        }\n\n        emit codeRebased();\n        emit stackChanged();\n        emit debugTaskStateChanged();\n    });\n\n    debugTaskDialog = new RizinTaskDialog(debugTask);\n    debugTaskDialog->setBreakOnClose(true);\n    debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    debugTaskDialog->setDesc(tr(\"Starting native debug...\"));\n    debugTaskDialog->show();\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::startEmulation()\n{\n    if (!currentlyDebugging) {\n        offsetPriorDebugging = getOffset();\n    }\n\n    // clear registers, init esil state, stack, progcounter at current seek\n    asyncTask(\n            [&](RzCore *core) {\n                rz_core_analysis_esil_reinit(core);\n                rz_core_analysis_esil_init_mem(core, NULL, UT64_MAX, UT32_MAX);\n                rz_core_analysis_esil_init_regs(core);\n                return nullptr;\n            },\n            debugTask);\n\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        delete debugTaskDialog;\n        debugTask.clear();\n\n        if (!currentlyDebugging || !currentlyEmulating) {\n            // prevent register flags from appearing during debug/emul\n            setConfig(\"asm.flags\", false);\n            // allows to view self-modifying code changes or other binary changes\n            setConfig(\"io.cache\", true);\n            currentlyDebugging = true;\n            currentlyEmulating = true;\n            emit toggleDebugView();\n        }\n\n        emit registersChanged();\n        emit stackChanged();\n        emit codeRebased();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTaskDialog = new RizinTaskDialog(debugTask);\n    debugTaskDialog->setBreakOnClose(true);\n    debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    debugTaskDialog->setDesc(tr(\"Starting emulation...\"));\n    debugTaskDialog->show();\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::attachRemote(const QString &uri)\n{\n    if (!currentlyDebugging) {\n        offsetPriorDebugging = getOffset();\n    }\n\n    // connect to a debugger with the given plugin\n    if (!asyncTask(\n                [&](RzCore *core) {\n                    rz_config_set_b(core->config, \"cfg.debug\", true);\n                    rz_core_file_reopen_remote_debug(core, uri.toStdString().c_str(), 0);\n                    return nullptr;\n                },\n                debugTask)) {\n        return;\n    }\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this, uri]() {\n        delete debugTaskDialog;\n        debugTask.clear();\n        // Check if we actually connected\n        bool connected = false;\n        RzCoreLocked core(Core());\n        RzList *descs = rz_id_storage_list(core->io->files);\n        RzListIter *it;\n        RzIODesc *desc;\n        CutterRzListForeach (descs, it, RzIODesc, desc) {\n            QString fileUri = QString(desc->uri);\n            if (!fileUri.compare(uri)) {\n                connected = true;\n            }\n        }\n        seekAndShow(getProgramCounterValue());\n        if (!connected) {\n            emit attachedRemote(false);\n            emit debugTaskStateChanged();\n            return;\n        }\n\n        emit registersChanged();\n        if (!currentlyDebugging || !currentlyEmulating) {\n            // prevent register flags from appearing during debug/emul\n            setConfig(\"asm.flags\", false);\n            currentlyDebugging = true;\n            emit toggleDebugView();\n        }\n\n        currentlyRemoteDebugging = true;\n        emit codeRebased();\n        emit attachedRemote(true);\n        emit debugTaskStateChanged();\n    });\n\n    debugTaskDialog = new RizinTaskDialog(debugTask);\n    debugTaskDialog->setBreakOnClose(true);\n    debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    debugTaskDialog->setDesc(tr(\"Connecting to: \") + uri);\n    debugTaskDialog->show();\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::attachDebug(int pid)\n{\n    if (!currentlyDebugging) {\n        offsetPriorDebugging = getOffset();\n    }\n\n    if (!asyncTask(\n                [&](RzCore *core) {\n                    // cannot use setConfig because core is\n                    // already locked, which causes a deadlock\n                    rz_config_set_b(core->config, \"cfg.debug\", true);\n                    auto uri = rz_str_newf(\"dbg://%d\", pid);\n                    if (currentlyOpenFile.isEmpty()) {\n                        rz_core_file_open_load(core, uri, 0, RZ_PERM_R, false);\n                    } else {\n                        rz_core_file_reopen_remote_debug(core, uri, 0);\n                    }\n                    free(uri);\n                    return nullptr;\n                },\n                debugTask)) {\n        return;\n    }\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this, pid]() {\n        delete debugTaskDialog;\n        debugTask.clear();\n\n        syncAndSeekProgramCounter();\n        if (!currentlyDebugging || !currentlyEmulating) {\n            // prevent register flags from appearing during debug/emul\n            setConfig(\"asm.flags\", false);\n            currentlyDebugging = true;\n            currentlyOpenFile = getConfig(\"file.path\");\n            currentlyAttachedToPID = pid;\n            emit toggleDebugView();\n        }\n\n        emit codeRebased();\n        emit debugTaskStateChanged();\n    });\n\n    debugTaskDialog = new RizinTaskDialog(debugTask);\n    debugTaskDialog->setBreakOnClose(true);\n    debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    debugTaskDialog->setDesc(tr(\"Attaching to process (\") + QString::number(pid) + \")...\");\n    debugTaskDialog->show();\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::suspendDebug()\n{\n    debugTask->breakTask();\n    debugTask->joinTask();\n}\n\nvoid CutterCore::stopDebug()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (!debugTask.isNull()) {\n        suspendDebug();\n    }\n\n    currentlyDebugging = false;\n    currentlyTracing = false;\n    currentlyRemoteDebugging = false;\n    emit debugTaskStateChanged();\n\n    CORE_LOCK();\n    if (currentlyEmulating) {\n        rz_core_analysis_esil_init_mem_del(core, NULL, UT64_MAX, UT32_MAX);\n        rz_core_analysis_esil_deinit(core);\n        resetWriteCache();\n        rz_core_debug_clear_register_flags(core);\n        rz_core_analysis_esil_trace_stop(core);\n        currentlyEmulating = false;\n    } else {\n        // ensure we have opened a file.\n        if (core->io->desc) {\n            rz_core_debug_process_close(core);\n        }\n        currentlyAttachedToPID = -1;\n    }\n\n    syncAndSeekProgramCounter();\n    setConfig(\"asm.flags\", true);\n    setConfig(\"io.cache\", false);\n    emit codeRebased();\n    emit toggleDebugView();\n    offsetPriorDebugging = getOffset();\n    emit debugTaskStateChanged();\n}\n\nvoid CutterCore::syncAndSeekProgramCounter()\n{\n    seekAndShow(getProgramCounterValue());\n    emit registersChanged();\n}\n\nvoid CutterCore::continueDebug()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_esil_step(core, UT64_MAX, \"0\", NULL, false);\n                        rz_core_reg_update_flags(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_debug_continue(core->dbg);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::continueBackDebug()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_esil_continue_back(core);\n                        rz_core_reg_update_flags(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_debug_continue_back(core->dbg);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::continueUntilDebug(ut64 offset)\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [=](RzCore *core) {\n                        rz_core_esil_step(core, offset, NULL, NULL, false);\n                        rz_core_reg_update_flags(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [=](RzCore *core) {\n                        rz_core_debug_continue_until(core, offset);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n    debugTask->startTask();\n}\n\nvoid CutterCore::continueUntilCall()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_analysis_continue_until_call(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_debug_step_one(core, 0);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::continueUntilSyscall()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_analysis_continue_until_syscall(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_cons_break_push(\n                                [](void *x) { rz_debug_stop(reinterpret_cast<RzDebug *>(x)); },\n                                core->dbg);\n                        rz_reg_arena_swap(core->dbg->reg, true);\n                        rz_debug_continue_syscalls(core->dbg, NULL, 0);\n                        rz_cons_break_pop();\n                        rz_core_dbg_follow_seek_register(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::stepDebug()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_esil_step(core, UT64_MAX, NULL, NULL, false);\n                        rz_core_reg_update_flags(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_debug_step_one(core, 1);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::stepOverDebug()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [&](RzCore *core) {\n                        rz_core_analysis_esil_step_over(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        asyncTask(\n                [](RzCore *core) {\n                    rz_core_debug_step_over(core, 1);\n                    rz_core_dbg_follow_seek_register(core);\n                    return nullptr;\n                },\n                debugTask);\n    }\n\n    emit debugTaskStateChanged();\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::stepOutDebug()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    emit debugTaskStateChanged();\n    asyncTask(\n            [](RzCore *core) {\n                rz_core_debug_step_until_frame(core);\n                rz_core_dbg_follow_seek_register(core);\n                return nullptr;\n            },\n            debugTask);\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::stepBackDebug()\n{\n    if (!currentlyDebugging) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_esil_step_back(core);\n                        rz_core_reg_update_flags(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        asyncTask(\n                [](RzCore *core) {\n                    rz_core_debug_step_back(core, 1);\n                    rz_core_dbg_follow_seek_register(core);\n                    return nullptr;\n                },\n                debugTask);\n    }\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        debugTask.clear();\n        syncAndSeekProgramCounter();\n        emit refreshCodeViews();\n        emit debugTaskStateChanged();\n    });\n\n    debugTask->startTask();\n}\n\nQStringList CutterCore::getDebugPlugins()\n{\n    QStringList plugins;\n    CORE_LOCK();\n    CutterHtSP<RzDebugPlugin>(core->dbg->plugins)\n            .ForEach([&plugins](const char *k, const RzDebugPlugin *plugin) {\n                plugins << plugin->name;\n                return true;\n            });\n    return plugins;\n}\n\nQString CutterCore::getActiveDebugPlugin()\n{\n    return getConfig(\"dbg.backend\");\n}\n\nvoid CutterCore::setDebugPlugin(QString plugin)\n{\n    setConfig(\"dbg.backend\", plugin);\n}\n\nvoid CutterCore::startTraceSession()\n{\n    if (!currentlyDebugging || currentlyTracing) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_analysis_esil_trace_start(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        core->dbg->session = rz_debug_session_new();\n                        rz_debug_add_checkpoint(core->dbg);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        delete debugTaskDialog;\n        debugTask.clear();\n\n        currentlyTracing = true;\n        emit debugTaskStateChanged();\n    });\n\n    debugTaskDialog = new RizinTaskDialog(debugTask);\n    debugTaskDialog->setBreakOnClose(true);\n    debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    debugTaskDialog->setDesc(tr(\"Creating debug tracepoint...\"));\n    debugTaskDialog->show();\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::stopTraceSession()\n{\n    if (!currentlyDebugging || !currentlyTracing) {\n        return;\n    }\n\n    if (currentlyEmulating) {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_core_analysis_esil_trace_stop(core);\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    } else {\n        if (!asyncTask(\n                    [](RzCore *core) {\n                        rz_debug_session_free(core->dbg->session);\n                        core->dbg->session = NULL;\n                        return nullptr;\n                    },\n                    debugTask)) {\n            return;\n        }\n    }\n    emit debugTaskStateChanged();\n\n    connect(debugTask.data(), &RizinTask::finished, this, [this]() {\n        delete debugTaskDialog;\n        debugTask.clear();\n\n        currentlyTracing = false;\n        emit debugTaskStateChanged();\n    });\n\n    debugTaskDialog = new RizinTaskDialog(debugTask);\n    debugTaskDialog->setBreakOnClose(true);\n    debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    debugTaskDialog->setDesc(tr(\"Stopping debug session...\"));\n    debugTaskDialog->show();\n\n    debugTask->startTask();\n}\n\nvoid CutterCore::toggleBreakpoint(RVA addr)\n{\n    CORE_LOCK();\n    rz_core_debug_breakpoint_toggle(core, addr);\n    emit breakpointsChanged(addr);\n}\n\nvoid CutterCore::addBreakpoint(const BreakpointDescription &config)\n{\n    CORE_LOCK();\n    RzBreakpointItem *breakpoint = nullptr;\n    int watchpoint_prot = 0;\n    if (config.hw) {\n        watchpoint_prot = config.permission & ~(RZ_PERM_X);\n    }\n\n    auto address = config.addr;\n    char *module = nullptr;\n    QByteArray moduleNameData;\n    if (config.type == BreakpointDescription::Named) {\n        address = Core()->math(config.positionExpression);\n    } else if (config.type == BreakpointDescription::Module) {\n        address = 0;\n        moduleNameData = config.positionExpression.toUtf8();\n        module = moduleNameData.data();\n    }\n    breakpoint = rz_debug_bp_add(core->dbg, address, config.size, config.hw, (watchpoint_prot != 0),\n                                 watchpoint_prot, module, config.moduleDelta);\n    if (!breakpoint) {\n        QMessageBox::critical(nullptr, tr(\"Breakpoint error\"), tr(\"Failed to create breakpoint\"));\n        return;\n    }\n    if (config.type == BreakpointDescription::Named) {\n        updateOwnedCharPtr(breakpoint->expr, config.positionExpression);\n    }\n\n    if (config.hw) {\n        breakpoint->size = config.size;\n    }\n    if (config.type == BreakpointDescription::Named) {\n        updateOwnedCharPtr(breakpoint->name, config.positionExpression);\n    }\n\n    int index = std::find(core->dbg->bp->bps_idx,\n                          core->dbg->bp->bps_idx + core->dbg->bp->bps_idx_count, breakpoint)\n            - core->dbg->bp->bps_idx;\n\n    breakpoint->enabled = config.enabled;\n    if (config.trace) {\n        setBreakpointTrace(index, config.trace);\n    }\n    if (!config.condition.isEmpty()) {\n        updateOwnedCharPtr(breakpoint->cond, config.condition);\n    }\n    if (!config.command.isEmpty()) {\n        updateOwnedCharPtr(breakpoint->data, config.command);\n    }\n    emit breakpointsChanged(breakpoint->addr);\n}\n\nvoid CutterCore::updateBreakpoint(int index, const BreakpointDescription &config)\n{\n    CORE_LOCK();\n    if (auto bp = rz_bp_get_index(core->dbg->bp, index)) {\n        rz_bp_del(core->dbg->bp, bp->addr);\n    }\n    // Delete by index currently buggy,\n    // required for breakpoints with non address based position\n    // rz_bp_del_index(core->dbg->bp, index);\n    addBreakpoint(config);\n}\n\nvoid CutterCore::delBreakpoint(RVA addr)\n{\n    CORE_LOCK();\n    rz_bp_del(core->dbg->bp, addr);\n    emit breakpointsChanged(addr);\n}\n\nvoid CutterCore::delAllBreakpoints()\n{\n    CORE_LOCK();\n    rz_bp_del_all(core->dbg->bp);\n    emit refreshCodeViews();\n}\n\nvoid CutterCore::enableBreakpoint(RVA addr)\n{\n    CORE_LOCK();\n    rz_bp_enable(core->dbg->bp, addr, true, 1);\n    emit breakpointsChanged(addr);\n}\n\nvoid CutterCore::disableBreakpoint(RVA addr)\n{\n    CORE_LOCK();\n    rz_bp_enable(core->dbg->bp, addr, false, 1);\n    emit breakpointsChanged(addr);\n}\n\nvoid CutterCore::setBreakpointTrace(int index, bool enabled)\n{\n    CORE_LOCK();\n    RzBreakpointItem *bpi = rz_bp_get_index(core->dbg->bp, index);\n    bpi->trace = enabled;\n}\n\nstatic BreakpointDescription breakpointDescriptionFromRizin(int index, rz_bp_item_t *bpi)\n{\n    BreakpointDescription bp;\n    bp.addr = bpi->addr;\n    bp.index = index;\n    bp.size = bpi->size;\n    if (bpi->expr) {\n        bp.positionExpression = bpi->expr;\n        bp.type = BreakpointDescription::Named;\n    }\n    bp.name = bpi->name;\n    bp.permission = bpi->perm;\n    bp.command = bpi->data;\n    bp.condition = bpi->cond;\n    bp.hw = bpi->hw;\n    bp.trace = bpi->trace;\n    bp.enabled = bpi->enabled;\n    return bp;\n}\n\nint CutterCore::breakpointIndexAt(RVA addr)\n{\n    CORE_LOCK();\n    return rz_bp_get_index_at(core->dbg->bp, addr);\n}\n\nBreakpointDescription CutterCore::getBreakpointAt(RVA addr)\n{\n    CORE_LOCK();\n    int index = rz_bp_get_index_at(core->dbg->bp, addr);\n    auto bp = rz_bp_get_index(core->dbg->bp, index);\n    if (bp) {\n        return breakpointDescriptionFromRizin(index, bp);\n    }\n    return BreakpointDescription();\n}\n\nQList<BreakpointDescription> CutterCore::getBreakpoints()\n{\n    CORE_LOCK();\n    QList<BreakpointDescription> ret;\n    // TODO: use higher level API, don't touch rizin bps_idx directly\n    for (int i = 0; i < core->dbg->bp->bps_idx_count; i++) {\n        if (auto bpi = core->dbg->bp->bps_idx[i]) {\n            ret.push_back(breakpointDescriptionFromRizin(i, bpi));\n        }\n    }\n\n    return ret;\n}\n\nQList<RVA> CutterCore::getBreakpointsAddresses()\n{\n    QList<RVA> bpAddresses;\n    for (const BreakpointDescription &bp : getBreakpoints()) {\n        bpAddresses << bp.addr;\n    }\n\n    return bpAddresses;\n}\n\nQList<RVA> CutterCore::getBreakpointsInFunction(RVA funcAddr)\n{\n    QList<RVA> allBreakpoints = getBreakpointsAddresses();\n    QList<RVA> functionBreakpoints;\n\n    // Use std manipulations to take only the breakpoints that belong to this function\n    std::copy_if(allBreakpoints.begin(), allBreakpoints.end(),\n                 std::back_inserter(functionBreakpoints),\n                 [this, funcAddr](RVA BPadd) { return getFunctionStart(BPadd) == funcAddr; });\n    return functionBreakpoints;\n}\n\nbool CutterCore::isBreakpoint(const QList<RVA> &breakpoints, RVA addr)\n{\n    return breakpoints.contains(addr);\n}\n\nQList<ThreadDescription> CutterCore::getProcessThreads(int pid)\n{\n    CORE_LOCK();\n    auto dbg = core_->dbg;\n    if (!dbg || !dbg->cur || !dbg->cur->threads) {\n        return {};\n    }\n    RzList *list = core_->dbg->cur->threads(dbg, pid != -1 ? pid : dbg->pid);\n    RzListIter *iter;\n    RzDebugPid *p;\n    QList<ThreadDescription> ret;\n\n    CutterRzListForeach (list, iter, RzDebugPid, p) {\n        ThreadDescription proc;\n\n        proc.current = dbg->tid == p->pid;\n        proc.ppid = p->ppid;\n        proc.pid = p->pid;\n        proc.uid = p->uid;\n        proc.status = static_cast<RzDebugPidState>(p->status);\n        proc.path = p->path;\n        proc.pc = p->pc;\n        proc.tls = p->tls;\n\n        ret << proc;\n    }\n    rz_list_free(list);\n    return ret;\n}\n\nQList<ProcessDescription> CutterCore::getProcesses(int pid)\n{\n    CORE_LOCK();\n    auto dbg = core_->dbg;\n    if (!dbg || !dbg->cur || !dbg->cur->threads) {\n        return {};\n    }\n    RzList *list = core_->dbg->cur->pids(dbg, pid >= 0 ? pid : dbg->pid);\n    RzListIter *iter;\n    RzDebugPid *p;\n    QList<ProcessDescription> ret;\n\n    CutterRzListForeach (list, iter, RzDebugPid, p) {\n        ProcessDescription proc;\n\n        proc.current = core->dbg->pid == p->pid;\n        proc.ppid = p->ppid;\n        proc.pid = p->pid;\n        proc.uid = p->uid;\n        proc.status = static_cast<RzDebugPidState>(p->status);\n        proc.path = p->path;\n\n        ret << proc;\n    }\n    rz_list_free(list);\n    return ret;\n}\n\nQList<MemoryMapDescription> CutterCore::getMemoryMap()\n{\n    CORE_LOCK();\n    RzList *list0 = rz_debug_map_list(core->dbg, false);\n    RzList *list1 = rz_debug_map_list(core->dbg, true);\n    rz_list_join(list0, list1);\n    QList<MemoryMapDescription> ret;\n    RzListIter *it;\n    RzDebugMap *map;\n    CutterRzListForeach (list0, it, RzDebugMap, map) {\n        MemoryMapDescription memMap;\n\n        memMap.name = map->name;\n        memMap.fileName = map->file;\n        memMap.addrStart = map->addr;\n        memMap.addrEnd = map->addr_end;\n        memMap.type = map->user ? \"u\" : \"s\";\n        memMap.permission = rz_str_rwx_i(map->perm);\n\n        ret << memMap;\n    }\n\n    return ret;\n}\n\nvoid CutterCore::setGraphEmpty(bool empty)\n{\n    emptyGraph = empty;\n}\n\nbool CutterCore::isGraphEmpty()\n{\n    return emptyGraph;\n}\n\nvoid CutterCore::getRegs()\n{\n    CORE_LOCK();\n    this->regs = {};\n    const RzList *rs = rz_reg_get_list(getReg(), RZ_REG_TYPE_ANY);\n    if (!rs) {\n        return;\n    }\n    for (const auto &r : CutterRzList<RzRegItem>(rs)) {\n        this->regs.push_back(r->name);\n    }\n}\n\nvoid CutterCore::setSettings()\n{\n    setConfig(\"scr.interactive\", false);\n\n    setConfig(\"hex.pairs\", false);\n    setConfig(\"asm.xrefs\", false);\n\n    setConfig(\"asm.tabs.once\", true);\n    setConfig(\"asm.flags.middle\", 2);\n\n    setConfig(\"analysis.hasnext\", false);\n    setConfig(\"asm.lines.call\", false);\n\n    // Colors\n    setConfig(\"scr.color\", COLOR_MODE_DISABLED);\n\n    // Don't show hits\n    setConfig(\"search.flags\", false);\n}\n\nQList<RVA> CutterCore::getSeekHistory()\n{\n    CORE_LOCK();\n    QList<RVA> ret;\n    RzListIter *it;\n    RzCoreSeekItem *undo;\n    RzList *list = rz_core_seek_list(core);\n    CutterRzListForeach (list, it, RzCoreSeekItem, undo) {\n        ret << undo->offset;\n    }\n\n    return ret;\n}\n\nQStringList CutterCore::getAsmPluginNames()\n{\n    CORE_LOCK();\n    QStringList ret;\n    CutterHtSP<RzAsmPlugin>(core->rasm->plugins)\n            .ForEach([&ret](const char *k, const RzAsmPlugin *ap) {\n                ret << ap->name;\n                return true;\n            });\n    return ret;\n}\n\nQStringList CutterCore::getAnalysisPluginNames()\n{\n    CORE_LOCK();\n    RzListIter *it;\n    QStringList ret;\n    CutterHtSP<RzAnalysisPlugin>(core->analysis->plugins)\n            .ForEach([&ret](const char *k, const RzAnalysisPlugin *ap) {\n                ret << ap->name;\n                return true;\n            });\n    return ret;\n}\n\nbool CutterCore::hasAssembler()\n{\n    CORE_LOCK();\n    QString archStr = Core()->getConfig(\"asm.arch\");\n    int currBits = Core()->getConfigi(\"asm.bits\");\n    if (!core->rasm || archStr.isEmpty() || currBits == 0) {\n        return false;\n    }\n\n    bool found = false;\n    CutterHtSP<RzAsmPlugin>(core->rasm->plugins).ForEach([&](const char *k, const RzAsmPlugin *ap) {\n        if (!ap->arch || !ap->assemble || !(ap->bits & currBits)) {\n            return true;\n        }\n        if (QString(ap->arch) == archStr) {\n            found = true;\n            return false;\n        }\n        return true;\n    });\n    return found;\n}\n\nQList<RzBinPluginDescription> CutterCore::getBinPluginDescriptions(bool bin, bool xtr)\n{\n    CORE_LOCK();\n    QList<RzBinPluginDescription> ret;\n    if (bin) {\n        CutterHtSP<RzBinPlugin>(core->bin->plugins)\n                .ForEach([&ret](const char *k, const RzBinPlugin *bp) {\n                    RzBinPluginDescription desc;\n                    desc.name = bp->name ? bp->name : \"\";\n                    desc.description = bp->desc ? bp->desc : \"\";\n                    desc.license = bp->license ? bp->license : \"\";\n                    desc.type = \"bin\";\n                    ret.append(desc);\n                    return true;\n                });\n    }\n    if (xtr) {\n        CutterHtSP<RzBinXtrPlugin>(core->bin->binxtrs)\n                .ForEach([&ret](const char *k, const RzBinXtrPlugin *bx) {\n                    RzBinPluginDescription desc;\n                    desc.name = bx->name ? bx->name : \"\";\n                    desc.description = bx->desc ? bx->desc : \"\";\n                    desc.license = bx->license ? bx->license : \"\";\n                    desc.type = \"xtr\";\n                    ret.append(desc);\n                    return true;\n                });\n    }\n    return ret;\n}\n\nQList<RzIOPluginDescription> CutterCore::getRIOPluginDescriptions()\n{\n    CORE_LOCK();\n    QList<RzIOPluginDescription> ret;\n    CutterHtSP<RzIOPlugin>(core->io->plugins).ForEach([&ret](const char *k, const RzIOPlugin *p) {\n        RzIOPluginDescription desc;\n        desc.name = p->name ? p->name : \"\";\n        desc.description = p->desc ? p->desc : \"\";\n        desc.license = p->license ? p->license : \"\";\n        desc.permissions = QString(\"r\") + (p->write ? \"w\" : \"_\") + (p->isdbg ? \"d\" : \"_\");\n        if (p->uris) {\n            desc.uris = QString::fromUtf8(p->uris).split(\",\");\n        }\n        ret.append(desc);\n        return true;\n    });\n    return ret;\n}\n\nQList<RzCorePluginDescription> CutterCore::getRCorePluginDescriptions()\n{\n    CORE_LOCK();\n    QList<RzCorePluginDescription> ret;\n    CutterHtSP<RzCorePlugin>(core->plugins).ForEach([&ret](const char *k, const RzCorePlugin *p) {\n        RzCorePluginDescription desc;\n        desc.name = p->name ? p->name : \"\";\n        desc.description = p->desc ? p->desc : \"\";\n        desc.license = p->license ? p->license : \"\";\n        ret.append(desc);\n        return true;\n    });\n    return ret;\n}\n\nQList<RzAsmPluginDescription> CutterCore::getRAsmPluginDescriptions()\n{\n    CORE_LOCK();\n    QList<RzAsmPluginDescription> ret;\n\n    CutterHtSP<RzAsmPlugin>(core->rasm->plugins)\n            .ForEach([&ret](const char *k, const RzAsmPlugin *ap) {\n                RzAsmPluginDescription plugin;\n\n                plugin.name = ap->name;\n                plugin.architecture = ap->arch;\n                plugin.author = ap->author;\n                plugin.version = ap->version;\n                plugin.cpus = ap->cpus;\n                plugin.description = ap->desc;\n                plugin.license = ap->license;\n\n                ret << plugin;\n                return true;\n            });\n\n    return ret;\n}\n\nQList<FunctionDescription> CutterCore::getAllFunctions()\n{\n    CORE_LOCK();\n\n    QList<FunctionDescription> funcList;\n    funcList.reserve(rz_list_length(core->analysis->fcns));\n\n    RzListIter *iter;\n    RzAnalysisFunction *fcn;\n    CutterRzListForeach (core->analysis->fcns, iter, RzAnalysisFunction, fcn) {\n        FunctionDescription function;\n        function.offset = fcn->addr;\n        function.linearSize = rz_analysis_function_linear_size(fcn);\n        function.nargs = rz_analysis_arg_count(fcn);\n        function.nlocals = rz_analysis_var_local_count(fcn);\n        function.nbbs = rz_pvector_len(fcn->bbs);\n        function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();\n        function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();\n        function.edges = rz_analysis_function_count_edges(fcn, nullptr);\n        function.stackframe = fcn->maxstack;\n        funcList.append(function);\n    }\n\n    return funcList;\n}\n\nstatic inline uint64_t rva(RzBinObject *o, uint64_t paddr, uint64_t vaddr, int va)\n{\n    return va ? rz_bin_object_get_vaddr(o, paddr, vaddr) : paddr;\n}\n\nQList<ImportDescription> CutterCore::getAllImports()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n    const RzPVector *imports = rz_bin_object_get_imports(bf->o);\n    if (!imports) {\n        return {};\n    }\n\n    QList<ImportDescription> qList;\n    bool va = core->io->va || core->bin->is_debugger;\n    for (const auto &import : CutterPVector<RzBinImport>(imports)) {\n        if (RZ_STR_ISEMPTY(import->name)) {\n            continue;\n        }\n\n        ImportDescription importDescription;\n\n        RzBinSymbol *sym = rz_bin_object_get_symbol_of_import(bf->o, import);\n        ut64 addr = sym ? rva(bf->o, sym->paddr, sym->vaddr, va) : UT64_MAX;\n        QString name { import->name };\n        if (RZ_STR_ISNOTEMPTY(import->classname)) {\n            name = QString(\"%1.%2\").arg(import->classname, import->name);\n        }\n        if (core->bin->prefix) {\n            name = QString(\"%1.%2\").arg(core->bin->prefix, name);\n        }\n\n        importDescription.ordinal = (int)import->ordinal;\n        importDescription.bind = import->bind;\n        importDescription.type = import->type;\n        importDescription.libname = import->libname;\n        importDescription.name = name;\n        importDescription.plt = addr;\n\n        qList << importDescription;\n    }\n\n    return qList;\n}\n\nQList<ExportDescription> CutterCore::getAllExports()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n\n    const RzPVector *symbols = rz_bin_object_get_symbols(bf->o);\n    if (!symbols) {\n        return {};\n    }\n\n    bool va = core->io->va || core->bin->is_debugger;\n    bool demangle = rz_config_get_b(core->config, \"bin.demangle\");\n\n    QList<ExportDescription> ret;\n    for (const auto &symbol : CutterPVector<RzBinSymbol>(symbols)) {\n        if (!(symbol->name && rz_core_sym_is_export(symbol))) {\n            continue;\n        }\n\n        RzBinSymNames sn = {};\n        rz_core_sym_name_init(&sn, symbol, demangle);\n\n        ExportDescription exportDescription;\n        exportDescription.vaddr = rva(bf->o, symbol->paddr, symbol->vaddr, va);\n        exportDescription.paddr = symbol->paddr;\n        exportDescription.size = symbol->size;\n        exportDescription.type = symbol->type;\n        exportDescription.name = sn.symbolname;\n        exportDescription.flag_name = sn.nameflag;\n        ret << exportDescription;\n\n        rz_core_sym_name_fini(&sn);\n    }\n    return ret;\n}\n\nQList<SymbolDescription> CutterCore::getAllSymbols()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n\n    QList<SymbolDescription> ret;\n    const RzPVector *symbols = rz_bin_object_get_symbols(bf->o);\n    if (symbols) {\n        for (const auto &bs : CutterPVector<RzBinSymbol>(symbols)) {\n            QString type = QString(bs->bind) + \" \" + QString(bs->type);\n            SymbolDescription symbol;\n            symbol.vaddr = bs->vaddr;\n            symbol.name = QString(bs->name);\n            symbol.bind = QString(bs->bind);\n            symbol.type = QString(bs->type);\n            ret << symbol;\n        }\n    }\n\n    const RzPVector *entries = rz_bin_object_get_entries(bf->o);\n    if (symbols) {\n        /* list entrypoints as symbols too */\n        int n = 0;\n        for (const auto &entry : CutterPVector<RzBinAddr>(entries)) {\n            SymbolDescription symbol;\n            symbol.vaddr = entry->vaddr;\n            symbol.name = QString(\"entry\") + QString::number(n++);\n            symbol.bind.clear();\n            symbol.type = \"entry\";\n            ret << symbol;\n        }\n    }\n\n    return ret;\n}\n\nQList<HeaderDescription> CutterCore::getAllHeaders()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n    const RzPVector *fields = rz_bin_object_get_fields(bf->o);\n    if (!fields) {\n        return {};\n    }\n    QList<HeaderDescription> ret;\n    for (auto field : CutterPVector<RzBinField>(fields)) {\n        HeaderDescription header;\n        header.vaddr = field->vaddr;\n        header.paddr = field->paddr;\n        header.value = RZ_STR_ISEMPTY(field->comment) ? \"\" : field->comment;\n        header.name = field->name;\n        ret << header;\n    }\n    return ret;\n}\n\nQList<FlirtDescription> CutterCore::getSignaturesDB()\n{\n    CORE_LOCK();\n\n    void *ptr = NULL;\n    RzListIter *iter = NULL;\n    QList<FlirtDescription> sigdb;\n\n    RzList *list = rz_core_analysis_sigdb_list(core, true);\n\n    rz_list_foreach(list, iter, ptr)\n    {\n        RzSigDBEntry *sig = static_cast<RzSigDBEntry *>(ptr);\n        FlirtDescription flirt;\n        flirt.bin_name = sig->bin_name;\n        flirt.arch_name = sig->arch_name;\n        flirt.base_name = sig->base_name;\n        flirt.short_path = sig->short_path;\n        flirt.file_path = sig->file_path;\n        flirt.details = sig->details;\n        flirt.n_modules = QString::number(sig->n_modules);\n        flirt.arch_bits = QString::number(sig->arch_bits);\n        sigdb << flirt;\n    }\n    rz_list_free(list);\n\n    return sigdb;\n}\n\nQList<CommentDescription> CutterCore::getAllComments(const QString &filterType)\n{\n    CORE_LOCK();\n    QList<CommentDescription> qList;\n    RzIntervalTreeIter it;\n    void *pVoid;\n    RzAnalysisMetaItem *item;\n    RzSpace *spaces = rz_spaces_current(&core->analysis->meta_spaces);\n    rz_interval_tree_foreach(&core->analysis->meta, it, pVoid)\n    {\n        item = reinterpret_cast<RzAnalysisMetaItem *>(pVoid);\n        if (item->type != RZ_META_TYPE_COMMENT) {\n            continue;\n        }\n        if (spaces && spaces != item->space) {\n            continue;\n        }\n        if (filterType != rz_meta_type_to_string(item->type)) {\n            continue;\n        }\n\n        RzIntervalNode *node = rz_interval_tree_iter_get(&it);\n        CommentDescription comment;\n        comment.offset = node->start;\n        comment.name = fromOwnedCharPtr(rz_str_escape(item->str));\n        qList << comment;\n    }\n    return qList;\n}\n\nQList<RelocDescription> CutterCore::getAllRelocs()\n{\n    CORE_LOCK();\n    QList<RelocDescription> ret;\n\n    if (core && core->bin && core->bin->cur && core->bin->cur->o) {\n        auto relocs = rz_bin_object_patch_relocs(core->bin->cur, core->bin->cur->o);\n        if (!relocs) {\n            return ret;\n        }\n        for (size_t i = 0; i < relocs->relocs_count; i++) {\n            RzBinReloc *reloc = relocs->relocs[i];\n            RelocDescription desc;\n            desc.vaddr = reloc->vaddr;\n            desc.paddr = reloc->paddr;\n            desc.type = (reloc->additive ? \"ADD_\" : \"SET_\") + QString::number(reloc->type);\n\n            if (reloc->import)\n                desc.name = reloc->import->name;\n            else\n                desc.name = QString(\"reloc_%1\").arg(QString::number(reloc->vaddr, 16));\n\n            ret << desc;\n        }\n    }\n\n    return ret;\n}\n\nQList<StringDescription> CutterCore::getAllStrings()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n    RzBinObject *obj = rz_bin_cur_object(core->bin);\n    if (!obj) {\n        return {};\n    }\n\n    RzPVector *strings = rz_core_bin_whole_strings(core, bf);\n    if (!strings) {\n        return {};\n    }\n\n    int va = core->io->va || core->bin->is_debugger;\n    RzStrEscOptions opt = {};\n    opt.show_asciidot = false;\n    opt.esc_bslash = true;\n    opt.esc_double_quotes = true;\n    opt.keep_printable = true;\n\n    QList<StringDescription> ret;\n    for (const auto &str : CutterPVector<RzBinString>(strings)) {\n        auto section = obj ? rz_bin_get_section_at(obj, str->paddr, 0) : NULL;\n\n        StringDescription string;\n        string.string = rz_str_escape_utf8(str->string, &opt);\n        string.vaddr = obj ? rva(obj, str->paddr, str->vaddr, va) : str->paddr;\n        string.type = rz_str_enc_as_string(str->type);\n        string.size = str->size;\n        string.length = str->length;\n        string.section = section ? section->name : \"\";\n\n        ret << string;\n    }\n\n    return ret;\n}\n\nQList<FlagspaceDescription> CutterCore::getAllFlagspaces()\n{\n    CORE_LOCK();\n    QList<FlagspaceDescription> flagspaces;\n    RzSpaceIter it;\n    RzSpace *space;\n    rz_flag_space_foreach(core->flags, it, space)\n    {\n        FlagspaceDescription flagspace;\n        flagspace.name = space->name;\n        flagspaces << flagspace;\n    }\n    return flagspaces;\n}\n\nQList<FlagDescription> CutterCore::getAllFlags(QString flagspace)\n{\n    CORE_LOCK();\n    QList<FlagDescription> flags;\n    std::string name = flagspace.isEmpty() || flagspace.isNull() ? \"*\" : flagspace.toStdString();\n    RzSpace *space = rz_flag_space_get(core->flags, name.c_str());\n    rz_flag_foreach_space(\n            core->flags, space,\n            [](RzFlagItem *item, void *user) {\n                FlagDescription flag;\n                flag.offset = item->offset;\n                flag.size = item->size;\n                flag.name = item->name;\n                flag.realname = item->name;\n                reinterpret_cast<QList<FlagDescription> *>(user)->append(flag);\n                return true;\n            },\n            &flags);\n    return flags;\n}\n\nQList<SectionDescription> CutterCore::getAllSections()\n{\n    CORE_LOCK();\n    QList<SectionDescription> sections;\n\n    RzBinObject *o = rz_bin_cur_object(core->bin);\n    if (!o) {\n        return sections;\n    }\n\n    RzPVector *sects = rz_bin_object_get_sections(o);\n    if (!sects) {\n        return sections;\n    }\n    RzList *hashnames = rz_list_newf(free);\n    if (!hashnames) {\n        return sections;\n    }\n    rz_list_push(hashnames, rz_str_dup(\"entropy\"));\n    for (const auto &sect : CutterPVector<RzBinSection>(sects)) {\n        if (RZ_STR_ISEMPTY(sect->name))\n            continue;\n\n        SectionDescription section;\n        section.name = sect->name;\n        section.vaddr = sect->vaddr;\n        section.vsize = sect->vsize;\n        section.paddr = sect->paddr;\n        section.size = sect->size;\n        section.perm = rz_str_rwx_i(sect->perm);\n        if (sect->size > 0) {\n            HtSS *digests = rz_core_bin_create_digests(core, sect->paddr, sect->size, hashnames);\n            if (!digests) {\n                continue;\n            }\n            const char *entropy = (const char *)ht_ss_find(digests, \"entropy\", NULL);\n            section.entropy = rz_str_get(entropy);\n            ht_ss_free(digests);\n        }\n\n        sections << section;\n    }\n    rz_pvector_free(sects);\n    return sections;\n}\n\nQStringList CutterCore::getSectionList()\n{\n    CORE_LOCK();\n    QStringList ret;\n\n    RzBinObject *o = rz_bin_cur_object(core->bin);\n    if (!o) {\n        return ret;\n    }\n\n    RzPVector *sects = rz_bin_object_get_sections(o);\n    if (!sects) {\n        return ret;\n    }\n    for (const auto &sect : CutterPVector<RzBinSection>(sects)) {\n        ret << sect->name;\n    }\n    rz_pvector_free(sects);\n    return ret;\n}\n\nstatic inline QString perms_str(int perms)\n{\n    return QString((perms & RZ_PERM_SHAR) ? 's' : '-') + rz_str_rwx_i(perms);\n}\n\nQList<SegmentDescription> CutterCore::getAllSegments()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n    RzPVector *segments = rz_bin_object_get_segments(bf->o);\n    if (!segments) {\n        return {};\n    }\n\n    QList<SegmentDescription> ret;\n    for (const auto &segment : CutterPVector<RzBinSection>(segments)) {\n        SegmentDescription segDesc;\n        segDesc.name = segment->name;\n        segDesc.vaddr = segment->vaddr;\n        segDesc.paddr = segment->paddr;\n        segDesc.size = segment->size;\n        segDesc.vsize = segment->vsize;\n        segDesc.perm = perms_str(segment->perm);\n        ret << segDesc;\n    }\n    rz_pvector_free(segments);\n\n    return ret;\n}\n\nQList<EntrypointDescription> CutterCore::getAllEntrypoint()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n    bool va = core->io->va || core->bin->is_debugger;\n    ut64 baddr = rz_bin_get_baddr(core->bin);\n    ut64 laddr = rz_bin_get_laddr(core->bin);\n\n    QList<EntrypointDescription> qList;\n    const RzPVector *entries = rz_bin_object_get_entries(bf->o);\n    for (const auto &entry : CutterPVector<RzBinAddr>(entries)) {\n        if (entry->type != RZ_BIN_ENTRY_TYPE_PROGRAM) {\n            continue;\n        }\n        const char *type = rz_bin_entry_type_string(entry->type);\n\n        EntrypointDescription entrypointDescription;\n        entrypointDescription.vaddr = rva(bf->o, entry->paddr, entry->vaddr, va);\n        entrypointDescription.paddr = entry->paddr;\n        entrypointDescription.baddr = baddr;\n        entrypointDescription.laddr = laddr;\n        entrypointDescription.haddr = entry->hpaddr ? entry->hpaddr : UT64_MAX;\n        entrypointDescription.type = type ? type : \"unknown\";\n        qList << entrypointDescription;\n    }\n\n    return qList;\n}\n\nQList<BinClassDescription> CutterCore::getAllClassesFromBin()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n\n    const RzPVector *cs = rz_bin_object_get_classes(bf->o);\n    if (!cs) {\n        return {};\n    }\n\n    QList<BinClassDescription> qList;\n    RzListIter *iter2, *iter3;\n    RzBinSymbol *sym;\n    RzBinClassField *f;\n    for (const auto &c : CutterPVector<RzBinClass>(cs)) {\n        BinClassDescription classDescription;\n        classDescription.name = c->name;\n        classDescription.addr = c->addr;\n        CutterRzListForeach (c->methods, iter2, RzBinSymbol, sym) {\n            BinClassMethodDescription methodDescription;\n            methodDescription.name = sym->name;\n            methodDescription.addr = sym->vaddr;\n            classDescription.methods << methodDescription;\n        }\n        CutterRzListForeach (c->fields, iter3, RzBinClassField, f) {\n            BinClassFieldDescription fieldDescription;\n            fieldDescription.name = f->name;\n            fieldDescription.addr = f->vaddr;\n            classDescription.fields << fieldDescription;\n        }\n        qList << classDescription;\n    }\n    return qList;\n}\n\nQList<BinClassDescription> CutterCore::getAllClassesFromFlags()\n{\n    static const QRegularExpression classFlagRegExp(\"^class\\\\.(.*)$\");\n    static const QRegularExpression methodFlagRegExp(\"^method\\\\.([^\\\\.]*)\\\\.(.*)$\");\n\n    CORE_LOCK();\n    QList<BinClassDescription> ret;\n    QMap<QString, BinClassDescription *> classesCache;\n\n    for (const auto &item : getAllFlags(\"classes\")) {\n        QRegularExpressionMatch match = classFlagRegExp.match(item.name);\n        if (match.hasMatch()) {\n            QString className = match.captured(1);\n            BinClassDescription *desc = nullptr;\n            auto it = classesCache.find(className);\n            if (it == classesCache.end()) {\n                BinClassDescription cls = {};\n                ret << cls;\n                desc = &ret.last();\n                classesCache[className] = desc;\n            } else {\n                desc = it.value();\n            }\n            desc->name = match.captured(1);\n            desc->addr = item.offset;\n            continue;\n        }\n\n        match = methodFlagRegExp.match(item.name);\n        if (match.hasMatch()) {\n            QString className = match.captured(1);\n            BinClassDescription *classDesc = nullptr;\n            auto it = classesCache.find(className);\n            if (it == classesCache.end()) {\n                // add a new stub class, will be replaced if class flag comes after it\n                BinClassDescription cls;\n                cls.name = tr(\"Unknown (%1)\").arg(className);\n                cls.addr = RVA_INVALID;\n                ret << cls;\n                classDesc = &ret.last();\n                classesCache[className] = classDesc;\n            } else {\n                classDesc = it.value();\n            }\n\n            BinClassMethodDescription meth;\n            meth.name = match.captured(2);\n            meth.addr = item.offset;\n            classDesc->methods << meth;\n            continue;\n        }\n    }\n    return ret;\n}\n\nQList<QString> CutterCore::getAllAnalysisClasses(bool sorted)\n{\n    CORE_LOCK();\n    QList<QString> ret;\n    PVectorPtr l = makePVectorPtr(rz_analysis_class_get_all(core->analysis, sorted));\n    if (!l) {\n        return ret;\n    }\n    ret.reserve(static_cast<int>(rz_pvector_len(l.get())));\n    for (const auto &kv : CutterPVector<SdbKv>(l.get())) {\n        ret.append(QString::fromUtf8(reinterpret_cast<const char *>(kv->base.key)));\n    }\n    return ret;\n}\n\nQList<AnalysisMethodDescription> CutterCore::getAnalysisClassMethods(const QString &cls)\n{\n    CORE_LOCK();\n    QList<AnalysisMethodDescription> ret;\n\n    RzVector *meths = rz_analysis_class_method_get_all(core->analysis, cls.toUtf8().constData());\n    if (!meths) {\n        return ret;\n    }\n\n    ret.reserve(static_cast<int>(meths->len));\n    RzAnalysisMethod *meth;\n    CutterRzVectorForeach(meths, meth, RzAnalysisMethod)\n    {\n        AnalysisMethodDescription desc;\n        desc.name = QString::fromUtf8(meth->name);\n        desc.realName = QString::fromUtf8(meth->real_name);\n        desc.addr = meth->addr;\n        desc.vtableOffset = meth->vtable_offset;\n        ret.append(desc);\n    }\n    rz_vector_free(meths);\n\n    return ret;\n}\n\nQList<AnalysisBaseClassDescription> CutterCore::getAnalysisClassBaseClasses(const QString &cls)\n{\n    CORE_LOCK();\n    QList<AnalysisBaseClassDescription> ret;\n\n    RzVector *bases = rz_analysis_class_base_get_all(core->analysis, cls.toUtf8().constData());\n    if (!bases) {\n        return ret;\n    }\n\n    ret.reserve(static_cast<int>(bases->len));\n    RzAnalysisBaseClass *base;\n    CutterRzVectorForeach(bases, base, RzAnalysisBaseClass)\n    {\n        AnalysisBaseClassDescription desc;\n        desc.id = QString::fromUtf8(base->id);\n        desc.offset = base->offset;\n        desc.className = QString::fromUtf8(base->class_name);\n        ret.append(desc);\n    }\n    rz_vector_free(bases);\n\n    return ret;\n}\n\nQList<AnalysisVTableDescription> CutterCore::getAnalysisClassVTables(const QString &cls)\n{\n    CORE_LOCK();\n    QList<AnalysisVTableDescription> acVtables;\n\n    RzVector *vtables = rz_analysis_class_vtable_get_all(core->analysis, cls.toUtf8().constData());\n    if (!vtables) {\n        return acVtables;\n    }\n\n    acVtables.reserve(static_cast<int>(vtables->len));\n    RzAnalysisVTable *vtable;\n    CutterRzVectorForeach(vtables, vtable, RzAnalysisVTable)\n    {\n        AnalysisVTableDescription desc;\n        desc.id = QString::fromUtf8(vtable->id);\n        desc.offset = vtable->offset;\n        desc.addr = vtable->addr;\n        acVtables.append(desc);\n    }\n    rz_vector_free(vtables);\n\n    return acVtables;\n}\n\nvoid CutterCore::createNewClass(const QString &cls)\n{\n    CORE_LOCK();\n    rz_analysis_class_create(core->analysis, cls.toUtf8().constData());\n}\n\nvoid CutterCore::renameClass(const QString &oldName, const QString &newName)\n{\n    CORE_LOCK();\n    rz_analysis_class_rename(core->analysis, oldName.toUtf8().constData(),\n                             newName.toUtf8().constData());\n}\n\nvoid CutterCore::deleteClass(const QString &cls)\n{\n    CORE_LOCK();\n    rz_analysis_class_delete(core->analysis, cls.toUtf8().constData());\n}\n\nbool CutterCore::getAnalysisMethod(const QString &cls, const QString &meth,\n                                   AnalysisMethodDescription *desc)\n{\n    CORE_LOCK();\n    RzAnalysisMethod analysisMeth;\n    if (rz_analysis_class_method_get(core->analysis, cls.toUtf8().constData(),\n                                     meth.toUtf8().constData(), &analysisMeth)\n        != RZ_ANALYSIS_CLASS_ERR_SUCCESS) {\n        return false;\n    }\n    desc->name = QString::fromUtf8(analysisMeth.name);\n    desc->realName = QString::fromUtf8(analysisMeth.real_name);\n    desc->addr = analysisMeth.addr;\n    desc->vtableOffset = analysisMeth.vtable_offset;\n    rz_analysis_class_method_fini(&analysisMeth);\n    return true;\n}\n\nvoid CutterCore::setAnalysisMethod(const QString &className, const AnalysisMethodDescription &meth)\n{\n    CORE_LOCK();\n    RzAnalysisMethod analysisMeth;\n    analysisMeth.name = rz_str_dup(meth.name.toUtf8().constData());\n    analysisMeth.real_name = rz_str_dup(meth.realName.toUtf8().constData());\n    analysisMeth.addr = meth.addr;\n    analysisMeth.vtable_offset = meth.vtableOffset;\n    rz_analysis_class_method_set(core->analysis, className.toUtf8().constData(), &analysisMeth);\n    rz_analysis_class_method_fini(&analysisMeth);\n}\n\nvoid CutterCore::renameAnalysisMethod(const QString &className, const QString &oldMethodName,\n                                      const QString &newMethodName)\n{\n    CORE_LOCK();\n    rz_analysis_class_method_rename(core->analysis, className.toUtf8().constData(),\n                                    oldMethodName.toUtf8().constData(),\n                                    newMethodName.toUtf8().constData());\n}\n\nQList<ResourcesDescription> CutterCore::getAllResources()\n{\n    CORE_LOCK();\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (!bf) {\n        return {};\n    }\n    const RzPVector *resources = rz_bin_object_get_resources(bf->o);\n    if (!resources) {\n        return {};\n    }\n\n    QList<ResourcesDescription> resourcesDescriptions;\n\n    for (const auto &resource : CutterPVector<RzBinResource>(resources)) {\n        ResourcesDescription description;\n        description.name = resource->name;\n        description.vaddr = resource->vaddr;\n        description.index = resource->index;\n        description.type = resource->type;\n        description.size = resource->size;\n        description.lang = resource->language;\n\n        resourcesDescriptions << description;\n    }\n\n    return resourcesDescriptions;\n}\n\nQList<VTableDescription> CutterCore::getAllVTables()\n{\n    CORE_LOCK();\n    QList<VTableDescription> vtableDescs;\n    RVTableContext context;\n    rz_analysis_vtable_begin(core->analysis, &context);\n    RzList *vtables = rz_analysis_vtable_search(&context);\n    RzListIter *iter;\n    RVTableInfo *table;\n    RVTableMethodInfo *method;\n    CutterRzListForeach (vtables, iter, RVTableInfo, table) {\n        VTableDescription tableDesc;\n        tableDesc.addr = table->saddr;\n        CutterRzVectorForeach(&table->methods, method, RVTableMethodInfo)\n        {\n            BinClassMethodDescription methodDesc;\n            RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, method->addr, 0);\n            const char *fname = fcn ? fcn->name : nullptr;\n            methodDesc.addr = method->addr;\n            methodDesc.name = fname;\n            tableDesc.methods << methodDesc;\n        }\n        vtableDescs << tableDesc;\n    }\n    rz_list_free(vtables);\n    return vtableDescs;\n}\n\nQList<TypeDescription> CutterCore::getAllTypes()\n{\n    QList<TypeDescription> types;\n\n    types.append(getAllPrimitiveTypes());\n    types.append(getAllUnions());\n    types.append(getAllStructs());\n    types.append(getAllEnums());\n    types.append(getAllTypedefs());\n\n    return types;\n}\n\nQList<TypeDescription> CutterCore::getBaseType(RzBaseTypeKind kind, const char *category)\n{\n    CORE_LOCK();\n    QList<TypeDescription> types;\n\n    RzList *ts = rz_type_db_get_base_types_of_kind(core->analysis->typedb, kind);\n    RzBaseType *type;\n    RzListIter *iter;\n\n    CutterRzListForeach (ts, iter, RzBaseType, type) {\n        TypeDescription exp;\n\n        exp.type = type->name;\n        exp.size = rz_type_db_base_get_bitsize(core->analysis->typedb, type);\n        exp.format = rz_base_type_as_format(core->analysis->typedb, type);\n        exp.category = tr(category);\n        types << exp;\n    }\n    rz_list_free(ts);\n\n    return types;\n}\n\nQList<TypeDescription> CutterCore::getAllPrimitiveTypes()\n{\n    return getBaseType(RZ_BASE_TYPE_KIND_ATOMIC, \"Primitive\");\n}\n\nQList<TypeDescription> CutterCore::getAllUnions()\n{\n    return getBaseType(RZ_BASE_TYPE_KIND_UNION, \"Union\");\n}\n\nQList<TypeDescription> CutterCore::getAllStructs()\n{\n    return getBaseType(RZ_BASE_TYPE_KIND_STRUCT, \"Struct\");\n}\n\nQList<TypeDescription> CutterCore::getAllEnums()\n{\n    return getBaseType(RZ_BASE_TYPE_KIND_ENUM, \"Enum\");\n}\n\nQList<TypeDescription> CutterCore::getAllTypedefs()\n{\n    return getBaseType(RZ_BASE_TYPE_KIND_TYPEDEF, \"Typedef\");\n}\n\nQString CutterCore::getTypeAsC(QString name)\n{\n    CORE_LOCK();\n    QString output = \"Failed to fetch the output.\";\n    if (name.isEmpty()) {\n        return output;\n    }\n    char *earg = rz_cmd_escape_arg(name.toUtf8().constData(), RZ_CMD_ESCAPE_ONE_ARG);\n    QString result = fromOwnedCharPtr(rz_core_types_as_c(core, earg, true));\n    free(earg);\n    return result;\n}\n\nbool CutterCore::typeExists(const QString &typeName)\n{\n    CORE_LOCK();\n    return rz_type_exists(core->analysis->typedb, typeName.toUtf8().constData());\n}\n\nbool CutterCore::isAddressMapped(RVA addr)\n{\n    CORE_LOCK();\n    return rz_io_map_get(core->io, addr);\n}\n\nQList<SearchDescription> CutterCore::getAllSearchCommand(QString searchFor, SearchKind kind,\n                                                         QString in)\n{\n    CORE_LOCK();\n    QList<SearchDescription> searchRef;\n\n    TempConfig cfg;\n    cfg.set(\"search.in\", in);\n    CutterJson searchArray;\n    char *arg = rz_cmd_escape_arg(searchFor.toUtf8().constData(), RZ_CMD_ESCAPE_ONE_ARG);\n    if (!arg) {\n        return {};\n    }\n\n    QString cmd, suffix;\n    // Those are the searches which don't follow the search hit standardization of the new\n    // search yet.\n    switch (kind) {\n    default:\n        qWarning() << tr(\"Error invalid search kind\\n\");\n        return searchRef;\n    case SearchKind::AsmCode:\n        cmd = \"/acj\";\n        break;\n    case SearchKind::ROPGadgets:\n        cmd = \"/Rj\";\n        break;\n    case SearchKind::ROPGadgetsRegex:\n        cmd = \"/R/j\";\n        break;\n    }\n    // Legacy commands don't get escaped arguments.\n    auto cstr = QString(\"%1 %2\").arg(cmd, kind == SearchKind::AsmCode ? searchFor : arg);\n    searchArray = cmdj(cstr);\n    if (kind == SearchKind::ROPGadgets || kind == SearchKind::ROPGadgetsRegex) {\n        for (CutterJson searchObject : searchArray) {\n            SearchDescription exp;\n\n            exp.code.clear();\n            for (CutterJson gadget : searchObject[RJsonKey::opcodes]) {\n                exp.code += gadget[RJsonKey::opcode].toString() + \";  \";\n            }\n\n            exp.offset = searchObject[RJsonKey::opcodes].first()[RJsonKey::offset].toRVA();\n            exp.size = searchObject[RJsonKey::size].toUt64();\n\n            searchRef << exp;\n        }\n        return searchRef;\n    }\n    for (CutterJson searchObject : searchArray) {\n        SearchDescription exp;\n\n        exp.offset = searchObject[RJsonKey::offset].toRVA();\n        exp.size = searchObject[RJsonKey::len].toUt64();\n        exp.code = searchObject[RJsonKey::code].toString();\n        exp.data = searchObject[RJsonKey::data].toString();\n        exp.detail = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, exp.offset);\n\n        searchRef << exp;\n    }\n    return searchRef;\n}\n\nstatic UniquePtrC<RzSearchOpt, &rz_search_opt_free> cutterSetupSearchOptions(RzCore *core)\n{\n    auto searchOpts = UniquePtrC<RzSearchOpt, &rz_search_opt_free>(rz_search_opt_new());\n    if (!searchOpts) {\n        return {};\n    }\n    RzThreadNCores max_threads =\n            (RzThreadNCores)rz_config_get_i(core->config, \"search.max_threads\");\n    max_threads = rz_th_max_threads(max_threads);\n    ut32 max_hits = rz_config_get_i(core->config, \"search.maxhits\");\n    const char *show_progress = rz_config_get(core->config, \"search.show_progress\");\n    if (!(rz_search_opt_set_max_threads(searchOpts.get(), max_threads)\n          && rz_search_opt_set_max_hits(searchOpts.get(), max_hits)\n          && rz_search_opt_set_show_progress_from_str(searchOpts.get(), show_progress))) {\n        RZ_LOG_ERROR(\"Failed setup find options.\\n\");\n        return {};\n    }\n\n    RzSearchFindOpt *fopts = rz_core_setup_default_search_find_opts(core);\n    if (!fopts) {\n        RZ_LOG_ERROR(\"Failed init find options.\\n\");\n        return {};\n    }\n    if (!rz_search_opt_set_find_options(searchOpts.get(), fopts)) {\n        RZ_LOG_ERROR(\"Failed add find options to the search options.\\n\");\n        return {};\n    }\n    return searchOpts;\n}\n\nclass CutterSearchLock\n{\npublic:\n    CutterSearchLock(RzCore *core) : core_(core)\n    {\n        rz_cons_break_push(NULL, NULL);\n        core_->in_search = true;\n    }\n    ~CutterSearchLock()\n    {\n        rz_cons_break_pop();\n        core_->in_search = false;\n    }\n\nprivate:\n    RzCore *core_ = nullptr;\n};\n\nstatic QString cutterGetSearchHitData(RzCore *core, SearchKind kind, RzSearchHit *hit)\n{\n    QString data = \"\";\n    size_t dataSize = RZ_MAX(hit->size, 16);\n    std::vector<ut8> buffer(dataSize);\n\n    if (!rz_io_read_at_mapped(core->io, hit->address, buffer.data(), dataSize)) {\n        return \"\";\n    }\n\n    switch (kind) {\n    default:\n        // for some kinds, we don't do anything.\n        break;\n    case SearchKind::Value32BE:\n        /* fall-thru */\n    case SearchKind::Value32LE:\n        /* fall-thru */\n    case SearchKind::Value64BE:\n        /* fall-thru */\n    case SearchKind::Value64LE:\n        /* fall-thru */\n    case SearchKind::HexString:\n        /* fall-thru */\n    case SearchKind::CryptographicMaterial:\n        /* fall-thru */\n    case SearchKind::MagicSignature: {\n        data = fromOwnedCharPtr(rz_hex_bin2strdup(buffer.data(), dataSize));\n        break;\n    }\n    case SearchKind::String:\n        /* fall-thru */\n    case SearchKind::StringCaseInsensitive:\n        /* fall-thru */\n    case SearchKind::StringRegexExtended: {\n        RzStrStringifyOpt sopt;\n        RzStrEnc encoding = RZ_STRING_ENC_GUESS;\n        QString enc = hit->hit_desc;\n        enc = enc.section(\".\", 1, 1);\n        if (!enc.isEmpty()) {\n            encoding = rz_str_enc_string_as_type(enc.toUtf8().constData());\n        }\n\n        if (encoding == RZ_STRING_ENC_GUESS) {\n            encoding = rz_str_guess_encoding_from_buffer(buffer.data(), dataSize);\n        }\n\n        sopt.buffer = buffer.data();\n        sopt.length = dataSize;\n        sopt.encoding = encoding;\n        sopt.wrap_at = 0;\n        sopt.escape_nl = false;\n        sopt.json = false;\n        sopt.stop_at_nil = true;\n        sopt.stop_at_unprintable = false;\n        sopt.urlencode = false;\n\n        data = fromOwnedCharPtr(rz_str_stringify_raw_buffer(&sopt, nullptr));\n        break;\n    }\n    }\n\n    return data;\n}\n\nstatic QString cutterValueAsHex(QString strVal, bool bigEndian, size_t size)\n{\n    ut64 value = rz_num_math(nullptr, strVal.toUtf8().constData());\n    ut8 buffer[sizeof(ut64)] = { 0 };\n    char output[64] = { 0 };\n    rz_write_ble(buffer, value, bigEndian, size);\n    rz_hex_bin2str(buffer, size == 32 ? 4 : 8, output);\n    return output;\n}\n\nQList<SearchDescription> CutterCore::getAllSearch(QString searchFor, SearchKind kind, QString in)\n{\n    if (searchFor.isEmpty() && kind != SearchKind::CryptographicMaterial\n        && kind != SearchKind::MagicSignature) {\n        return {};\n    }\n\n    // call the oldsyle command for this search.\n    // this must be done before CORE_LOCK() to avoid deadlocks.\n    switch (kind) {\n    case SearchKind::AsmCode:\n        /* fall-thru */\n    case SearchKind::ROPGadgets:\n        /* fall-thru */\n    case SearchKind::ROPGadgetsRegex:\n        // old style search\n        return getAllSearchCommand(searchFor, kind, in);\n    default:\n        // use C API\n        break;\n    }\n\n    CORE_LOCK();\n\n    if (core->in_search) {\n        // this is impossible to happen, unless the user runs\n        // search via terminal before calling the Qt UI\n        RZ_LOG_ERROR(\"cutter: recursive search detected, search aborted.\\n\");\n        return {};\n    }\n\n    // this takes ownership of the search lock\n    CutterSearchLock searchLock(core);\n\n    TempConfig cfg;\n    cfg.set(\"search.in\", in);\n\n    QList<SearchDescription> searchRef;\n    RzList /*<RzSearchHit *>*/ *hits = nullptr;\n    auto userOpts = cutterSetupSearchOptions(core);\n    if (!userOpts) {\n        return searchRef;\n    }\n\n    switch (kind) {\n    default:\n        qWarning() << tr(\"Error invalid search kind\\n\");\n        return searchRef;\n    case SearchKind::HexString: {\n        RzSearchBytesPattern *pattern =\n                rz_search_parse_byte_pattern(searchFor.toUtf8().constData(), \"bytes\");\n        if (!pattern) {\n            return searchRef;\n        }\n        hits = rz_core_search_bytes(core, userOpts.get(), pattern);\n        break;\n    }\n    case SearchKind::Value32BE: {\n        searchFor = cutterValueAsHex(searchFor, true, 32);\n        RzSearchBytesPattern *pattern =\n                rz_search_parse_byte_pattern(searchFor.toUtf8().constData(), \"value32.be\");\n        if (!pattern) {\n            return searchRef;\n        }\n        hits = rz_core_search_bytes(core, userOpts.get(), pattern);\n        break;\n    }\n    case SearchKind::Value32LE: {\n        searchFor = cutterValueAsHex(searchFor, false, 32);\n        RzSearchBytesPattern *pattern =\n                rz_search_parse_byte_pattern(searchFor.toUtf8().constData(), \"value32.le\");\n        if (!pattern) {\n            return searchRef;\n        }\n        hits = rz_core_search_bytes(core, userOpts.get(), pattern);\n        break;\n    }\n    case SearchKind::Value64BE: {\n        searchFor = cutterValueAsHex(searchFor, true, 64);\n        RzSearchBytesPattern *pattern =\n                rz_search_parse_byte_pattern(searchFor.toUtf8().constData(), \"value64.be\");\n        if (!pattern) {\n            return searchRef;\n        }\n        hits = rz_core_search_bytes(core, userOpts.get(), pattern);\n        break;\n    }\n    case SearchKind::Value64LE: {\n        searchFor = cutterValueAsHex(searchFor, false, 64);\n        RzSearchBytesPattern *pattern =\n                rz_search_parse_byte_pattern(searchFor.toUtf8().constData(), \"value64.le\");\n        if (!pattern) {\n            return searchRef;\n        }\n        hits = rz_core_search_bytes(core, userOpts.get(), pattern);\n        break;\n    }\n    case SearchKind::String:\n    case SearchKind::StringCaseInsensitive: {\n        const auto str = searchFor.toUtf8();\n        hits = rz_core_search_string(core, userOpts.get(), str.constData(), str.size(),\n                                     kind == SearchKind::StringCaseInsensitive\n                                             ? RZ_REGEX_CASELESS | RZ_REGEX_LITERAL\n                                             : RZ_REGEX_LITERAL,\n                                     RZ_STRING_ENC_GUESS);\n        break;\n    }\n    case SearchKind::StringRegexExtended:\n        hits = rz_core_search_string(core, userOpts.get(), searchFor.toUtf8().constData(), 0,\n                                     RZ_REGEX_EXTENDED, RZ_STRING_ENC_GUESS);\n        break;\n    case SearchKind::CryptographicMaterial:\n        hits = rz_core_search_cryptographic_material(core, userOpts.get(),\n                                                     RZ_SEARCH_COLLECTION_CRYPTOGRAPHIC_ALL);\n        break;\n    case SearchKind::MagicSignature:\n        hits = rz_core_search_magic(core, userOpts.get(), nullptr);\n        break;\n    }\n\n    RzListIter *it;\n    RzSearchHit *hit;\n    CutterRzListForeach (hits, it, RzSearchHit, hit) {\n        SearchDescription exp;\n        QString detail = fromOwnedCharPtr(rz_search_hit_detail_as_string(hit));\n\n        exp.offset = hit->address;\n        exp.size = hit->size;\n        exp.data = cutterGetSearchHitData(core, kind, hit).trimmed();\n        exp.detail = hit->hit_desc;\n        if (!detail.isEmpty()) {\n            exp.detail += \" (\" + detail + \")\";\n        }\n        exp.detail += \" \";\n        exp.detail += rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, exp.offset);\n        exp.detail = exp.detail.trimmed();\n\n        searchRef << exp;\n    }\n\n    rz_list_free(hits);\n    return searchRef;\n}\n\nQList<XrefDescription> CutterCore::collectXRefsForVariable(const QString &variableName, RVA offset,\n                                                           int accessTypeMask, bool stopAtFirst)\n{\n    CORE_LOCK();\n    auto fcn = functionIn(offset);\n    if (!fcn) {\n        return {};\n    }\n\n    QList<XrefDescription> xrefs;\n    for (const auto &v : CutterPVector<RzAnalysisVar>(&fcn->vars)) {\n        if (variableName != v->name) {\n            continue;\n        }\n        RzAnalysisVarAccess *acc;\n        CutterRzVectorForeach(&v->accesses, acc, RzAnalysisVarAccess)\n        {\n            if (!(acc->type & accessTypeMask)) {\n                continue;\n            }\n            XrefDescription xref;\n            RVA addr = fcn->addr + acc->offset;\n            xref.from = addr;\n            xref.to = addr;\n\n            if (acc->type & RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE) {\n                xref.from_str = RzAddressString(addr);\n            } else {\n                xref.to_str = RzAddressString(addr);\n            }\n\n            xrefs << xref;\n            if (stopAtFirst) {\n                return xrefs;\n            }\n        }\n    }\n    return xrefs;\n}\n\nQList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites,\n                                                       RVA offset)\n{\n    ut8 mask = findWrites ? RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE : RZ_ANALYSIS_VAR_ACCESS_TYPE_READ;\n    return collectXRefsForVariable(variableName, offset, mask, false);\n}\n\nXrefDescription CutterCore::getFirstXRefForVariable(const QString &variableName, RVA offset)\n{\n    ut8 mask = RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE | RZ_ANALYSIS_VAR_ACCESS_TYPE_READ;\n    auto result = collectXRefsForVariable(variableName, offset, mask, true);\n\n    return result.isEmpty() ? XrefDescription() : result.first();\n}\n\nQList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function,\n                                            const QString &filterType)\n{\n    QList<XrefDescription> xrefList = QList<XrefDescription>();\n\n    RzList *xrefs = nullptr;\n    CORE_LOCK();\n\n    if (to) {\n        xrefs = rz_analysis_xrefs_get_to(core->analysis, addr);\n    } else {\n        xrefs = rz_analysis_xrefs_get_from(core->analysis, addr);\n    }\n\n    RzListIter *it;\n    RzAnalysisXRef *xref;\n    CutterRzListForeach (xrefs, it, RzAnalysisXRef, xref) {\n        XrefDescription xd;\n        xd.from = xref->from;\n        xd.to = xref->to;\n        xd.type = rz_analysis_xrefs_type_tostring(xref->type);\n\n        if (!filterType.isNull() && filterType != xd.type)\n            continue;\n        if (!whole_function && !to && xd.from != addr) {\n            continue;\n        }\n\n        char *from = rz_core_addr_get_name_delta(core, xd.from);\n        if (from) {\n            xd.from_str = QString::fromUtf8(from);\n            free(from);\n        } else {\n            xd.from_str = RzAddressString(xd.from);\n        }\n\n        xd.to_str = Core()->flagAt(xd.to);\n\n        xrefList << xd;\n    }\n    rz_list_free(xrefs);\n    return xrefList;\n}\n\nQString CutterCore::getXRefCommentAt(RVA offset)\n{\n    CORE_LOCK();\n    QString commentStr;\n    char *comment = rz_core_get_xref_comment(core, offset);\n    if (comment) {\n        commentStr = QString::fromUtf8(comment);\n    }\n    free(comment);\n    return commentStr;\n}\n\nvoid CutterCore::addGlobalVariable(RVA offset, QString name, QString typ)\n{\n    name = sanitizeStringForCommand(name);\n    CORE_LOCK();\n    char *errmsg = NULL;\n    RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,\n                                                   typ.toStdString().c_str(), &errmsg);\n    if (errmsg) {\n        qWarning() << tr(\"Error parsing type: \\\"%1\\\" message: \").arg(typ) << errmsg;\n        free(errmsg);\n        return;\n    }\n    if (!rz_analysis_var_global_create(core->analysis, name.toStdString().c_str(), globType,\n                                       offset)) {\n        qWarning() << tr(\"Error creating global variable: \\\"%1\\\"\").arg(name);\n        return;\n    }\n\n    emit globalVarsChanged();\n}\n\nvoid CutterCore::modifyGlobalVariable(RVA offset, QString name, QString typ)\n{\n    name = sanitizeStringForCommand(name);\n    CORE_LOCK();\n    RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset);\n    if (!glob) {\n        return;\n    }\n    // Compare if the name is not the same - also rename it\n    if (name.compare(glob->name)) {\n        rz_analysis_var_global_rename(core->analysis, glob->name, name.toStdString().c_str());\n    }\n    char *errmsg = NULL;\n    RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser,\n                                                   typ.toStdString().c_str(), &errmsg);\n    if (errmsg) {\n        qWarning() << tr(\"Error parsing type: \\\"%1\\\" message: \").arg(typ) << errmsg;\n        free(errmsg);\n        return;\n    }\n    rz_analysis_var_global_set_type(glob, globType);\n\n    emit globalVarsChanged();\n}\n\nvoid CutterCore::delGlobalVariable(QString name)\n{\n    name = sanitizeStringForCommand(name);\n    CORE_LOCK();\n    rz_analysis_var_global_delete_byname(core->analysis, name.toStdString().c_str());\n\n    emit globalVarsChanged();\n}\n\nvoid CutterCore::delGlobalVariable(RVA offset)\n{\n    CORE_LOCK();\n    rz_analysis_var_global_delete_byaddr_at(core->analysis, offset);\n\n    emit globalVarsChanged();\n}\n\nQString CutterCore::getGlobalVariableType(QString name)\n{\n    name = sanitizeStringForCommand(name);\n    CORE_LOCK();\n    RzAnalysisVarGlobal *glob =\n            rz_analysis_var_global_get_byname(core->analysis, name.toStdString().c_str());\n    if (!glob) {\n        return QString(\"\");\n    }\n    const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);\n    if (!gtype) {\n        return QString(\"\");\n    }\n    return QString(gtype);\n}\n\nQString CutterCore::getGlobalVariableType(RVA offset)\n{\n    CORE_LOCK();\n    RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byaddr_at(core->analysis, offset);\n    if (!glob) {\n        return QString(\"\");\n    }\n    const char *gtype = rz_type_as_string(core->analysis->typedb, glob->type);\n    if (!gtype) {\n        return QString(\"\");\n    }\n    return QString(gtype);\n}\n\nvoid CutterCore::addFlag(RVA offset, QString name, RVA size)\n{\n    name = sanitizeStringForCommand(name);\n    CORE_LOCK();\n    rz_flag_set(core->flags, name.toStdString().c_str(), offset, size);\n    emit flagsChanged();\n}\n\n/**\n * @brief Gets all the flags present at a specific address\n * @param addr The address to be checked\n * @return String containing all the flags which are comma-separated\n */\nQString CutterCore::listFlagsAsStringAt(RVA addr)\n{\n    CORE_LOCK();\n    char *flagList = rz_flag_get_liststr(core->flags, addr);\n    QString result = fromOwnedCharPtr(flagList);\n    return result;\n}\n\nQString CutterCore::nearestFlag(RVA offset, RVA *flagOffsetOut)\n{\n    CORE_LOCK();\n    auto r = rz_flag_get_at(core->flags, offset, true);\n    if (!r) {\n        return {};\n    }\n    if (flagOffsetOut) {\n        *flagOffsetOut = r->offset;\n    }\n    return r->name;\n}\n\nvoid CutterCore::addMark(RVA from, RVA to, QString name, QString comment, QColor color)\n{\n    CORE_LOCK();\n    auto m = rz_mark_set(core->marks, name.toStdString().c_str(), from, to);\n    if (m) {\n        rz_mark_item_set_comment(m, comment.toStdString().c_str());\n        rz_mark_item_set_color(m, color.name().toStdString().c_str());\n    }\n    emit marksChanged();\n}\n\nvoid CutterCore::delMark(const QString &name)\n{\n    CORE_LOCK();\n    auto m = rz_mark_get(core->marks, name.toStdString().c_str());\n    if (m) {\n        rz_mark_unset(core->marks, m);\n    }\n    emit marksChanged();\n}\n\nQList<MarkDescription> CutterCore::convertMarks(RzList *marks)\n{\n    QList<MarkDescription> markList;\n\n    RzListIter *it;\n    RzMarkItem *mark;\n    CutterRzListForeach (marks, it, RzMarkItem, mark) {\n        MarkDescription desc;\n        desc.from = mark->from;\n        desc.to = mark->to;\n        desc.name = mark->name;\n        desc.realname = mark->realname;\n        desc.comment = mark->comment;\n        desc.color = mark->color ? QColor(mark->color) : QColor(Qt::black);\n\n        markList.append(desc);\n    }\n    rz_list_free(marks);\n    return markList;\n}\n\nQList<MarkDescription> CutterCore::getMarks()\n{\n    CORE_LOCK();\n    return convertMarks(rz_mark_all_list(core->marks));\n}\n\nQList<MarkDescription> CutterCore::getMarksAt(RVA addr)\n{\n    CORE_LOCK();\n    return convertMarks(rz_mark_get_all_off(core->marks, addr));\n}\n\nQColor CutterCore::getBlendedMarksColorAt(RVA addr)\n{\n    const auto &marks = getMarksAt(addr);\n    double r = 0, g = 0, b = 0, a = 0;\n    bool first = true;\n\n    // Iterate in reverse because the oldest/first mark is at the end\n    for (auto it = marks.crbegin(); it != marks.crend(); ++it) {\n        QColor c = it->color;\n        if (!c.isValid()) {\n            continue;\n        }\n\n        double cr = c.redF(), cg = c.greenF(), cb = c.blueF();\n        if (first) {\n            r = cr, g = cg, b = cb, a = MARK_ALPHA_F;\n            first = false;\n        } else {\n            double a_out = MARK_ALPHA_F + a * (1.0 - MARK_ALPHA_F);\n            r = (cr * MARK_ALPHA_F + r * a * (1.0 - MARK_ALPHA_F)) / a_out;\n            g = (cg * MARK_ALPHA_F + g * a * (1.0 - MARK_ALPHA_F)) / a_out;\n            b = (cb * MARK_ALPHA_F + b * a * (1.0 - MARK_ALPHA_F)) / a_out;\n            a = a_out;\n        }\n    }\n    return first ? QColor() : QColor::fromRgbF(r, g, b, a);\n}\n\nvoid CutterCore::handleREvent(int type, void *data)\n{\n    switch (type) {\n    case RZ_EVENT_CLASS_NEW: {\n        auto ev = reinterpret_cast<RzEventClass *>(data);\n        emit classNew(QString::fromUtf8(ev->name));\n        break;\n    }\n    case RZ_EVENT_CLASS_DEL: {\n        auto ev = reinterpret_cast<RzEventClass *>(data);\n        emit classDeleted(QString::fromUtf8(ev->name));\n        break;\n    }\n    case RZ_EVENT_CLASS_RENAME: {\n        auto ev = reinterpret_cast<RzEventClassRename *>(data);\n        emit classRenamed(QString::fromUtf8(ev->name_old), QString::fromUtf8(ev->name_new));\n        break;\n    }\n    case RZ_EVENT_CLASS_ATTR_SET: {\n        auto ev = reinterpret_cast<RzEventClassAttrSet *>(data);\n        emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));\n        break;\n    }\n    case RZ_EVENT_CLASS_ATTR_DEL: {\n        auto ev = reinterpret_cast<RzEventClassAttr *>(data);\n        emit classAttrsChanged(QString::fromUtf8(ev->class_name));\n        break;\n    }\n    case RZ_EVENT_CLASS_ATTR_RENAME: {\n        auto ev = reinterpret_cast<RzEventClassAttrRename *>(data);\n        emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));\n        break;\n    }\n    case RZ_EVENT_DEBUG_PROCESS_FINISHED: {\n        auto ev = reinterpret_cast<RzEventDebugProcessFinished *>(data);\n        emit debugProcessFinished(ev->pid);\n        break;\n    }\n    default:\n        break;\n    }\n}\n\nvoid CutterCore::triggerFlagsChanged()\n{\n    emit flagsChanged();\n}\n\nvoid CutterCore::triggerVarsChanged()\n{\n    emit varsChanged();\n}\n\nvoid CutterCore::triggerFunctionRenamed(const RVA offset, const QString &newName)\n{\n    emit functionRenamed(offset, newName);\n}\n\nvoid CutterCore::loadPDB(const QString &file)\n{\n    CORE_LOCK();\n    rz_core_bin_pdb_load(core, file.toUtf8().constData());\n}\n\nQList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)\n{\n    CORE_LOCK();\n    auto vec = fromOwned(\n            rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));\n    if (!vec) {\n        return {};\n    }\n\n    RzCoreDisasmOptions options = {};\n    options.cbytes = 1;\n    options.vec = vec.get();\n    {\n        auto restoreSeek = seekTemp(offset);\n        if (rz_cons_singleton()->is_html) {\n            rz_cons_singleton()->is_html = false;\n            rz_cons_singleton()->was_html = true;\n        }\n        rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, NULL, &options);\n    }\n\n    QList<DisassemblyLine> r;\n    for (const auto &t : CutterPVector<RzAnalysisDisasmText>(vec.get())) {\n        QString text = t->text;\n        QStringList tokens = text.split('\\n');\n        // text might contain multiple lines\n        // so we split them and keep only one\n        // arrow/jump to addr.\n        for (const auto &tok : tokens) {\n            DisassemblyLine line;\n            line.offset = t->offset;\n            line.text = ansiEscapeToHtml(tok);\n            line.arrow = t->arrow;\n            r << line;\n            // only the first one.\n            t->arrow = RVA_INVALID;\n        }\n    }\n    return r;\n}\n\n/**\n * @brief return hexdump of <size> from an <offset> by a given formats\n * @param address - the address from which to print the hexdump\n * @param size - number of bytes to print\n * @param format - the type of hexdump (qwords, words. decimal, etc)\n */\nQString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)\n{\n    CORE_LOCK();\n    char *res = nullptr;\n    switch (format) {\n    case HexdumpFormats::Normal:\n        res = rz_core_print_hexdump_or_hexdiff_str(core, RZ_OUTPUT_MODE_STANDARD, address, size,\n                                                   false);\n        break;\n    case HexdumpFormats::Half:\n        res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 2, size,\n                                     RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);\n        break;\n    case HexdumpFormats::Word:\n        res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 4, size,\n                                     RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);\n        break;\n    case HexdumpFormats::Quad:\n        res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 8, size,\n                                     RZ_CORE_PRINT_FORMAT_TYPE_HEXADECIMAL);\n        break;\n    case HexdumpFormats::Signed:\n        res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 1, size,\n                                     RZ_CORE_PRINT_FORMAT_TYPE_INTEGER);\n        break;\n    case HexdumpFormats::Octal:\n        res = rz_core_print_dump_str(core, RZ_OUTPUT_MODE_STANDARD, address, 1, size,\n                                     RZ_CORE_PRINT_FORMAT_TYPE_OCTAL);\n        break;\n    }\n\n    return fromOwnedCharPtr(res);\n}\n\nQByteArray CutterCore::hexStringToBytes(const QString &hex)\n{\n    QByteArray hexChars = hex.toUtf8();\n    QByteArray bytes;\n    bytes.reserve(hexChars.length() / 2);\n    int size = rz_hex_str2bin(hexChars.constData(), reinterpret_cast<ut8 *>(bytes.data()));\n    bytes.resize(size);\n    return bytes;\n}\n\nQString CutterCore::bytesToHexString(const QByteArray &bytes)\n{\n    return QString::fromUtf8(bytes.toHex());\n}\n\nvoid CutterCore::loadScript(const QString &scriptname)\n{\n    {\n        CORE_LOCK();\n        rz_core_cmd_file(core, scriptname.toUtf8().constData());\n        rz_cons_flush();\n    }\n    triggerRefreshAll();\n}\n\nQString CutterCore::getRizinVersionReadable(const char *program)\n{\n    return fromOwnedCharPtr(rz_version_str(core_->sys_path, program));\n}\n\nQString CutterCore::getVersionInformation()\n{\n    int i;\n    QString versionInfo;\n    struct vcs_t\n    {\n        const char *name;\n        const char *(*callback)();\n    } vcs[] = {\n        { \"rz_arch\", &rz_arch_version },\n        { \"rz_lib\", &rz_lib_version },\n        { \"rz_egg\", &rz_egg_version },\n        { \"rz_bin\", &rz_bin_version },\n        { \"rz_cons\", &rz_cons_version },\n        { \"rz_flag\", &rz_flag_version },\n        { \"rz_core\", &rz_core_version },\n        { \"rz_crypto\", &rz_crypto_version },\n        { \"rz_debug\", &rz_debug_version },\n        { \"rz_hash\", &rz_hash_version },\n        { \"rz_io\", &rz_io_version },\n#if !USE_LIB_MAGIC\n        { \"rz_magic\", &rz_magic_version },\n#endif\n        { \"rz_reg\", &rz_reg_version },\n        { \"rz_sign\", &rz_sign_version },\n        { \"rz_search\", &rz_search_version },\n        { \"rz_syscall\", &rz_syscall_version },\n        { \"rz_util\", &rz_util_version },\n        /* ... */\n        { NULL, NULL }\n    };\n    versionInfo.append(getRizinVersionReadable());\n    versionInfo.append(\"\\n\");\n    for (i = 0; vcs[i].name; i++) {\n        struct vcs_t *v = &vcs[i];\n        const char *name = v->callback();\n        versionInfo.append(QString(\"%1 %2\\n\").arg(name, v->name));\n    }\n    return versionInfo;\n}\n\nQStringList CutterCore::getColorThemes()\n{\n    QStringList r;\n    CORE_LOCK();\n    RzPVector *themes_list = rz_core_get_themes(core);\n    if (!themes_list) {\n        return r;\n    }\n    for (const auto &th : CutterPVector<char>(themes_list)) {\n        r << fromOwnedCharPtr(rz_str_trim_dup(th));\n    }\n    rz_pvector_free(themes_list);\n    return r;\n}\n\nQHash<QString, QColor> CutterCore::getTheme()\n{\n    QHash<QString, QColor> theme;\n    for (int i = 0;; ++i) {\n        const char *k = rz_cons_pal_get_name(i);\n        if (!k) {\n            break;\n        }\n        RzColor color = rz_cons_pal_get_i(i);\n        theme.insert(k, QColor(color.r, color.g, color.b));\n    }\n    return theme;\n}\n\nQStringList CutterCore::getThemeKeys()\n{\n    QStringList stringList;\n    for (int i = 0;; ++i) {\n        const char *k = rz_cons_pal_get_name(i);\n        if (!k) {\n            break;\n        }\n        stringList << k;\n    }\n    return stringList;\n}\n\nbool CutterCore::setColor(const QString &key, const QString &color)\n{\n    if (!rz_cons_pal_set(key.toUtf8().constData(), color.toUtf8().constData())) {\n        return false;\n    }\n    rz_cons_pal_update_event();\n    return true;\n}\n\nQString CutterCore::ansiEscapeToHtml(const QString &text)\n{\n    int len;\n    QString r = text;\n    r.replace(\"\\t\", \"        \");\n    char *html = rz_cons_html_filter(r.toUtf8().constData(), &len);\n    if (!html) {\n        return {};\n    }\n    r = QString::fromUtf8(html, len);\n    rz_mem_free(html);\n    return r;\n}\n\nBasicBlockHighlighter *CutterCore::getBBHighlighter()\n{\n    return bbHighlighter;\n}\n\nBasicInstructionHighlighter *CutterCore::getBIHighlighter()\n{\n    return &biHighlighter;\n}\n\nvoid CutterCore::setIOCache(bool enabled)\n{\n    if (enabled) {\n        // disable write mode when cache is enabled\n        setWriteMode(false);\n    }\n    setConfig(\"io.cache\", enabled);\n    this->iocache = enabled;\n\n    emit ioCacheChanged(enabled);\n    emit ioModeChanged();\n}\n\nbool CutterCore::isIOCacheEnabled() const\n{\n    return iocache;\n}\n\nvoid CutterCore::commitWriteCache()\n{\n    CORE_LOCK();\n    // Temporarily disable cache mode\n    TempConfig tempConfig;\n    tempConfig.set(\"io.cache\", false);\n    auto desc = core->io->desc;\n    bool reopen = !isWriteModeEnabled() && desc;\n    if (reopen) {\n        rz_core_io_file_reopen(core, desc->fd, RZ_PERM_RW);\n    }\n    rz_io_cache_commit(core->io, 0, UT64_MAX);\n    rz_core_block_read(core);\n    if (reopen) {\n        rz_core_io_file_open(core, desc->fd);\n    }\n}\n\nvoid CutterCore::resetWriteCache()\n{\n    CORE_LOCK();\n    rz_io_cache_reset(core->io, core->io->cached);\n}\n\n// Enable or disable write-mode. Avoid unecessary changes if not need.\nvoid CutterCore::setWriteMode(bool enabled)\n{\n    bool writeModeState = isWriteModeEnabled();\n\n    if (writeModeState == enabled && !this->iocache) {\n        // New mode is the same as current and IO Cache is disabled. Do nothing.\n        return;\n    }\n\n    CORE_LOCK();\n    // Change from read-only to write-mode\n    RzIODesc *desc = core->io->desc;\n    if (desc) {\n        if (enabled) {\n            if (!writeModeState) {\n                rz_core_io_file_reopen(core, desc->fd, RZ_PERM_RW);\n            }\n        } else {\n            // Change from write-mode to read-only\n            rz_core_io_file_open(core, desc->fd);\n        }\n    }\n    // Disable cache mode because we specifically set write or\n    // read-only modes.\n    if (this->iocache) {\n        setIOCache(false);\n    }\n    emit writeModeChanged(enabled);\n    emit ioModeChanged();\n}\n\nbool CutterCore::isWriteModeEnabled()\n{\n    CORE_LOCK();\n    RzListIter *it;\n    RzCoreFile *cf;\n    CutterRzListForeach (core->files, it, RzCoreFile, cf) {\n        RzIODesc *desc = rz_io_desc_get(core->io, cf->fd);\n        if (!desc) {\n            continue;\n        }\n        if (desc->perm & RZ_PERM_W) {\n            return true;\n        }\n    }\n    return false;\n}\n\n/**\n * @brief get a compact disassembly preview for tooltips\n * @param address - the address from which to print the disassembly\n * @param num_of_lines - number of instructions to print\n */\nQStringList CutterCore::getDisassemblyPreview(RVA address, int num_of_lines)\n{\n    QList<DisassemblyLine> disassemblyLines;\n    {\n        // temporarily simplify the disasm output to get it colorful and simple to read\n        TempConfig tempConfig;\n        tempConfig.set(\"scr.color\", COLOR_MODE_16M)\n                .set(\"asm.lines\", false)\n                .set(\"asm.var\", false)\n                .set(\"asm.comments\", false)\n                .set(\"asm.bytes\", false)\n                .set(\"asm.lines.fcn\", false)\n                .set(\"asm.lines.out\", false)\n                .set(\"asm.lines.bb\", false)\n                .set(\"asm.bb.line\", false);\n\n        disassemblyLines = disassembleLines(address, num_of_lines + 1);\n    }\n    QStringList disasmPreview;\n    for (const DisassemblyLine &line : disassemblyLines) {\n        disasmPreview << line.text;\n        if (disasmPreview.length() >= num_of_lines) {\n            disasmPreview << \"...\";\n            break;\n        }\n    }\n    if (!disasmPreview.isEmpty()) {\n        return disasmPreview;\n    } else {\n        return QStringList();\n    }\n}\n\n/**\n * @brief get a compact hexdump preview for tooltips\n * @param address - the address from which to print the hexdump\n * @param size - number of bytes to print\n */\nQString CutterCore::getHexdumpPreview(RVA address, int size)\n{\n    // temporarily simplify the disasm output to get it colorful and simple to read\n    TempConfig tempConfig;\n    tempConfig.set(\"scr.color\", COLOR_MODE_16M)\n            .set(\"asm.offset\", true)\n            .set(\"hex.header\", false)\n            .set(\"hex.cols\", 16);\n    return ansiEscapeToHtml(hexdump(address, size, HexdumpFormats::Normal))\n            .replace(QLatin1Char('\\n'), \"<br>\");\n}\n\nQByteArray CutterCore::ioRead(RVA addr, int len)\n{\n    CORE_LOCK();\n\n    QByteArray array;\n\n    if (len <= 0)\n        return array;\n\n    /* Zero-copy */\n    array.resize(len);\n    if (!rz_io_read_at_mapped(core->io, addr, (uint8_t *)array.data(), len)) {\n        array.fill(0xff);\n    }\n\n    return array;\n}\n\nQStringList CutterCore::getConfigVariableSpaces(const QString &key)\n{\n    CORE_LOCK();\n    RzList *list = rz_core_config_in_space(core, key.toUtf8().constData());\n    if (!list) {\n        return {};\n    }\n\n    QStringList stringList;\n    for (const auto &x : CutterRzList<char>(list)) {\n        stringList << x;\n    }\n    rz_list_free(list);\n    return stringList;\n}\n\nchar *CutterCore::getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address)\n{\n    CORE_LOCK();\n    char *string = nullptr;\n    RzGraph *graph = rz_core_graph(core, type, address);\n    if (!graph) {\n        if (address == RVA_INVALID) {\n            qWarning() << tr(\"Cannot get global graph\");\n        } else {\n            qWarning() << tr(\"Cannot get graph at \") << RzAddressString(address);\n        }\n        return nullptr;\n    }\n    core->graph->is_callgraph = type == RZ_CORE_GRAPH_TYPE_FUNCALL;\n\n    switch (format) {\n    case RZ_CORE_GRAPH_FORMAT_CMD: {\n        string = rz_graph_drawable_to_cmd(graph);\n        break;\n    }\n    case RZ_CORE_GRAPH_FORMAT_DOT: {\n        string = rz_core_graph_to_dot_str(core, graph);\n        break;\n    }\n    case RZ_CORE_GRAPH_FORMAT_JSON:\n        /* fall-thru */\n    case RZ_CORE_GRAPH_FORMAT_JSON_DISASM: {\n        string = rz_graph_drawable_to_json_str(graph, true);\n        break;\n    }\n    case RZ_CORE_GRAPH_FORMAT_GML: {\n        string = rz_graph_drawable_to_gml(graph);\n        break;\n    }\n    default:\n        break;\n    }\n    rz_graph_free(graph);\n\n    if (!string) {\n        qWarning() << tr(\"Failed to generate graph\");\n    }\n\n    return string;\n}\n\nvoid CutterCore::writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type,\n                                          RVA address)\n{\n    TempConfig tempConfig;\n    tempConfig.set(\"scr.color\", false);\n    tempConfig.set(\"graph.gv.format\", format);\n\n    CORE_LOCK();\n    auto filepath = path.toUtf8();\n\n    if (!rz_core_graph_write(core, address, type, filepath)) {\n        if (address == RVA_INVALID) {\n            qWarning() << tr(\"Cannot get global graph\");\n        } else {\n            qWarning() << tr(\"Cannot get graph at \") << RzAddressString(address);\n        }\n    }\n}\n\nvoid CutterCore::showTypeInTypesWidget(const QString &typeName)\n{\n    emit showTypeRequested(typeName);\n}\n"
  },
  {
    "path": "src/core/Cutter.h",
    "content": "#ifndef CUTTER_H\n#define CUTTER_H\n\n#include \"core/CutterCommon.h\"\n#include \"core/CutterDescriptions.h\"\n#include \"core/CutterJson.h\"\n#include \"core/Basefind.h\"\n#include \"common/BasicInstructionHighlighter.h\"\n\n#include <QMap>\n#include <QMenu>\n#include <QDebug>\n#include <QObject>\n#include <QSharedPointer>\n#include <QStringList>\n#include <QMessageBox>\n#include <QErrorMessage>\n#include <QMutex>\n#include <QDir>\n#include <functional>\n#include <memory>\n\nclass AsyncTaskManager;\nclass BasicInstructionHighlighter;\nclass CutterCore;\nclass Decompiler;\nclass RizinTask;\nclass RizinCmdTask;\nclass RizinFunctionTask;\nclass RizinTaskDialog;\n\n#include \"common/BasicBlockHighlighter.h\"\n#include \"common/Helpers.h\"\n\n#include <rz_project.h>\n#include <memory>\n\n#define Core() (CutterCore::instance())\n\nclass RzCoreLocked;\n\nstruct CUTTER_EXPORT AddrRefs\n{\n    RVA addr;\n    QString mapname;\n    QString section;\n    QString reg;\n    QString fcn;\n    QString type;\n    QString asm_op;\n    QString perms;\n    ut64 value;\n    bool has_value;\n    QString string;\n    QSharedPointer<AddrRefs> ref;\n};\n\nstruct CUTTER_EXPORT RegisterRef\n{\n    ut64 value;\n    AddrRefs ref;\n    QString name;\n};\n\nenum class SearchKind {\n    AsmCode,\n    HexString,\n    ROPGadgets,\n    ROPGadgetsRegex,\n    String,\n    StringCaseInsensitive,\n    StringRegexExtended,\n    Value32BE,\n    Value32LE,\n    Value64BE,\n    Value64LE,\n    CryptographicMaterial,\n    MagicSignature,\n};\n\nclass CUTTER_EXPORT CutterCore : public QObject\n{\n    Q_OBJECT\n\n    friend class RzCoreLocked;\n    friend class RizinTask;\n    friend class Basefind;\n\npublic:\n    explicit CutterCore(QObject *parent = nullptr);\n    ~CutterCore();\n    static CutterCore *instance();\n\n    void initialize(bool loadPlugins = true);\n    void loadCutterRC();\n    void loadDefaultCutterRC();\n    QDir getCutterRCDefaultDirectory() const;\n\n    AsyncTaskManager *getAsyncTaskManager() { return asyncTaskManager; }\n\n    RVA getOffset() const { return core_->offset; }\n\n    /* Core functions (commands) */\n    /* Almost the same as core_cmd_raw,\n     * only executes std::function<bool(RzCore *)> instead of char* */\n    QString getFunctionExecOut(const std::function<bool(RzCore *)> &fcn,\n                               const RVA addr = RVA_INVALID);\n    static QString sanitizeStringForCommand(QString s);\n    /**\n     * @brief send a command to Rizin\n     * @param str the command you want to execute\n     * @return command output\n     * @note if you want to seek to an address, you should use CutterCore::seek.\n     */\n    QString cmd(const char *str);\n    QString cmd(const QString &str) { return cmd(str.toUtf8().constData()); }\n\n    /**\n     * @brief send a task to Rizin\n     * @param fcn the task you want to execute\n     * @return execute successful?\n     */\n    bool asyncTask(std::function<void *(RzCore *)> fcn, QSharedPointer<RizinTask> &task);\n    void functionTask(std::function<void *(RzCore *)> fcn);\n\n    /**\n     * @brief Execute a Rizin command \\a cmd.  By nature, the API\n     * is executing raw commands, and thus ignores multiple commands and overcome command\n     * injections.\n     * @param cmd - a raw command to execute. Passing multiple commands (e.g \"px 5; pd 7 && pdf\")\n     * will result in them treated as arguments to first command.\n     * @return the output of the command\n     */\n    QString cmdRaw(const char *cmd);\n\n    /**\n     * @brief a wrapper around cmdRaw(const char *cmd,).\n     */\n    QString cmdRaw(const QString &cmd) { return cmdRaw(cmd.toUtf8().constData()); };\n\n    /**\n     * @brief Execute a Rizin command \\a cmd at \\a address. The function will preform a silent seek\n     * to the address without triggering the seekChanged event nor adding new entries to the seek\n     * history. By nature, the API is executing a single command without going through Rizin shell,\n     * and thus ignores multiple commands and tries to overcome command injections.\n     * @param cmd - a raw command to execute. If multiple commands will be passed (e.g \"px 5; pd 7\n     * && pdf\") then only the first command will be executed.\n     * @param address - an address to which Cutter will temporarily seek.\n     * @return the output of the command\n     */\n    QString cmdRawAt(const char *cmd, RVA address);\n\n    /**\n     * @brief a wrapper around cmdRawAt(const char *cmd, RVA address).\n     */\n    QString cmdRawAt(const QString &str, RVA address)\n    {\n        return cmdRawAt(str.toUtf8().constData(), address);\n    }\n\n    class SeekReturn\n    {\n        RVA returnAddress;\n        bool empty = true;\n\n    public:\n        SeekReturn(RVA returnAddress) : returnAddress(returnAddress), empty(false) {}\n        ~SeekReturn()\n        {\n            if (!empty) {\n                Core()->seekSilent(returnAddress);\n            }\n        }\n        SeekReturn(SeekReturn &&from)\n        {\n            if (this != &from) {\n                returnAddress = from.returnAddress;\n                empty = from.empty;\n                from.empty = true;\n            }\n        };\n    };\n\n    SeekReturn seekTemp(RVA address)\n    {\n        SeekReturn returner(getOffset());\n        seekSilent(address);\n        return returner;\n    }\n\n    enum class SeekHistoryType { New, Undo, Redo };\n\n    CutterJson cmdj(const char *str);\n    CutterJson cmdj(const QString &str) { return cmdj(str.toUtf8().constData()); }\n    QString cmdTask(const QString &str);\n\n    QString getRizinVersionReadable(const char *program = nullptr);\n    QString getVersionInformation();\n\n    CutterJson parseJson(const char *name, char *res, const char *cmd = nullptr);\n    CutterJson parseJson(const char *name, char *res, const QString &cmd = QString())\n    {\n        return parseJson(name, res, cmd.isNull() ? nullptr : cmd.toLocal8Bit().constData());\n    }\n\n    QStringList autocomplete(const QString &cmd, RzLinePromptType promptType);\n\n    /* Functions methods */\n    void renameFunction(const RVA offset, const QString &newName);\n    void delFunction(RVA addr);\n    void renameFlag(QString old_name, QString new_name);\n    /**\n     * @brief Renames the specified local variable in the function specified by the\n     * address given.\n     * @param newName Specifies the name to which the current name of the variable\n     * should be renamed.\n     * @param oldName Specifies the current name of the function variable.\n     * @param functionAddress Specifies the exact address of the function.\n     */\n    void renameFunctionVariable(QString newName, QString oldName, RVA functionAddress);\n\n    /**\n     * @param addr\n     * @return a function that contains addr or nullptr\n     */\n    RzAnalysisFunction *functionIn(ut64 addr);\n\n    /**\n     * @param addr\n     * @return the function that has its entrypoint at addr or nullptr\n     */\n    RzAnalysisFunction *functionAt(ut64 addr);\n\n    RVA getFunctionStart(RVA addr);\n    RVA getFunctionEnd(RVA addr);\n    RVA getLastFunctionInstruction(RVA addr);\n    QString flagAt(RVA addr, bool getClosestFlag = true);\n    void createFunctionAt(RVA addr);\n    void createFunctionAt(RVA addr, QString name);\n    QStringList getDisassemblyPreview(RVA address, int num_of_lines);\n\n    /* Flags */\n    void delFlag(RVA addr);\n    void delFlag(const QString &name);\n    void addFlag(RVA offset, QString name, RVA size);\n    QString listFlagsAsStringAt(RVA addr);\n    /**\n     * @brief Get nearest flag at or before offset.\n     * @param offset search position\n     * @param flagOffsetOut address of returned flag\n     * @return flag name\n     */\n    QString nearestFlag(RVA offset, RVA *flagOffsetOut);\n    void triggerFlagsChanged();\n\n    /* Marks */\n    void addMark(RVA from, RVA to, QString name, QString comment = {}, QColor color = {});\n    void delMark(const QString &name);\n    QList<MarkDescription> getMarks();\n    QList<MarkDescription> getMarksAt(RVA addr);\n    /**\n     * @brief Compute the blended color of all marks containing a specific address.\n     * @param addr address to query\n     * @return resulting blended color, or invalid QColor if no marks are present at\n     * the specified address\n     */\n    QColor getBlendedMarksColorAt(RVA addr);\n\n    /* Global Variables */\n    void addGlobalVariable(RVA offset, QString name, QString typ);\n    void delGlobalVariable(QString name);\n    void delGlobalVariable(RVA offset);\n    void modifyGlobalVariable(RVA offset, QString name, QString typ);\n    QString getGlobalVariableType(QString name);\n    QString getGlobalVariableType(RVA offset);\n\n    /* Edition functions */\n    CutterRzIter<RzAnalysisBytes> getRzAnalysisBytesSingle(RVA addr);\n    QString getInstructionBytes(RVA addr);\n    QString getInstructionOpcode(RVA addr);\n    void editInstruction(RVA addr, const QString &inst, bool fillWithNops = false);\n    void nopInstruction(RVA addr);\n    void jmpReverse(RVA addr);\n    void editBytes(RVA addr, const QString &inst);\n    void editBytesEndian(RVA addr, const QString &bytes);\n\n    /* Code/Data */\n    void setToCode(RVA addr);\n    enum class StringTypeFormats { None, ASCII_LATIN1, UTF8 };\n    /**\n     * @brief Adds string at address\n     * That function calls the 'Cs' command\n     * \\param addr The address of the array where the string will be applied\n     * \\param size The size of string\n     * \\param type The type of string\n     */\n    void setAsString(RVA addr, int size = 0, StringTypeFormats type = StringTypeFormats::None);\n    /**\n     * @brief Removes string at address\n     * That function calls the 'Cs-' command\n     * \\param addr The address of the array where the string will be applied\n     */\n    void removeString(RVA addr);\n    /**\n     * @brief Gets string at address\n     * That function correspond the 'Cs.' command\n     * \\param addr The address of the string\n     * @return string at requested address\n     */\n    QString getMetaString(RVA addr);\n    /**\n     * @brief Gets string at address\n     * That function calls the 'ps' command\n     * \\param addr The address of the first byte of the array\n     * @return string at requested address\n     */\n    QString getString(RVA addr);\n    QString getString(RVA addr, uint64_t len, RzStrEnc encoding, bool escape_nl = false);\n    void setToData(RVA addr, int size, int repeat = 1);\n    int sizeofDataMeta(RVA addr);\n\n    /* Comments */\n    void setComment(RVA addr, const QString &cmt);\n    void delComment(RVA addr);\n    QString getCommentAt(RVA addr);\n    void setImmediateBase(const QString &rzBaseName, RVA offset = RVA_INVALID);\n    void setCurrentBits(int bits, RVA offset = RVA_INVALID);\n\n    /**\n     * @brief Changes immediate displacement to structure offset\n     * This function makes use of the \"aht\" command of Rizin to apply structure\n     * offset to the immediate displacement used in the given instruction\n     * \\param structureOffset The name of struct which will be applied\n     * \\param offset The address of the instruction where the struct will be applied\n     */\n    void applyStructureOffset(const QString &structureOffset, RVA offset = RVA_INVALID);\n\n    /* Classes */\n    QList<QString> getAllAnalysisClasses(bool sorted);\n    QList<AnalysisMethodDescription> getAnalysisClassMethods(const QString &cls);\n    QList<AnalysisBaseClassDescription> getAnalysisClassBaseClasses(const QString &cls);\n    QList<AnalysisVTableDescription> getAnalysisClassVTables(const QString &cls);\n    void createNewClass(const QString &cls);\n    void renameClass(const QString &oldName, const QString &newName);\n    void deleteClass(const QString &cls);\n    bool getAnalysisMethod(const QString &cls, const QString &meth,\n                           AnalysisMethodDescription *desc);\n    void renameAnalysisMethod(const QString &className, const QString &oldMethodName,\n                              const QString &newMethodName);\n    void setAnalysisMethod(const QString &cls, const AnalysisMethodDescription &meth);\n\n    /* File related methods */\n    bool loadFile(QString path, ut64 baddr = 0LL, ut64 mapaddr = 0LL, int perms = RZ_PERM_R,\n                  int va = 0, bool loadbin = false, const QString &forceBinPlugin = QString());\n    bool tryFile(QString path, bool rw);\n    bool mapFile(QString path, RVA mapaddr);\n    void loadScript(const QString &scriptname);\n\n    /* Seek functions */\n    void seek(QString thing);\n    void seek(ut64 offset);\n    void seekSilent(ut64 offset);\n    void seekSilent(QString thing) { seekSilent(math(thing)); }\n    void seekPrev();\n    void seekNext();\n    void updateSeek(SeekHistoryType type = SeekHistoryType::New);\n    /**\n     * @brief Raise a memory widget showing current offset, prefer last active\n     * memory widget.\n     */\n    void showMemoryWidget();\n    /**\n     * @brief Seek to \\p offset and raise a memory widget showing it.\n     * @param offset\n     */\n    void seekAndShow(ut64 offset);\n    /**\n     * @brief \\see CutterCore::show(ut64)\n     * @param thing - addressable expression\n     */\n    void seekAndShow(QString thing);\n    RVA getOffset();\n    RVA prevOpAddr(RVA startAddr, int count);\n    RVA nextOpAddr(RVA startAddr, int count);\n\n    /* SigDB / Flirt functions */\n    void applySignature(const QString &filepath);\n    void createSignature(const QString &filepath);\n\n    /* Math functions */\n    ut64 math(const QString &expr);\n    ut64 num(const QString &expr);\n    QString itoa(ut64 num, int rdx = 16);\n\n    /* Config functions */\n    void setConfig(const char *k, const char *v);\n    void setConfig(const QString &k, const char *v);\n    void setConfig(const char *k, const QString &v);\n    void setConfig(const QString &k, const QString &v) { setConfig(k.toUtf8().constData(), v); }\n    void setConfig(const char *k, int v);\n    void setConfig(const QString &k, int v) { setConfig(k.toUtf8().constData(), v); }\n    void setConfig(const char *k, bool v);\n    void setConfig(const QString &k, bool v) { setConfig(k.toUtf8().constData(), v); }\n    void setConfig(const char *k, const QVariant &v);\n    void setConfig(const QString &k, const QVariant &v) { setConfig(k.toUtf8().constData(), v); }\n    int getConfigi(const char *k);\n    int getConfigi(const QString &k) { return getConfigi(k.toUtf8().constData()); }\n    ut64 getConfigut64(const char *k);\n    ut64 getConfigut64(const QString &k) { return getConfigut64(k.toUtf8().constData()); }\n    bool getConfigb(const char *k);\n    bool getConfigb(const QString &k) { return getConfigb(k.toUtf8().constData()); }\n    QString getConfig(const char *k);\n    QString getConfig(const QString &k) { return getConfig(k.toUtf8().constData()); }\n    QString getConfigDescription(const char *k);\n    QStringList getConfigOptions(const char *k);\n    QStringList getColorThemes();\n    QHash<QString, QColor> getTheme();\n    QStringList getThemeKeys();\n    bool setColor(const QString &key, const QString &color);\n    QStringList getConfigVariableSpaces(const QString &key = \"\");\n\n    /* Assembly\\Hexdump related methods */\n    QByteArray assemble(const QString &code);\n    QString disassemble(const QByteArray &data);\n    QString disassembleSingleInstruction(RVA addr);\n    QList<DisassemblyLine> disassembleLines(RVA offset, int lines);\n\n    static QByteArray hexStringToBytes(const QString &hex);\n    static QString bytesToHexString(const QByteArray &bytes);\n    enum class HexdumpFormats { Normal, Half, Word, Quad, Signed, Octal };\n    QString hexdump(RVA offset, int size, HexdumpFormats format);\n    QString getHexdumpPreview(RVA offset, int size);\n\n    void setCPU(QString arch, QString cpu, int bits);\n    void setEndianness(bool big);\n\n    /* SDB */\n    QList<QString> sdbList(QString path);\n    QList<QString> sdbListKeys(QString path);\n    QString sdbGet(QString path, QString key);\n    bool sdbSet(QString path, QString key, QString val);\n\n    /* Debug */\n    QString getRegisterName(QString registerRole);\n    RVA getProgramCounterValue();\n    void setRegister(QString regName, QString regValue);\n    void setCurrentDebugThread(int tid);\n    /**\n     * @brief Attach to a given pid from a debug session\n     */\n    void setCurrentDebugProcess(int pid);\n    /**\n     * @brief Returns a list of stack address and their telescoped references\n     * @param size number of bytes to scan\n     * @param depth telescoping depth\n     */\n    QList<AddrRefs> getStack(int size = 0x100, int depth = 6);\n    /**\n     * @brief Recursively dereferences pointers starting at the specified address\n     *        up to a given depth\n     * @param addr telescoping addr\n     * @param depth telescoping depth\n     */\n    AddrRefs getAddrRefs(RVA addr, int depth);\n    /**\n     * @brief return a RefDescription with a formatted ref string and configured colors\n     * @param ref the \"ref\" JSON node from getAddrRefs\n     */\n    RefDescription formatRefDesc(const QSharedPointer<AddrRefs> &ref);\n    /**\n     * @brief Get a list of a given process's threads\n     * @param pid The pid of the process, -1 for the currently debugged process\n     * @return List of ProcessDescription\n     */\n    QList<ThreadDescription> getProcessThreads(int pid = -1);\n    /**\n     * @brief Get a list of heap chunks\n     * Uses RZ_API rz_heap_chunks_list to get vector of chunks\n     * If arena_addr is zero return the chunks for main arena\n     * @param arena_addr base address for the arena\n     * @return Vector of heap chunks for the given arena\n     */\n    QVector<Chunk> getHeapChunks(RVA arena_addr);\n\n    /**\n     * @brief Get a list of heap arenas\n     * Uses RZ_API rz_heap_arenas_list to get list of arenas\n     * @return Vector of arenas\n     */\n    QVector<Arena> getArenas();\n\n    /**\n     * @brief Get detailed information about a heap chunk\n     * Uses RZ_API rz_heap_chunk\n     * @return RzHeapChunkSimple struct pointer for the heap chunk\n     */\n    RzHeapChunkSimple *getHeapChunk(ut64 addr);\n    /**\n     * @brief Get heap bins of an arena with given base address\n     * (including large, small, fast, unsorted, tcache)\n     * @param arena_addr Base address of the arena\n     * @return QVector of non empty RzHeapBin pointers\n     */\n    QVector<RzHeapBin *> getHeapBins(ut64 arena_addr);\n    /**\n     * @brief Write the given chunk header to memory\n     * @param chunkSimple RzHeapChunkSimple pointer of the chunk to be written\n     * @return true if the write succeeded else false\n     */\n    bool writeHeapChunk(RzHeapChunkSimple *chunkSimple);\n    int getArchBits();\n    void startDebug();\n    void startEmulation();\n    /**\n     * @brief attach to a remote debugger\n     * @param uri remote debugger uri\n     * @note attachedRemote(bool) signals the result\n     */\n    void attachRemote(const QString &uri);\n    void attachDebug(int pid);\n    void stopDebug();\n    void suspendDebug();\n    void syncAndSeekProgramCounter();\n    void continueDebug();\n    void continueBackDebug();\n    void continueUntilCall();\n    void continueUntilSyscall();\n    void continueUntilDebug(ut64 offset);\n    void stepDebug();\n    void stepOverDebug();\n    void stepOutDebug();\n    void stepBackDebug();\n\n    void startTraceSession();\n    void stopTraceSession();\n\n    void addBreakpoint(const BreakpointDescription &config);\n    void updateBreakpoint(int index, const BreakpointDescription &config);\n    void toggleBreakpoint(RVA addr);\n    void delBreakpoint(RVA addr);\n    void delAllBreakpoints();\n    void enableBreakpoint(RVA addr);\n    void disableBreakpoint(RVA addr);\n    /**\n     * @brief Enable or disable breakpoint tracing.\n     * @param index - breakpoint index to modify\n     * @param enabled - true if tracing should be enabled\n     */\n    void setBreakpointTrace(int index, bool enabled);\n    int breakpointIndexAt(RVA addr);\n    BreakpointDescription getBreakpointAt(RVA addr);\n\n    bool isBreakpoint(const QList<RVA> &breakpoints, RVA addr);\n    QList<RVA> getBreakpointsAddresses();\n\n    /**\n     * @brief Sets the RzRun profile directives by writing them to a file\n     * If a profile path is already set in 'dbg.profile', this method overwrites that file\n     * If no path is set, it creates a temporary file and updates 'dbg.profile' to point to it\n     * @param directives The raw string containing key=value profile directives\n     */\n    void setProfileDirectives(const QString &directives);\n\n    /**\n     * @brief Sets the register profile to the provided one\n     * @param profileData Raw profile string to be applied\n     */\n    void setRegisterProfile(const QString &profileData);\n\n    /**\n     * @brief Converts a GDB profile into Rizin format\n     * @param profilePath Path to the GDB profile\n     * @return Converted profile string\n     */\n    QString convertGDBProfile(const QString &profilePath);\n\n    /**\n     * @brief Retrieves the current register profile string\n     * @return The active register profile content\n     */\n    QString getRegisterProfile();\n\n    /**\n     * @brief Get all breakpoinst that are belong to a functions at this address\n     */\n    QList<RVA> getBreakpointsInFunction(RVA funcAddr);\n    QString getActiveDebugPlugin();\n    QStringList getDebugPlugins();\n    void setDebugPlugin(QString plugin);\n    bool isDebugTaskInProgress();\n    /**\n     * @brief Check if we can use output/input redirection with the currently debugged process\n     */\n    bool isRedirectableDebugee();\n    bool currentlyDebugging = false;\n    bool currentlyEmulating = false;\n    bool currentlyTracing = false;\n    bool currentlyRemoteDebugging = false;\n    int currentlyAttachedToPID = -1;\n    QString currentlyOpenFile;\n\n    /* Decompilers */\n    QList<Decompiler *> getDecompilers();\n    Decompiler *getDecompilerById(const QString &id);\n\n    /**\n     * Register a new decompiler\n     *\n     * The decompiler must have a unique id, otherwise this method will fail.\n     * The decompiler's parent will be set to this CutterCore instance, so it will automatically be\n     * freed later.\n     *\n     * @return whether the decompiler was registered successfully\n     */\n    bool registerDecompiler(Decompiler *decompiler);\n\n    RVA getOffsetJump(RVA addr);\n    CutterJson getSignatureInfo();\n    bool existsFileInfo();\n    void setGraphEmpty(bool empty);\n    bool isGraphEmpty();\n\n    void getRegs();\n    QList<QString> regs;\n    void setSettings();\n\n    void loadPDB(const QString &file);\n\n    QByteArray ioRead(RVA addr, int len);\n\n    QList<RVA> getSeekHistory();\n\n    /* Plugins */\n    QStringList getAsmPluginNames();\n    QStringList getAnalysisPluginNames();\n\n    /**\n     * @brief Checks if an assembler is available for the current architecture.\n     * @return true if there is an assembler plugin for the current architecture, false otherwise.\n     */\n    bool hasAssembler();\n\n    /* Widgets */\n    QList<RzBinPluginDescription> getBinPluginDescriptions(bool bin = true, bool xtr = true);\n    QList<RzIOPluginDescription> getRIOPluginDescriptions();\n    QList<RzCorePluginDescription> getRCorePluginDescriptions();\n    QList<RzAsmPluginDescription> getRAsmPluginDescriptions();\n    QList<FunctionDescription> getAllFunctions();\n    QList<ImportDescription> getAllImports();\n    QList<ExportDescription> getAllExports();\n    QList<SymbolDescription> getAllSymbols();\n    QList<HeaderDescription> getAllHeaders();\n    QList<GlobalDescription> getAllGlobals();\n    QList<FlirtDescription> getSignaturesDB();\n    QList<CommentDescription> getAllComments(const QString &filterType);\n    QList<RelocDescription> getAllRelocs();\n    QList<StringDescription> getAllStrings();\n    QList<FlagspaceDescription> getAllFlagspaces();\n    QList<FlagDescription> getAllFlags(QString flagspace = QString());\n    QList<SectionDescription> getAllSections();\n    QList<SegmentDescription> getAllSegments();\n    QList<EntrypointDescription> getAllEntrypoint();\n    QList<BinClassDescription> getAllClassesFromBin();\n    QList<BinClassDescription> getAllClassesFromFlags();\n    QList<ResourcesDescription> getAllResources();\n    QList<VTableDescription> getAllVTables();\n\n    /**\n     * @return all loaded types\n     */\n    QList<TypeDescription> getAllTypes();\n\n    /**\n     * @return all loaded primitive types\n     */\n    QList<TypeDescription> getAllPrimitiveTypes();\n\n    /**\n     * @return all loaded unions\n     */\n    QList<TypeDescription> getAllUnions();\n\n    /**\n     * @return all loaded structs\n     */\n    QList<TypeDescription> getAllStructs();\n\n    /**\n     * @return all loaded enums\n     */\n    QList<TypeDescription> getAllEnums();\n\n    /**\n     * @return all loaded typedefs\n     */\n    QList<TypeDescription> getAllTypedefs();\n\n    /**\n     * @brief Fetching the C representation of a given Type\n     * @param name - the name or the type of the given Type\n     * @return The type decleration as C output\n     */\n    QString getTypeAsC(QString name);\n\n    /**\n     * @brief Check if a type exists using its name\n     * @param typeName Name of the type to validate\n     * @return true if the type exists, false otherwise\n     */\n    bool typeExists(const QString &typeName);\n\n    /**\n     * @brief Highlight a specific type in the Types widget\n     * @param typeName The name of the type to be shown\n     */\n    void showTypeInTypesWidget(const QString &typeName);\n\n    /**\n     * @brief Checks if the given address is mapped to a region\n     * @param addr The address to be checked\n     * @return true if addr is mapped, false otherwise\n     */\n    bool isAddressMapped(RVA addr);\n\n    QList<MemoryMapDescription> getMemoryMap();\n    QList<SearchDescription> getAllSearch(QString searchFor, SearchKind kind, QString in);\n    QList<BreakpointDescription> getBreakpoints();\n    /**\n     * @brief Get list of processes attachable by debugger\n     *\n     * @param pid 0 - all processes, -1 - currently debugged process\n     * @return QList<ProcessDescription>\n     */\n    QList<ProcessDescription> getProcesses(int pid = 0);\n    /**\n     * @brief Get the right RzReg object based on the cutter state (debugging vs emulating)\n     */\n    RzReg *getReg();\n    /**\n     * @brief returns a list of reg values and their telescoped references\n     * @param depth telescoping depth\n     */\n    QList<RegisterRef> getRegisterRefs(int depth = 6);\n    QVector<RegisterRefValueDescription> getRegisterRefValues();\n    QList<VariableDescription> getVariables(RVA at);\n    /**\n     * @brief Fetches all the writes or reads to the specified local variable 'variableName'\n     * in the function in which the specified offset is a part of.\n     * @param variableName Name of the local variable.\n     * @param findWrites If this is true, then locations at which modification happen to the\n     * specified local variable is fetched. Else, the locations at which the local is variable is\n     * read is fetched.\n     * @param offset An offset in the function in which the specified local variable exist.\n     * @return A list of XrefDescriptions that contains details of all the writes or reads that\n     * happen to the variable 'variableName'.\n     */\n    QList<XrefDescription> getXRefsForVariable(QString variableName, bool findWrites, RVA offset);\n    QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function,\n                                    const QString &filterType = QString());\n    /**\n     * @brief Find the first read or write access to a local variable\n     * @param variableName Name of the local variable\n     * @param offset An address within the function containing the variable\n     * @return The first XrefDescription found, empty if none\n     */\n    XrefDescription getFirstXRefForVariable(const QString &variableName, RVA offset);\n\n    /**\n     * @brief Retrieves the auto-generated comment describing XRefs at a specific offset\n     * @return The XRef comment string, seperated by \\n if it spans multiple lines\n     */\n    QString getXRefCommentAt(RVA offset);\n\n    void handleREvent(int type, void *data);\n\n    /* Signals related */\n    void triggerVarsChanged();\n    void triggerFunctionRenamed(const RVA offset, const QString &newName);\n    void triggerRefreshAll();\n    void triggerAsmOptionsChanged();\n    void triggerGraphOptionsChanged();\n\n    void message(const QString &msg, bool debug = false);\n\n    QStringList getSectionList();\n\n    RzCoreLocked lock();\n    CUTTER_DEPRECATED(\"Use CutterCore::lock instead\")\n    RzCoreLocked core();\n\n    static QString ansiEscapeToHtml(const QString &text);\n    BasicBlockHighlighter *getBBHighlighter();\n    BasicInstructionHighlighter *getBIHighlighter();\n\n    /**\n     * @brief Enable or dsiable Cache mode. Cache mode is used to imagine writing to the opened file\n     * without committing the changes to the disk.\n     * @param enabled\n     */\n    void setIOCache(bool enabled);\n\n    /**\n     * @brief Check if Cache mode is enabled.\n     * @return true if Cache is enabled, otherwise return false.\n     */\n    bool isIOCacheEnabled() const;\n\n    /**\n     * @brief Commit write cache to the file on disk.\n     */\n    void commitWriteCache();\n    /**\n     * @brief Reset write cache.\n     */\n    void resetWriteCache();\n\n    /**\n     * @brief Enable or disable Write mode. When the file is opened in write mode, any changes to it\n     * will be immediately committed to the file on disk, thus modify the file. This function wrap\n     * Rizin function which re-open the file with the desired permissions.\n     * @param enabled\n     */\n    void setWriteMode(bool enabled);\n    /**\n     * @brief Check if the file is opened in write mode.\n     * @return true if write mode is enabled, otherwise return false.\n     */\n    bool isWriteModeEnabled();\n\n    /**\n     * @brief   Returns the textual version of global or specific graph.\n     * @param   type     Graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT\n     * @param   format   Graph format, example RZ_CORE_GRAPH_FORMAT_DOT or RZ_CORE_GRAPH_FORMAT_GML\n     * @param   address  The object address (if global set it to RVA_INVALID)\n     * @return  The textual graph string.\n     */\n    char *getTextualGraphAt(RzCoreGraphType type, RzCoreGraphFormat format, RVA address);\n\n    /**\n     * @brief   Writes a graphviz graph to a file.\n     * @param   path     The file output path\n     * @param   format   The output format (see graph.gv.format)\n     * @param   type     The graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or\n     * RZ_CORE_GRAPH_TYPE_IMPORT\n     * @param   address  The object address (if global set it to RVA_INVALID)\n     */\n    void writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type, RVA address);\n\nsignals:\n    void refreshAll();\n\n    void functionRenamed(const RVA offset, const QString &new_name);\n    void varsChanged();\n    void globalVarsChanged();\n    void functionsChanged();\n    void flagsChanged();\n    void commentsChanged(RVA addr);\n    void registersChanged();\n    void instructionChanged(RVA offset);\n    void breakpointsChanged(RVA offset);\n    void refreshCodeViews();\n    void stackChanged();\n    void marksChanged();\n    /**\n     * @brief update all the widgets that are affected by rebasing in debug mode\n     */\n    void codeRebased();\n\n    void switchedThread();\n    void switchedProcess();\n\n    void classNew(const QString &cls);\n    void classDeleted(const QString &cls);\n    void classRenamed(const QString &oldName, const QString &newName);\n    void classAttrsChanged(const QString &cls);\n\n    /**\n     * @brief end of current debug event received\n     */\n    void debugProcessFinished(int pid);\n\n    void attachedRemote(bool successfully);\n\n    void ioCacheChanged(bool newval);\n    void writeModeChanged(bool newval);\n    void ioModeChanged();\n\n    /**\n     * emitted when debugTask started or finished running\n     */\n    void debugTaskStateChanged();\n\n    /**\n     * emitted when config regarding disassembly display changes\n     */\n    void asmOptionsChanged();\n\n    /**\n     * emitted when config regarding graph display changes\n     */\n    void graphOptionsChanged();\n\n    /**\n     * @brief seekChanged is emitted each time Rizin's seek value is modified\n     * @param offset\n     * @param historyType\n     */\n    void seekChanged(RVA offset, SeekHistoryType type = SeekHistoryType::New);\n\n    void toggleDebugView();\n\n    void newMessage(const QString &msg);\n    void newDebugMessage(const QString &msg);\n\n    void showMemoryWidgetRequested();\n\n    /**\n     * @brief emitted when a specific type is requested to be shown in the Types Widget\n     */\n    void showTypeRequested(const QString &typeName);\n\nprivate:\n    /**\n     * Internal reference to the RzCore.\n     * NEVER use this directly! Always use the CORE_LOCK(); macro and access it like core->...\n     */\n    RzCore *core_ = nullptr;\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n    QMutex coreMutex;\n#else\n    QRecursiveMutex coreMutex;\n#endif\n    int coreLockDepth = 0;\n    void *coreBed = nullptr;\n\n    AsyncTaskManager *asyncTaskManager;\n    RVA offsetPriorDebugging = RVA_INVALID;\n    QErrorMessage msgBox;\n\n    QList<Decompiler *> decompilers;\n\n    bool emptyGraph = false;\n    BasicBlockHighlighter *bbHighlighter;\n    bool iocache = false;\n    BasicInstructionHighlighter biHighlighter;\n\n    QSharedPointer<RizinTask> debugTask;\n    RizinTaskDialog *debugTaskDialog;\n\n    QVector<QString> getCutterRCFilePaths() const;\n    QList<TypeDescription> getBaseType(RzBaseTypeKind kind, const char *category);\n    QList<SearchDescription> getAllSearchCommand(QString searchFor, SearchKind kind, QString in);\n    QList<MarkDescription> convertMarks(RzList *marks);\n    /**\n     * @brief Collect cross-references for the specified local variable\n     * @param variableName Name of the variable\n     * @param offset An address within the function containing the variable\n     * @param accessTypeMask Mask of access types (Read/Write) to include\n     * @param stopAtFirst Whether to return immediately after the first match\n     * @return List of matching XrefDescription objects\n     */\n    QList<XrefDescription> collectXRefsForVariable(const QString &variableName, RVA offset,\n                                                   int accessTypeMask, bool stopAtFirst);\n};\n\nclass CUTTER_EXPORT RzCoreLocked\n{\n    CutterCore *const core;\n\npublic:\n    explicit RzCoreLocked(CutterCore *core);\n    RzCoreLocked(const RzCoreLocked &) = delete;\n    RzCoreLocked &operator=(const RzCoreLocked &) = delete;\n    RzCoreLocked(RzCoreLocked &&);\n    ~RzCoreLocked();\n    operator RzCore *() &;\n    RzCore *operator->() &;\n    // Reduce chance of following misuse of Core()->lock()\n    // rizinStruct* foo = rizin_func(Core()->lock()->something, arg);\n    operator RzCore *() && = delete;\n    RzCore *operator->() && = delete;\n};\n\n#endif // CUTTER_H\n"
  },
  {
    "path": "src/core/CutterCommon.h",
    "content": "/** \\file CutterCommon.h\n * This file contains any definition that is useful in the whole project.\n * For example, it may contain custom types (RVA, ut64), list iterators, etc.\n */\n#ifndef CUTTERCORE_H\n#define CUTTERCORE_H\n\n#include \"rz_core.h\"\n#include <QString>\n#include \"RizinCpp.h\"\n\n// Workaround for compile errors on Windows\n#ifdef Q_OS_WIN\n#    undef min\n#    undef max\n#endif // Q_OS_WIN\n\n// Global information for Cutter\n#define APPNAME \"Cutter\"\n\n/**\n * @brief Type to be used for all kinds of addresses/offsets in rizin address space.\n */\ntypedef ut64 RVA;\n\n/**\n * @brief Maximum value of RVA. Do NOT use this for specifying invalid values, use RVA_INVALID\n * instead.\n */\n#define RVA_MAX UT64_MAX\n\n/**\n * @brief Value for specifying an invalid RVA.\n */\n#define RVA_INVALID RVA_MAX\n\ninline QString RzAddressString(RVA addr)\n{\n    return QString::asprintf(\"%#010llx\", addr);\n}\n\ninline QString RzSizeString(RVA size)\n{\n    return QString::asprintf(\"%#llx\", size);\n}\n\ninline QString RzHexString(RVA size)\n{\n    return QString::asprintf(\"%#llx\", size);\n}\n\n#ifdef CUTTER_SOURCE_BUILD\n#    define CUTTER_EXPORT Q_DECL_EXPORT\n#else\n#    define CUTTER_EXPORT Q_DECL_IMPORT\n#endif\n\n#if defined(__has_cpp_attribute)\n#    if __has_cpp_attribute(deprecated)\n#        define CUTTER_DEPRECATED(msg) [[deprecated(msg)]]\n#    endif\n#endif\n#if !defined(CUTTER_DEPRECATED)\n#    define CUTTER_DEPRECATED(msg)\n#endif\n\n#endif // CUTTERCORE_H\n"
  },
  {
    "path": "src/core/CutterDescriptions.h",
    "content": "/** \\file CutterDescriptions.h\n * This file contains every structure description that are used in widgets.\n * The descriptions are used for the Qt metatypes.\n */\n#ifndef DESCRIPTIONS_H\n#define DESCRIPTIONS_H\n\n#include <QString>\n#include <QList>\n#include <QStringList>\n#include <QMetaType>\n#include <QColor>\n#include \"core/CutterCommon.h\"\n\nstruct FunctionDescription\n{\n    RVA offset;\n    RVA linearSize;\n    RVA nargs;\n    RVA nbbs;\n    RVA nlocals;\n    QString calltype;\n    QString name;\n    RVA edges;\n    RVA stackframe;\n\n    bool contains(RVA addr) const\n    {\n        // TODO: this is not exactly correct in edge cases.\n        // rz_analysis_function_contains() does it right.\n        return addr >= offset && addr < offset + linearSize;\n    }\n};\n\nstruct ImportDescription\n{\n    RVA plt;\n    int ordinal;\n    QString bind;\n    QString type;\n    QString name;\n    QString libname;\n};\n\nstruct ExportDescription\n{\n    RVA vaddr;\n    RVA paddr;\n    RVA size;\n    QString type;\n    QString name;\n    QString flag_name;\n};\n\nstruct HeaderDescription\n{\n    RVA vaddr;\n    RVA paddr;\n    QString value;\n    QString name;\n};\n\nstruct FlirtDescription\n{\n    QString bin_name;\n    QString arch_name;\n    QString arch_bits;\n    QString base_name;\n    QString short_path;\n    QString file_path;\n    QString details;\n    QString n_modules;\n};\n\nstruct TypeDescription\n{\n    QString type;\n    int size;\n    QString format;\n    QString category;\n};\n\nstruct SearchDescription\n{\n    RVA offset;\n    size_t size;\n    QString code;\n    QString data;\n    QString detail;\n};\n\nstruct SymbolDescription\n{\n    RVA vaddr;\n    QString bind;\n    QString type;\n    QString name;\n};\n\nstruct CommentDescription\n{\n    RVA offset;\n    QString name;\n};\n\nstruct RelocDescription\n{\n    RVA vaddr;\n    RVA paddr;\n    QString type;\n    QString name;\n};\n\nstruct StringDescription\n{\n    RVA vaddr;\n    QString string;\n    QString type;\n    QString section;\n    ut32 length;\n    ut32 size;\n};\n\nstruct FlagspaceDescription\n{\n    QString name;\n};\n\nstruct FlagDescription\n{\n    RVA offset;\n    RVA size;\n    QString name;\n    QString realname;\n};\n\nstruct SectionDescription\n{\n    RVA vaddr;\n    RVA paddr;\n    RVA size;\n    RVA vsize;\n    QString name;\n    QString perm;\n    QString entropy;\n};\n\nstruct SegmentDescription\n{\n    RVA vaddr;\n    RVA paddr;\n    RVA size;\n    RVA vsize;\n    QString name;\n    QString perm;\n};\n\nstruct EntrypointDescription\n{\n    RVA vaddr;\n    RVA paddr;\n    RVA baddr;\n    RVA laddr;\n    RVA haddr;\n    QString type;\n};\n\nstruct XrefDescription\n{\n    RVA from;\n    QString from_str;\n    RVA to;\n    QString to_str;\n    QString type;\n};\n\nstruct RzBinPluginDescription\n{\n    QString name;\n    QString description;\n    QString license;\n    QString type;\n};\n\nstruct RzIOPluginDescription\n{\n    QString name;\n    QString description;\n    QString license;\n    QString permissions;\n    QList<QString> uris;\n};\n\nstruct RzCorePluginDescription\n{\n    QString name;\n    QString description;\n    QString license;\n};\n\nstruct RzAsmPluginDescription\n{\n    QString name;\n    QString architecture;\n    QString author;\n    QString version;\n    QString cpus;\n    QString description;\n    QString license;\n};\n\nstruct DisassemblyLine\n{\n    RVA offset;\n    QString text;\n    RVA arrow;\n};\n\nstruct BinClassBaseClassDescription\n{\n    QString name;\n    RVA offset;\n};\n\nstruct BinClassMethodDescription\n{\n    QString name;\n    RVA addr = RVA_INVALID;\n    st64 vtableOffset = -1;\n};\n\nstruct BinClassFieldDescription\n{\n    QString name;\n    RVA addr = RVA_INVALID;\n};\n\nstruct BinClassDescription\n{\n    QString name;\n    RVA addr = RVA_INVALID;\n    RVA vtableAddr = RVA_INVALID;\n    QList<BinClassBaseClassDescription> baseClasses;\n    QList<BinClassMethodDescription> methods;\n    QList<BinClassFieldDescription> fields;\n};\n\nstruct AnalysisMethodDescription\n{\n    QString name;\n    QString realName;\n    RVA addr;\n    st64 vtableOffset;\n};\n\nstruct AnalysisBaseClassDescription\n{\n    QString id;\n    RVA offset;\n    QString className;\n};\n\nstruct AnalysisVTableDescription\n{\n    QString id;\n    ut64 offset;\n    ut64 addr;\n};\n\nstruct ResourcesDescription\n{\n    QString name;\n    RVA vaddr;\n    ut64 index;\n    QString type;\n    ut64 size;\n    QString lang;\n};\n\nstruct VTableDescription\n{\n    RVA addr;\n    QList<BinClassMethodDescription> methods;\n};\n\nstruct BlockDescription\n{\n    RVA addr;\n    RVA size;\n    int flags;\n    int functions;\n    int inFunctions;\n    int comments;\n    int symbols;\n    int strings;\n    ut8 rwx;\n};\n\nstruct BlockStatistics\n{\n    RVA from;\n    RVA to;\n    RVA blocksize;\n    QList<BlockDescription> blocks;\n};\n\nstruct MemoryMapDescription\n{\n    RVA addrStart;\n    RVA addrEnd;\n    QString name;\n    QString fileName;\n    QString type;\n    QString permission;\n};\n\nstruct BreakpointDescription\n{\n    enum PositionType {\n        Address,\n        Named,\n        Module,\n    };\n\n    RVA addr = 0;\n    int64_t moduleDelta = 0;\n    int index = -1;\n    PositionType type = Address;\n    int size = 0;\n    int permission = 0;\n    QString positionExpression;\n    QString name;\n    QString command;\n    QString condition;\n    bool hw = false;\n    bool trace = false;\n    bool enabled = true;\n};\n\nstruct ProcessDescription\n{\n    bool current;\n    int pid;\n    int uid;\n    int ppid;\n    RzDebugPidState status;\n    QString path;\n};\n\nstruct ThreadDescription\n{\n    bool current;\n    int pid;\n    int uid;\n    int ppid;\n    RzDebugPidState status;\n    QString path;\n    ut64 pc;\n    ut64 tls;\n};\n\nstruct RefDescription\n{\n    QString ref;\n    QColor refColor;\n};\n\nstruct VariableDescription\n{\n    RzAnalysisVarStorageType storageType;\n    QString name;\n    QString type;\n    QString value;\n};\n\nstruct GlobalDescription\n{\n    RVA addr;\n    QString type;\n    QString name;\n};\n\nstruct RegisterRefValueDescription\n{\n    QString name;\n    QString value;\n    QString ref;\n};\n\nstruct Chunk\n{\n    RVA offset;\n    QString status;\n    int size;\n};\n\nstruct Arena\n{\n    RVA offset;\n    QString type;\n    ut64 top;\n    ut64 last_remainder;\n    ut64 next;\n    ut64 next_free;\n    ut64 system_mem;\n    ut64 max_system_mem;\n};\n\nstruct BasefindCoreStatusDescription\n{\n    size_t index;\n    ut32 percentage;\n};\n\nstruct BasefindResultDescription\n{\n    RVA candidate;\n    ut32 score;\n};\n\nstruct MarkDescription\n{\n    RVA from;\n    RVA to;\n    QString name;\n    QString realname;\n    QString comment;\n    QColor color;\n};\n\nQ_DECLARE_METATYPE(FunctionDescription)\nQ_DECLARE_METATYPE(ImportDescription)\nQ_DECLARE_METATYPE(ExportDescription)\nQ_DECLARE_METATYPE(SymbolDescription)\nQ_DECLARE_METATYPE(CommentDescription)\nQ_DECLARE_METATYPE(RelocDescription)\nQ_DECLARE_METATYPE(StringDescription)\nQ_DECLARE_METATYPE(FlagspaceDescription)\nQ_DECLARE_METATYPE(FlagDescription)\nQ_DECLARE_METATYPE(GlobalDescription)\nQ_DECLARE_METATYPE(XrefDescription)\nQ_DECLARE_METATYPE(EntrypointDescription)\nQ_DECLARE_METATYPE(RzBinPluginDescription)\nQ_DECLARE_METATYPE(RzIOPluginDescription)\nQ_DECLARE_METATYPE(RzCorePluginDescription)\nQ_DECLARE_METATYPE(RzAsmPluginDescription)\nQ_DECLARE_METATYPE(BinClassMethodDescription)\nQ_DECLARE_METATYPE(BinClassFieldDescription)\nQ_DECLARE_METATYPE(BinClassDescription)\nQ_DECLARE_METATYPE(const BinClassDescription *)\nQ_DECLARE_METATYPE(const BinClassMethodDescription *)\nQ_DECLARE_METATYPE(const BinClassFieldDescription *)\nQ_DECLARE_METATYPE(AnalysisBaseClassDescription)\nQ_DECLARE_METATYPE(AnalysisMethodDescription)\nQ_DECLARE_METATYPE(AnalysisVTableDescription)\nQ_DECLARE_METATYPE(ResourcesDescription)\nQ_DECLARE_METATYPE(VTableDescription)\nQ_DECLARE_METATYPE(TypeDescription)\nQ_DECLARE_METATYPE(HeaderDescription)\nQ_DECLARE_METATYPE(FlirtDescription)\nQ_DECLARE_METATYPE(SearchDescription)\nQ_DECLARE_METATYPE(SectionDescription)\nQ_DECLARE_METATYPE(SegmentDescription)\nQ_DECLARE_METATYPE(MemoryMapDescription)\nQ_DECLARE_METATYPE(BreakpointDescription)\nQ_DECLARE_METATYPE(BreakpointDescription::PositionType)\nQ_DECLARE_METATYPE(ProcessDescription)\nQ_DECLARE_METATYPE(RefDescription)\nQ_DECLARE_METATYPE(VariableDescription)\nQ_DECLARE_METATYPE(BasefindCoreStatusDescription)\nQ_DECLARE_METATYPE(BasefindResultDescription)\nQ_DECLARE_METATYPE(MarkDescription)\n\n#endif // DESCRIPTIONS_H\n"
  },
  {
    "path": "src/core/CutterJson.cpp",
    "content": "#include \"core/CutterJson.h\"\n\nCutterJson CutterJson::last() const\n{\n    if (!has_children()) {\n        return CutterJson();\n    }\n\n    const RzJson *last = value->children.first;\n    while (last->next) {\n        last = last->next;\n    }\n\n    return CutterJson(last, owner);\n}\n\nQStringList CutterJson::keys() const\n{\n    QStringList list;\n\n    if (value && value->type == RZ_JSON_OBJECT) {\n        for (const RzJson *child = value->children.first; child; child = child->next) {\n            list.append(child->key);\n        }\n    }\n\n    return list;\n}\n"
  },
  {
    "path": "src/core/CutterJson.h",
    "content": "#ifndef CUTTER_JSON_H\n#define CUTTER_JSON_H\n\n#include \"core/CutterCommon.h\"\n\n#include <QSharedPointer>\n#include <QString>\n#include <QStringList>\n#include <rz_project.h>\n\nclass CutterJsonOwner;\n\nclass CUTTER_EXPORT CutterJson\n{\npublic:\n    class iterator\n    {\n    public:\n        iterator(const RzJson *value, QSharedPointer<CutterJsonOwner> owner)\n            : value(value), owner(owner)\n        {\n        }\n\n        CutterJson operator*() const { return CutterJson(value, owner); }\n\n        bool operator!=(const iterator &other) const { return value != other.value; }\n\n        iterator &operator++()\n        {\n            value = value->next;\n            return *this;\n        }\n\n    private:\n        const RzJson *value;\n        QSharedPointer<CutterJsonOwner> owner;\n    };\n\n    CutterJson() : value(nullptr), owner(nullptr) {}\n\n    CutterJson(const RzJson *value, QSharedPointer<CutterJsonOwner> owner)\n        : value(value), owner(owner)\n    {\n    }\n\n    CutterJson first() const\n    {\n        return CutterJson(has_children() ? value->children.first : nullptr, owner);\n    }\n\n    CutterJson last() const;\n\n    CutterJson operator[](const QString &key) const\n    {\n        QByteArray utf8 = key.toUtf8();\n        return (*this)[utf8.data()];\n    }\n\n    CutterJson operator[](const char *key) const\n    {\n        return CutterJson(\n                value && value->type == RZ_JSON_OBJECT ? rz_json_get(value, key) : nullptr, owner);\n    }\n\n    iterator begin() const\n    {\n        return iterator(has_children() ? value->children.first : nullptr, owner);\n    }\n\n    iterator end() const { return iterator(nullptr, nullptr); }\n\n    bool toBool() const { return value && value->type == RZ_JSON_BOOLEAN && value->num.u_value; }\n    st64 toSt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.s_value : 0; }\n    ut64 toUt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.u_value : 0; }\n\n    RVA toRVA() const\n    {\n        return value && value->type == RZ_JSON_INTEGER ? value->num.u_value : RVA_INVALID;\n    }\n\n    QString toString() const\n    {\n        return value && value->type == RZ_JSON_STRING ? QString(value->str_value) : QString();\n    }\n\n    QString key() const { return value ? value->key : QString(); }\n    QStringList keys() const;\n    size_t size() const { return has_children() ? value->children.count : 0; }\n    RzJsonType type() const { return value ? value->type : RZ_JSON_NULL; }\n    bool valid() const { return value ? true : false; }\n    const RzJson *lowLevelValue() const { return value; }\n\nprivate:\n    bool has_children() const\n    {\n        return value && (value->type == RZ_JSON_OBJECT || value->type == RZ_JSON_ARRAY);\n    }\n\n    const RzJson *value;\n    QSharedPointer<CutterJsonOwner> owner;\n};\n\nclass CUTTER_EXPORT CutterJsonOwner\n{\npublic:\n    CutterJsonOwner(RzJson *value, char *text) : value(value), text(text) {}\n\n    virtual ~CutterJsonOwner()\n    {\n        rz_json_free(value);\n        rz_mem_free(text);\n    }\n\nprivate:\n    RzJson *value;\n    char *text;\n};\n\n#endif // CUTTER_JSON_H\n"
  },
  {
    "path": "src/core/MainWindow.cpp",
    "content": "#include \"core/MainWindow.h\"\n#include \"ui_MainWindow.h\"\n\n// Common Headers\n#include \"common/AnalysisTask.h\"\n#include \"common/BugReporting.h\"\n#include \"common/Highlighter.h\"\n#include \"common/Helpers.h\"\n#include \"common/SvgIconEngine.h\"\n#include \"common/ProgressIndicator.h\"\n#include \"common/TempConfig.h\"\n#include \"common/RunScriptTask.h\"\n#include \"common/PythonManager.h\"\n#include \"plugins/CutterPlugin.h\"\n#include \"plugins/PluginManager.h\"\n#include \"CutterConfig.h\"\n#include \"CutterApplication.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n// Dialogs\n#include \"dialogs/WelcomeDialog.h\"\n#include \"dialogs/NewFileDialog.h\"\n#include \"dialogs/InitialOptionsDialog.h\"\n#include \"dialogs/CommentsDialog.h\"\n#include \"dialogs/AboutDialog.h\"\n#include \"dialogs/preferences/PreferencesDialog.h\"\n#include \"dialogs/MapFileDialog.h\"\n#include \"dialogs/AsyncTaskDialog.h\"\n#include \"dialogs/LayoutManager.h\"\n\n// Widgets Headers\n#include \"widgets/DisassemblerGraphView.h\"\n#include \"widgets/GraphView.h\"\n#include \"widgets/GraphWidget.h\"\n#include \"widgets/GlobalsWidget.h\"\n#include \"widgets/OverviewWidget.h\"\n#include \"widgets/OverviewView.h\"\n#include \"widgets/FunctionsWidget.h\"\n#include \"widgets/SectionsWidget.h\"\n#include \"widgets/SegmentsWidget.h\"\n#include \"widgets/CommentsWidget.h\"\n#include \"widgets/ImportsWidget.h\"\n#include \"widgets/ExportsWidget.h\"\n#include \"widgets/TypesWidget.h\"\n#include \"widgets/SearchWidget.h\"\n#include \"widgets/SymbolsWidget.h\"\n#include \"widgets/StringsWidget.h\"\n#include \"widgets/RelocsWidget.h\"\n#include \"widgets/FlagsWidget.h\"\n#include \"widgets/VisualNavbar.h\"\n#include \"widgets/Dashboard.h\"\n#include \"widgets/SdbWidget.h\"\n#include \"widgets/Omnibar.h\"\n#include \"widgets/ConsoleWidget.h\"\n#include \"widgets/EntrypointWidget.h\"\n#include \"widgets/ClassesWidget.h\"\n#include \"widgets/ResourcesWidget.h\"\n#include \"widgets/VTablesWidget.h\"\n#include \"widgets/HeadersWidget.h\"\n#include \"widgets/FlirtWidget.h\"\n#include \"widgets/DebugActions.h\"\n#include \"widgets/MemoryMapWidget.h\"\n#include \"widgets/BreakpointWidget.h\"\n#include \"widgets/RegisterRefsWidget.h\"\n#include \"widgets/DisassemblyWidget.h\"\n#include \"widgets/StackWidget.h\"\n#include \"widgets/ThreadsWidget.h\"\n#include \"widgets/ProcessesWidget.h\"\n#include \"widgets/RegistersWidget.h\"\n#include \"widgets/BacktraceWidget.h\"\n#include \"widgets/HexdumpWidget.h\"\n#include \"widgets/DecompilerWidget.h\"\n#include \"widgets/HexWidget.h\"\n#include \"widgets/RizinGraphWidget.h\"\n#include \"widgets/CallGraph.h\"\n#include \"widgets/HeapDockWidget.h\"\n\n// Qt Headers\n#include <QActionGroup>\n#include <QApplication>\n#include <QComboBox>\n#include <QCompleter>\n#include <QDebug>\n#include <QDesktopServices>\n#include <QDir>\n#include <QDockWidget>\n#include <QFile>\n#include <QFileDialog>\n#include <QFont>\n#include <QFontDialog>\n#include <QLabel>\n#include <QLineEdit>\n#include <QList>\n#include <QMessageBox>\n#include <QProcess>\n#include <QPropertyAnimation>\n#include <QSysInfo>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QInputDialog>\n\n#include <QScrollBar>\n#include <QSettings>\n#include <QShortcut>\n#include <QStringListModel>\n#include <QStyledItemDelegate>\n#include <QStyleFactory>\n#include <QTextCursor>\n#include <QtGlobal>\n#include <QToolButton>\n#include <QToolTip>\n#include <QTreeWidgetItem>\n#include <QSvgRenderer>\n\n// Graphics\n#include <QGraphicsEllipseItem>\n#include <QGraphicsScene>\n#include <QGraphicsView>\n\n// Tools\n#include \"tools/basefind/BaseFindDialog.h\"\n\ntemplate<class T>\nT *getNewInstance(MainWindow *m)\n{\n    return new T(m);\n}\n\nusing namespace Cutter;\n\nMainWindow::MainWindow(QWidget *parent)\n    : QMainWindow(parent), core(Core()), ui(new Ui::MainWindow), ioModesController(this)\n{\n    tabsOnTop = false;\n    configuration = Config();\n\n    initUI();\n}\n\nMainWindow::~MainWindow() {}\n\nvoid MainWindow::initUI()\n{\n    ui->setupUi(this);\n\n    // Initialize context menu extensions for plugins\n    disassemblyContextMenuExtensions = new QMenu(tr(\"Plugins\"), this);\n    addressableContextMenuExtensions = new QMenu(tr(\"Plugins\"), this);\n\n    connect(ui->actionExtraDecompiler, &QAction::triggered, this, &MainWindow::addExtraDecompiler);\n    connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph);\n    connect(ui->actionExtraDisassembly, &QAction::triggered, this,\n            &MainWindow::addExtraDisassembly);\n    connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);\n    connect(ui->actionCommitChanges, &QAction::triggered, this,\n            []() { Core()->commitWriteCache(); });\n    ui->actionCommitChanges->setEnabled(false);\n    connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);\n\n    widgetTypeToConstructorMap.insert(GraphWidget::getWidgetType(), getNewInstance<GraphWidget>);\n    widgetTypeToConstructorMap.insert(DisassemblyWidget::getWidgetType(),\n                                      getNewInstance<DisassemblyWidget>);\n    widgetTypeToConstructorMap.insert(HexdumpWidget::getWidgetType(),\n                                      getNewInstance<HexdumpWidget>);\n    widgetTypeToConstructorMap.insert(DecompilerWidget::getWidgetType(),\n                                      getNewInstance<DecompilerWidget>);\n\n    initToolBar();\n    initDocks();\n\n    emptyState = saveState();\n    /*\n     *  Some global shortcuts\n     */\n\n    // Period goes to command entry\n    QShortcut *cmd_shortcut = Shortcuts()->makeQShortcut(\"Console.focusConsole\", this);\n    connect(cmd_shortcut, &QShortcut::activated, consoleDock, &ConsoleWidget::focusInputLineEdit);\n\n    // S goes to goto entry\n    QShortcut *seek_shortcut = Shortcuts()->makeQShortcut(\"General.seek\", this);\n    connect(seek_shortcut, &QShortcut::activated, this->omnibar,\n            [this]() { this->omnibar->setFocus(); });\n    QShortcut *seek_to_func_end_shortcut =\n            Shortcuts()->makeQShortcut(\"General.seekToFunctionEnd\", this);\n    connect(seek_to_func_end_shortcut, &QShortcut::activated, this,\n            &MainWindow::seekToFunctionLastInstruction);\n    QShortcut *seek_to_func_start_shortcut =\n            Shortcuts()->makeQShortcut(\"General.seekToFunctionStart\", this);\n    connect(seek_to_func_start_shortcut, &QShortcut::activated, this,\n            &MainWindow::seekToFunctionStart);\n\n    Shortcuts()->setupAction(*ui->actionRefresh_contents, \"General.refreshContents\");\n\n    connect(ui->actionZoomIn, &QAction::triggered, this, &MainWindow::onZoomIn);\n    connect(ui->actionZoomOut, &QAction::triggered, this, &MainWindow::onZoomOut);\n    connect(ui->actionZoomReset, &QAction::triggered, this, &MainWindow::onZoomReset);\n\n    connect(core, &CutterCore::toggleDebugView, this, &MainWindow::toggleDebugView);\n\n    connect(core, &CutterCore::newMessage, this->consoleDock, &ConsoleWidget::addOutput);\n    connect(core, &CutterCore::newDebugMessage, this->consoleDock, &ConsoleWidget::addDebugOutput);\n\n    connect(core, &CutterCore::showMemoryWidgetRequested, this,\n            static_cast<void (MainWindow::*)()>(&MainWindow::showMemoryWidget));\n\n    connect(core, &CutterCore::showTypeRequested, typesDock, [this](const QString &typeName) {\n        typesDock->toggleDockWidget(true);\n        typesDock->selectTypeByName(typeName);\n    });\n\n    updateTasksIndicator();\n    connect(core->getAsyncTaskManager(), &AsyncTaskManager::tasksChanged, this,\n            &MainWindow::updateTasksIndicator);\n\n    // Undo and redo seek\n    Shortcuts()->setupAction(*ui->actionBackward, \"General.back\");\n    Shortcuts()->setupAction(*ui->actionForward, \"General.forward\");\n\n    initBackForwardMenu();\n\n    connect(core, &CutterCore::ioModeChanged, this, &MainWindow::setAvailableIOModeOptions);\n\n    QActionGroup *ioModeActionGroup = new QActionGroup(this);\n\n    ioModeActionGroup->addAction(ui->actionCacheMode);\n    ioModeActionGroup->addAction(ui->actionWriteMode);\n    ioModeActionGroup->addAction(ui->actionReadOnly);\n\n    connect(ui->actionCacheMode, &QAction::triggered, this, [this]() {\n        ioModesController.setIOMode(IOModesController::Mode::CACHE);\n        setAvailableIOModeOptions();\n    });\n\n    connect(ui->actionWriteMode, &QAction::triggered, this, [this]() {\n        ioModesController.setIOMode(IOModesController::Mode::WRITE);\n        setAvailableIOModeOptions();\n    });\n\n    connect(ui->actionReadOnly, &QAction::triggered, this, [this]() {\n        ioModesController.setIOMode(IOModesController::Mode::READ_ONLY);\n        setAvailableIOModeOptions();\n    });\n\n    connect(ui->actionSaveLayout, &QAction::triggered, this, &MainWindow::saveNamedLayout);\n    connect(ui->actionManageLayouts, &QAction::triggered, this, &MainWindow::manageLayouts);\n    connect(ui->actionDocumentation, &QAction::triggered, this, &MainWindow::documentationClicked);\n\n    /* Setup plugins interfaces */\n    const auto &plugins = Plugins()->getPlugins();\n    for (auto &plugin : plugins) {\n        plugin->setupInterface(this);\n    }\n\n    // Check if plugins are loaded and display tooltips accordingly\n    ui->menuWindows->setToolTipsVisible(true);\n    if (plugins.empty()) {\n        ui->menuPlugins->menuAction()->setToolTip(\n                tr(\"No plugins are installed. Check the plugins section on Cutter documentation to \"\n                   \"learn more.\"));\n        ui->menuPlugins->setEnabled(false);\n    } else if (ui->menuPlugins->isEmpty()) {\n        ui->menuPlugins->menuAction()->setToolTip(\n                tr(\"The installed plugins didn't add entries to this menu.\"));\n        ui->menuPlugins->setEnabled(false);\n    }\n\n    connect(ui->actionUnlock, &QAction::toggled, this, [this](bool unlock) { lockDocks(!unlock); });\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)\n    ui->actionGrouped_dock_dragging->setVisible(false);\n#endif\n\n    enableDebugWidgetsMenu(false);\n    readSettings();\n\n    // Display tooltip for the Analyze Program action\n    ui->actionAnalyze->setToolTip(tr(\"Analyze the program using Rizin's \\\"aaa\\\" command\"));\n    ui->menuFile->setToolTipsVisible(true);\n}\n\nvoid MainWindow::initToolBar()\n{\n    chooseThemeIcons();\n\n    // Sepparator between undo/redo and goto lineEdit\n    QWidget *spacer3 = new QWidget();\n    spacer3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n    spacer3->setStyleSheet(\"background-color: rgba(0,0,0,0)\");\n    spacer3->setMinimumSize(20, 20);\n    spacer3->setMaximumWidth(100);\n    ui->mainToolBar->addWidget(spacer3);\n\n    DebugActions *debugActions = new DebugActions(ui->mainToolBar, this);\n    // Debug menu\n    auto debugViewAction = ui->menuDebug->addAction(tr(\"View\"));\n    debugViewAction->setMenu(ui->menuAddDebugWidgets);\n    ui->menuDebug->addSeparator();\n    ui->menuDebug->addAction(debugActions->actionStart);\n    ui->menuDebug->addAction(debugActions->actionStartEmul);\n    ui->menuDebug->addAction(debugActions->actionAttach);\n    ui->menuDebug->addAction(debugActions->actionStartRemote);\n    ui->menuDebug->addAction(debugActions->actionStop);\n    ui->menuDebug->addSeparator();\n    ui->menuDebug->addAction(debugActions->actionStep);\n    ui->menuDebug->addAction(debugActions->actionStepOver);\n    ui->menuDebug->addAction(debugActions->actionStepOut);\n    ui->menuDebug->addAction(debugActions->actionStepBack);\n    ui->menuDebug->addSeparator();\n    ui->menuDebug->addAction(debugActions->actionContinue);\n    ui->menuDebug->addAction(debugActions->actionContinueUntilCall);\n    ui->menuDebug->addAction(debugActions->actionContinueUntilSyscall);\n    ui->menuDebug->addAction(debugActions->actionContinueBack);\n    ui->menuDebug->addSeparator();\n    ui->menuDebug->addAction(debugActions->actionTrace);\n\n    // Sepparator between undo/redo and goto lineEdit\n    QWidget *spacer4 = new QWidget();\n    spacer4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n    spacer4->setStyleSheet(\"background-color: rgba(0,0,0,0)\");\n    spacer4->setMinimumSize(10, 10);\n    spacer4->setMaximumWidth(10);\n    ui->mainToolBar->addWidget(spacer4);\n\n    // Omnibar LineEdit\n    this->omnibar = new Omnibar(this);\n    ui->mainToolBar->addWidget(this->omnibar);\n\n    // Add special separators to the toolbar that expand to separate groups of elements\n    QWidget *spacer2 = new QWidget();\n    spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n    spacer2->setStyleSheet(\"background-color: rgba(0,0,0,0)\");\n    spacer2->setMinimumSize(10, 10);\n    spacer2->setMaximumWidth(300);\n    ui->mainToolBar->addWidget(spacer2);\n\n    // Separator between back/forward and undo/redo buttons\n    QWidget *spacer = new QWidget();\n    spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);\n    spacer->setStyleSheet(\"background-color: rgba(0,0,0,0)\");\n    spacer->setMinimumSize(20, 20);\n    ui->mainToolBar->addWidget(spacer);\n\n    tasksProgressIndicator = new ProgressIndicator();\n    tasksProgressIndicator->setStyleSheet(\"background-color: rgba(0,0,0,0)\");\n    ui->mainToolBar->addWidget(tasksProgressIndicator);\n\n    QWidget *spacerEnd = new QWidget();\n    spacerEnd->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);\n    spacerEnd->setStyleSheet(\"background-color: rgba(0,0,0,0)\");\n    spacerEnd->setMinimumSize(4, 0);\n    spacerEnd->setMaximumWidth(4);\n    ui->mainToolBar->addWidget(spacerEnd);\n\n    // Visual navigation tool bar\n    this->visualNavbar = new VisualNavbar(this);\n    this->visualNavbar->setMovable(false);\n    addToolBarBreak(Qt::TopToolBarArea);\n    addToolBar(visualNavbar);\n    QObject::connect(configuration, &Configuration::colorsUpdated, this,\n                     [this]() { this->visualNavbar->updateGraphicsScene(); });\n    QObject::connect(configuration, &Configuration::interfaceThemeChanged, this,\n                     &MainWindow::chooseThemeIcons);\n}\n\nvoid MainWindow::initDocks()\n{\n    dockWidgets.reserve(20);\n    consoleDock = new ConsoleWidget(this);\n\n    overviewDock = new OverviewWidget(this);\n    overviewDock->hide();\n    actionOverview = overviewDock->toggleViewAction();\n    connect(overviewDock, &OverviewWidget::isAvailableChanged, this,\n            [this](bool isAvailable) { actionOverview->setEnabled(isAvailable); });\n    actionOverview->setEnabled(overviewDock->getIsAvailable());\n    actionOverview->setChecked(overviewDock->getUserOpened());\n\n    dashboardDock = new Dashboard(this);\n    functionsDock = new FunctionsWidget(this);\n    typesDock = new TypesWidget(this);\n    searchDock = new SearchWidget(this);\n    commentsDock = new CommentsWidget(this);\n    stringsDock = new StringsWidget(this);\n\n    QList<CutterDockWidget *> debugDocks = { stackDock = new StackWidget(this),\n                                             threadsDock = new ThreadsWidget(this),\n                                             processesDock = new ProcessesWidget(this),\n                                             backtraceDock = new BacktraceWidget(this),\n                                             registersDock = new RegistersWidget(this),\n                                             memoryMapDock = new MemoryMapWidget(this),\n                                             breakpointDock = new BreakpointWidget(this),\n                                             registerRefsDock = new RegisterRefsWidget(this),\n                                             heapDock = new HeapDockWidget(this) };\n\n    QList<CutterDockWidget *> infoDocks = {\n        classesDock = new ClassesWidget(this),\n        entrypointDock = new EntrypointWidget(this),\n        exportsDock = new ExportsWidget(this),\n        flagsDock = new FlagsWidget(this),\n        headersDock = new HeadersWidget(this),\n        importsDock = new ImportsWidget(this),\n        relocsDock = new RelocsWidget(this),\n        resourcesDock = new ResourcesWidget(this),\n        sdbDock = new SdbWidget(this),\n        sectionsDock = new SectionsWidget(this),\n        segmentsDock = new SegmentsWidget(this),\n        symbolsDock = new SymbolsWidget(this),\n        globalsDock = new GlobalsWidget(this),\n        vTablesDock = new VTablesWidget(this),\n        flirtDock = new FlirtWidget(this),\n        rzGraphDock = new RizinGraphWidget(this),\n        callGraphDock = new CallGraphWidget(this, false),\n        globalCallGraphDock = new CallGraphWidget(this, true),\n    };\n\n    auto makeActionList = [this](QList<CutterDockWidget *> docks) {\n        QList<QAction *> result;\n        for (auto dock : docks) {\n            if (dock != nullptr) {\n                result.push_back(dock->toggleViewAction());\n            } else {\n                auto separator = new QAction(this);\n                separator->setSeparator(true);\n                result.push_back(separator);\n            }\n        }\n        return result;\n    };\n\n    QList<CutterDockWidget *> windowDocks = {\n        dashboardDock, nullptr,     functionsDock, overviewDock, nullptr,\n        searchDock,    stringsDock, typesDock,     nullptr,\n    };\n    ui->menuWindows->insertActions(ui->actionExtraDecompiler, makeActionList(windowDocks));\n    QList<CutterDockWidget *> windowDocks2 = {\n        consoleDock,\n        commentsDock,\n        nullptr,\n    };\n    ui->menuWindows->addActions(makeActionList(windowDocks2));\n    ui->menuAddInfoWidgets->addActions(makeActionList(infoDocks));\n    ui->menuAddDebugWidgets->addActions(makeActionList(debugDocks));\n\n    auto uniqueDocks = windowDocks + windowDocks2 + infoDocks + debugDocks;\n    for (auto dock : uniqueDocks) {\n        if (dock) { // ignore nullptr used as separators\n            addWidget(dock);\n        }\n    }\n}\n\nvoid MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph)\n{\n    if (!overviewDock) {\n        return;\n    }\n    if (visibility) {\n        overviewDock->setTargetGraphWidget(targetGraph);\n    }\n}\n\nvoid MainWindow::updateTasksIndicator()\n{\n    bool running = core->getAsyncTaskManager()->getTasksRunning();\n    tasksProgressIndicator->setProgressIndicatorVisible(running);\n}\n\nvoid MainWindow::addExtraGraph()\n{\n    auto *extraDock = new GraphWidget(this);\n    addExtraWidget(extraDock);\n}\n\nvoid MainWindow::addExtraHexdump()\n{\n    auto *extraDock = new HexdumpWidget(this);\n    addExtraWidget(extraDock);\n}\n\nvoid MainWindow::addExtraDisassembly()\n{\n    auto *extraDock = new DisassemblyWidget(this);\n    addExtraWidget(extraDock);\n}\n\nvoid MainWindow::addExtraDecompiler()\n{\n    auto *extraDock = new DecompilerWidget(this);\n    addExtraWidget(extraDock);\n}\n\nvoid MainWindow::addExtraWidget(CutterDockWidget *extraDock)\n{\n    extraDock->setTransient(true);\n    dockOnMainArea(extraDock);\n    addWidget(extraDock);\n    extraDock->show();\n    extraDock->raise();\n}\n\nQMenu *MainWindow::getMenuByType(MenuType type)\n{\n    switch (type) {\n    case MenuType::File:\n        return ui->menuFile;\n    case MenuType::Edit:\n        return ui->menuEdit;\n    case MenuType::View:\n        return ui->menuView;\n    case MenuType::Windows:\n        return ui->menuWindows;\n    case MenuType::Debug:\n        return ui->menuDebug;\n    case MenuType::Help:\n        return ui->menuHelp;\n    case MenuType::Plugins:\n        return ui->menuPlugins;\n    default:\n        return nullptr;\n    }\n}\n\nvoid MainWindow::addPluginDockWidget(CutterDockWidget *dockWidget)\n{\n    addWidget(dockWidget);\n    ui->menuPlugins->addAction(dockWidget->toggleViewAction());\n    addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget);\n    pluginDocks.push_back(dockWidget);\n}\n\nvoid MainWindow::addMenuFileAction(QAction *action)\n{\n    ui->menuFile->addAction(action);\n}\n\nvoid MainWindow::openNewFile(InitialOptions &options, bool skipOptionsDialog)\n{\n    setFilename(options.filename);\n\n    /* Prompt to load filename.rz script */\n    if (options.script.isEmpty()) {\n        QString script = QString(\"%1.rz\").arg(this->filename);\n        if (rz_file_exists(script.toStdString().data())) {\n            QMessageBox mb(this);\n            mb.setWindowTitle(tr(\"Script loading\"));\n            mb.setText(tr(\"Do you want to load the '%1' script?\").arg(script));\n            mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);\n            if (mb.exec() == QMessageBox::Yes) {\n                options.script = script;\n            }\n        }\n    }\n\n    /* Show analysis options dialog */\n    displayInitialOptionsDialog(options, skipOptionsDialog);\n}\n\nvoid MainWindow::openNewFileFailed()\n{\n    displayNewFileDialog();\n    QMessageBox mb(this);\n    mb.setIcon(QMessageBox::Critical);\n    mb.setStandardButtons(QMessageBox::Ok);\n    mb.setWindowTitle(tr(\"Cannot open file!\"));\n    mb.setText(tr(\"Could not open the file! Make sure the file exists and that you have the \"\n                  \"correct permissions.\"));\n    mb.exec();\n}\n\n/**\n * @brief displays the WelocmeDialog\n *\n * Upon first execution of Cutter, the WelcomeDialog would be showed to the user.\n * The Welcome dialog would be showed after a reset of Cutter's preferences by the user.\n */\n\nvoid MainWindow::displayWelcomeDialog()\n{\n    WelcomeDialog w;\n    w.exec();\n}\n\nvoid MainWindow::displayNewFileDialog()\n{\n    NewFileDialog *n = new NewFileDialog(this);\n    newFileDialog = n;\n    n->setAttribute(Qt::WA_DeleteOnClose);\n    n->show();\n}\n\nvoid MainWindow::closeNewFileDialog()\n{\n    if (newFileDialog) {\n        newFileDialog->close();\n    }\n    newFileDialog = nullptr;\n}\n\nvoid MainWindow::displayInitialOptionsDialog(const InitialOptions &options, bool skipOptionsDialog)\n{\n    auto o = new InitialOptionsDialog(this);\n    o->setAttribute(Qt::WA_DeleteOnClose);\n    o->loadOptions(options);\n\n    if (skipOptionsDialog) {\n        if (!options.projectFile.isEmpty()) {\n            if (!openProject(options.projectFile)) {\n                displayNewFileDialog();\n            };\n        } else {\n            o->setupAndStartAnalysis();\n        }\n    } else {\n        o->show();\n    }\n}\n\nbool MainWindow::openProject(const QString &file)\n{\n    RzProjectErr err;\n    RzList *res = rz_list_new();\n    {\n        RzCoreLocked core(Core());\n        err = rz_project_load_file(core, file.toUtf8().constData(), true, res);\n    }\n    if (err != RZ_PROJECT_ERR_SUCCESS) {\n        const char *s = rz_project_err_message(err);\n        QString msg = tr(\"Failed to open project: %1\").arg(QString::fromUtf8(s));\n        RzListIter *it;\n        CutterRzListForeach (res, it, const char, s) {\n            msg += \"\\n\" + QString::fromUtf8(s);\n        }\n        QMessageBox::critical(this, tr(\"Open Project\"), msg);\n        rz_list_free(res);\n        return false;\n    }\n\n    Config()->addRecentProject(file);\n\n    rz_list_free(res);\n    setFilename(file.trimmed());\n    finalizeOpen();\n    return true;\n}\n\nvoid MainWindow::finalizeOpen()\n{\n    core->getRegs();\n    core->updateSeek();\n    refreshAll();\n    // Add fortune message\n    {\n        auto rizin = Core()->lock();\n        char *fortune = rz_core_fortune_get_random(rizin);\n        if (fortune) {\n            core->message(\"\\n\" + QString(fortune));\n            free(fortune);\n        }\n    }\n\n    // hide all docks before showing window to avoid false positive for refreshDeferrer\n    for (auto dockWidget : dockWidgets) {\n        dockWidget->hide();\n    }\n\n    QSettings settings;\n    auto geometry = settings.value(\"geometry\").toByteArray();\n    if (!geometry.isEmpty()) {\n        restoreGeometry(geometry);\n        show();\n    } else {\n        showMaximized();\n    }\n\n    Config()->adjustColorThemeDarkness();\n    setViewLayout(getViewLayout(LAYOUT_DEFAULT));\n\n    // Set focus to disasm or graph widget\n    // Graph with function in it has focus priority over DisasmWidget.\n    // If there are no graph/disasm widgets focus on MainWindow\n\n    setFocus();\n    bool graphContainsFunc = false;\n    for (auto dockWidget : dockWidgets) {\n        auto graphWidget = qobject_cast<GraphWidget *>(dockWidget);\n        if (graphWidget && dockWidget->isVisibleToUser()) {\n            graphContainsFunc = !graphWidget->getGraphView()->getBlocks().empty();\n            if (graphContainsFunc) {\n                dockWidget->raiseMemoryWidget();\n                break;\n            }\n        }\n        auto disasmWidget = qobject_cast<DisassemblyWidget *>(dockWidget);\n        if (disasmWidget && dockWidget->isVisibleToUser()) {\n            disasmWidget->raiseMemoryWidget();\n            // continue looping in case there is a graph widget\n        }\n        auto decompilerWidget = qobject_cast<DecompilerWidget *>(dockWidget);\n        if (decompilerWidget && dockWidget->isVisibleToUser()) {\n            decompilerWidget->raiseMemoryWidget();\n            // continue looping in case there is a graph widget\n        }\n    }\n}\n\nRzProjectErr MainWindow::saveProject(bool *canceled)\n{\n    QString file = core->getConfig(\"prj.file\");\n    if (file.isEmpty()) {\n        return saveProjectAs(canceled);\n    }\n    if (canceled) {\n        *canceled = false;\n    }\n    auto rizin = core->lock();\n    RzProjectErr err = rz_project_save_file(rizin, file.toUtf8().constData(), false);\n    if (err == RZ_PROJECT_ERR_SUCCESS) {\n        Config()->addRecentProject(file);\n    }\n    return err;\n}\n\nRzProjectErr MainWindow::saveProjectAs(bool *canceled)\n{\n    QString projectFile = core->getConfig(\"prj.file\");\n    if (projectFile.isEmpty()) {\n        // preferred name is of fromat 'binary.exe.rzdb'\n        projectFile = QString(\"%1.%2\").arg(filename).arg(\"rzdb\");\n    }\n\n    QFileDialog fileDialog(this);\n    // Append 'rzdb' suffix if it does not exist\n    fileDialog.setDefaultSuffix(\"rzdb\");\n    QString file = fileDialog.getSaveFileName(this, tr(\"Save Project\"), projectFile,\n                                              tr(\"Rizin Project (*.rzdb)\"));\n    if (file.isEmpty()) {\n        if (canceled) {\n            *canceled = true;\n        }\n        return RZ_PROJECT_ERR_SUCCESS;\n    }\n    if (canceled) {\n        *canceled = false;\n    }\n    auto rizin = core->lock();\n    RzProjectErr err = rz_project_save_file(rizin, file.toUtf8().constData(), false);\n    if (err == RZ_PROJECT_ERR_SUCCESS) {\n        Config()->addRecentProject(file);\n    }\n    return err;\n}\n\nvoid MainWindow::showProjectSaveError(RzProjectErr err)\n{\n    if (err == RZ_PROJECT_ERR_SUCCESS) {\n        return;\n    }\n    const char *s = rz_project_err_message(err);\n    QMessageBox::critical(this, tr(\"Save Project\"),\n                          tr(\"Failed to save project: %1\").arg(QString::fromUtf8(s)));\n}\n\nvoid MainWindow::refreshOmniBar(const QStringList &flags)\n{\n    omnibar->refresh(flags);\n}\n\nvoid MainWindow::setFilename(const QString &fn)\n{\n    // Add file name to window title\n    this->filename = fn;\n    this->setWindowTitle(APPNAME \" – \" + fn);\n}\n\nvoid MainWindow::closeEvent(QCloseEvent *event)\n{\n\n    // Check if there are uncommitted changes\n    if (!ioModesController.askCommitUnsavedChanges()) {\n        // if false, Cancel was chosen\n        event->ignore();\n        return;\n    }\n\n    activateWindow();\n\n    QMessageBox::StandardButton ret = QMessageBox::question(\n            this, APPNAME, tr(\"Do you really want to exit?\\nSave your project before closing!\"),\n            (QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard\n                                           | QMessageBox::Cancel));\n    if (ret == QMessageBox::Cancel) {\n        event->ignore();\n        return;\n    }\n\n    if (ret == QMessageBox::Save) {\n        bool canceled;\n        RzProjectErr save_err = saveProject(&canceled);\n        if (canceled) {\n            event->ignore();\n            return;\n        } else if (save_err != RZ_PROJECT_ERR_SUCCESS) {\n            event->ignore();\n            showProjectSaveError(save_err);\n            return;\n        }\n    }\n\n    if (!core->currentlyDebugging) {\n        saveSettings();\n    } else {\n        core->stopDebug();\n    }\n    QMainWindow::closeEvent(event);\n}\n\nvoid MainWindow::paintEvent(QPaintEvent *event)\n{\n    QMainWindow::paintEvent(event);\n    /*\n     * Dirty hack\n     * Just to adjust the width of Functions Widget to fixed size.\n     * After everything is drawn, restore the max width limit.\n     */\n    if (functionsDock && functionDockWidthToRestore) {\n        functionsDock->setMaximumWidth(functionDockWidthToRestore);\n        functionDockWidthToRestore = 0;\n    }\n}\n\nvoid MainWindow::readSettings()\n{\n    QSettings settings;\n\n    responsive = settings.value(\"responsive\").toBool();\n    lockDocks(settings.value(\"panelLock\").toBool());\n    tabsOnTop = settings.value(\"tabsOnTop\").toBool();\n    setTabLocation();\n    bool dockGroupedDragging = settings.value(\"docksGroupedDragging\", false).toBool();\n    ui->actionGrouped_dock_dragging->setChecked(dockGroupedDragging);\n    on_actionGrouped_dock_dragging_triggered(dockGroupedDragging);\n\n    loadLayouts(settings);\n}\n\nvoid MainWindow::saveSettings()\n{\n    QSettings settings;\n\n    settings.setValue(\"panelLock\", !ui->actionUnlock->isChecked());\n    settings.setValue(\"tabsOnTop\", tabsOnTop);\n    settings.setValue(\"docksGroupedDragging\", ui->actionGrouped_dock_dragging->isChecked());\n    settings.setValue(\"geometry\", saveGeometry());\n\n    layouts[Core()->currentlyDebugging ? LAYOUT_DEBUG : LAYOUT_DEFAULT] = getViewLayout();\n    saveLayouts(settings);\n}\n\nvoid MainWindow::setTabLocation()\n{\n    if (tabsOnTop) {\n        this->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);\n        ui->actionTabs_on_Top->setChecked(true);\n    } else {\n        this->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::South);\n        ui->actionTabs_on_Top->setChecked(false);\n    }\n}\n\nvoid MainWindow::refreshAll()\n{\n    core->triggerRefreshAll();\n}\n\nvoid MainWindow::lockDocks(bool lock)\n{\n    if (ui->actionUnlock->isChecked() == lock) {\n        ui->actionUnlock->setChecked(!lock);\n    }\n    if (lock) {\n        for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {\n            dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);\n        }\n    } else {\n        for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {\n            dockWidget->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable\n                                    | QDockWidget::DockWidgetFloatable);\n        }\n    }\n}\n\nvoid MainWindow::restoreDocks()\n{\n    // Initial structure\n    // func | main area | debug\n    //      |___________|\n    //      | console   |\n    addDockWidget(Qt::LeftDockWidgetArea, functionsDock);\n    splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal);\n    splitDockWidget(dashboardDock, stackDock, Qt::Horizontal);\n    splitDockWidget(dashboardDock, consoleDock, Qt::Vertical);\n\n    // overview bellow func\n    splitDockWidget(functionsDock, overviewDock, Qt::Vertical);\n\n    // main area\n    tabifyDockWidget(dashboardDock, entrypointDock);\n    tabifyDockWidget(dashboardDock, flagsDock);\n    tabifyDockWidget(dashboardDock, stringsDock);\n    tabifyDockWidget(dashboardDock, relocsDock);\n    tabifyDockWidget(dashboardDock, importsDock);\n    tabifyDockWidget(dashboardDock, exportsDock);\n    tabifyDockWidget(dashboardDock, typesDock);\n    tabifyDockWidget(dashboardDock, searchDock);\n    tabifyDockWidget(dashboardDock, headersDock);\n    tabifyDockWidget(dashboardDock, flirtDock);\n    tabifyDockWidget(dashboardDock, symbolsDock);\n    tabifyDockWidget(dashboardDock, globalsDock);\n    tabifyDockWidget(dashboardDock, classesDock);\n    tabifyDockWidget(dashboardDock, resourcesDock);\n    tabifyDockWidget(dashboardDock, vTablesDock);\n    tabifyDockWidget(dashboardDock, sdbDock);\n    tabifyDockWidget(dashboardDock, memoryMapDock);\n    tabifyDockWidget(dashboardDock, breakpointDock);\n    tabifyDockWidget(dashboardDock, registerRefsDock);\n    tabifyDockWidget(dashboardDock, rzGraphDock);\n    tabifyDockWidget(dashboardDock, callGraphDock);\n    tabifyDockWidget(dashboardDock, globalCallGraphDock);\n    for (const auto &it : dockWidgets) {\n        // Check whether or not current widgets is graph, hexdump or disasm\n        if (isExtraMemoryWidget(it)) {\n            tabifyDockWidget(dashboardDock, it);\n        }\n    }\n\n    // Console | Sections/segments/comments\n    splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal);\n    tabifyDockWidget(sectionsDock, segmentsDock);\n    tabifyDockWidget(sectionsDock, commentsDock);\n\n    // Add Stack, Registers, Threads and Backtrace vertically stacked\n    splitDockWidget(stackDock, registersDock, Qt::Vertical);\n    tabifyDockWidget(stackDock, backtraceDock);\n    tabifyDockWidget(backtraceDock, threadsDock);\n    tabifyDockWidget(threadsDock, processesDock);\n    tabifyDockWidget(processesDock, heapDock);\n\n    for (auto dock : pluginDocks) {\n        dockOnMainArea(dock);\n    }\n}\n\nbool MainWindow::isDebugWidget(QDockWidget *dock) const\n{\n    return dock == stackDock || dock == registersDock || dock == backtraceDock\n            || dock == threadsDock || dock == memoryMapDock || dock == breakpointDock\n            || dock == processesDock || dock == registerRefsDock || dock == heapDock;\n}\n\nbool MainWindow::isExtraMemoryWidget(QDockWidget *dock) const\n{\n    return qobject_cast<GraphWidget *>(dock) || qobject_cast<HexdumpWidget *>(dock)\n            || qobject_cast<DisassemblyWidget *>(dock) || qobject_cast<DecompilerWidget *>(dock);\n}\n\nMemoryWidgetType MainWindow::getMemoryWidgetTypeToRestore()\n{\n    if (lastSyncMemoryWidget) {\n        return lastSyncMemoryWidget->getType();\n    }\n    return MemoryWidgetType::Disassembly;\n}\n\nQString MainWindow::getUniqueObjectName(const QString &widgetType) const\n{\n    QStringList docks;\n    docks.reserve(dockWidgets.size());\n    QString name;\n    for (const auto &it : dockWidgets) {\n        name = it->objectName();\n        if (name.split(';').at(0) == widgetType) {\n            docks.push_back(name);\n        }\n    }\n\n    if (docks.isEmpty()) {\n        return widgetType;\n    }\n\n    int id = 0;\n    while (docks.contains(widgetType + \";\" + QString::number(id))) {\n        id++;\n    }\n\n    return widgetType + \";\" + QString::number(id);\n}\n\nvoid MainWindow::showMemoryWidget()\n{\n    if (lastSyncMemoryWidget) {\n        if (lastSyncMemoryWidget->tryRaiseMemoryWidget()) {\n            return;\n        }\n    }\n    showMemoryWidget(MemoryWidgetType::Disassembly);\n}\n\nvoid MainWindow::showMemoryWidget(MemoryWidgetType type)\n{\n    for (auto &dock : dockWidgets) {\n        if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {\n            if (memoryWidget->getType() == type && memoryWidget->getSeekable()->isSynchronized()) {\n                memoryWidget->tryRaiseMemoryWidget();\n                return;\n            }\n        }\n    }\n    auto memoryDockWidget = addNewMemoryWidget(type, Core()->getOffset());\n    memoryDockWidget->raiseMemoryWidget();\n}\n\nQMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType)\n{\n    QMenu *menu = new QMenu(parent);\n    // Memory dock widgets\n    for (auto &dock : dockWidgets) {\n        if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {\n            if (memoryWidget->getType() == MemoryWidgetType::Graph\n                || memoryWidget->getType() == MemoryWidgetType::Decompiler) {\n                if (addressType == AddressTypeHint::Data) {\n                    continue;\n                }\n            }\n            QAction *action = new QAction(memoryWidget->windowTitle(), menu);\n            connect(action, &QAction::triggered, this, [memoryWidget, address]() {\n                memoryWidget->getSeekable()->seek(address);\n                memoryWidget->raiseMemoryWidget();\n            });\n            menu->addAction(action);\n        }\n    }\n    menu->addSeparator();\n    // Rest of the AddressableDockWidgets that weren't added already\n    for (auto &dock : dockWidgets) {\n        if (auto memoryWidget = qobject_cast<AddressableDockWidget *>(dock)) {\n            if (qobject_cast<MemoryDockWidget *>(dock)) {\n                continue;\n            }\n            QAction *action = new QAction(memoryWidget->windowTitle(), menu);\n            connect(action, &QAction::triggered, this, [memoryWidget, address]() {\n                memoryWidget->getSeekable()->seek(address);\n                memoryWidget->raiseMemoryWidget();\n            });\n            menu->addAction(action);\n        }\n    }\n    menu->addSeparator();\n    auto createAddNewWidgetAction = [this, menu, address](QString label, MemoryWidgetType type) {\n        QAction *action = new QAction(label, menu);\n        connect(action, &QAction::triggered, this,\n                [this, address, type]() { addNewMemoryWidget(type, address, false); });\n        menu->addAction(action);\n    };\n    createAddNewWidgetAction(tr(\"New disassembly\"), MemoryWidgetType::Disassembly);\n    if (addressType != AddressTypeHint::Data) {\n        createAddNewWidgetAction(tr(\"New graph\"), MemoryWidgetType::Graph);\n    }\n    createAddNewWidgetAction(tr(\"New hexdump\"), MemoryWidgetType::Hexdump);\n    createAddNewWidgetAction(tr(\"New Decompiler\"), MemoryWidgetType::Decompiler);\n    return menu;\n}\n\nvoid MainWindow::setCurrentMemoryWidget(MemoryDockWidget *memoryWidget)\n{\n    if (memoryWidget->getSeekable()->isSynchronized()) {\n        lastSyncMemoryWidget = memoryWidget;\n    }\n    lastMemoryWidget = memoryWidget;\n}\n\nMemoryDockWidget *MainWindow::getLastMemoryWidget()\n{\n    return lastMemoryWidget;\n}\n\nMemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA address,\n                                                 bool synchronized)\n{\n    MemoryDockWidget *memoryWidget = nullptr;\n    switch (type) {\n    case MemoryWidgetType::Graph:\n        memoryWidget = new GraphWidget(this);\n        break;\n    case MemoryWidgetType::Hexdump:\n        memoryWidget = new HexdumpWidget(this);\n        break;\n    case MemoryWidgetType::Disassembly:\n        memoryWidget = new DisassemblyWidget(this);\n        break;\n    case MemoryWidgetType::Decompiler:\n        memoryWidget = new DecompilerWidget(this);\n        break;\n    case MemoryWidgetType::CallGraph:\n        memoryWidget = new CallGraphWidget(this, false);\n        break;\n    case MemoryWidgetType::GlobalCallGraph:\n        memoryWidget = new CallGraphWidget(this, true);\n        break;\n    }\n    auto seekable = memoryWidget->getSeekable();\n    seekable->setSynchronization(synchronized);\n    seekable->seek(address);\n    addExtraWidget(memoryWidget);\n    return memoryWidget;\n}\n\nvoid MainWindow::initBackForwardMenu()\n{\n    auto prepareButtonMenu = [this](QAction *action) -> QMenu * {\n        QToolButton *button = qobject_cast<QToolButton *>(ui->mainToolBar->widgetForAction(action));\n        if (!button) {\n            return nullptr;\n        }\n        QMenu *menu = new QMenu(button);\n        button->setMenu(menu);\n        button->setPopupMode(QToolButton::DelayedPopup);\n        button->setContextMenuPolicy(Qt::CustomContextMenu);\n        connect(button, &QWidget::customContextMenuRequested, button,\n                [menu, button](const QPoint &pos) { menu->exec(button->mapToGlobal(pos)); });\n\n        QFontMetrics metrics(font());\n        // Roughly 10-16 lines depending on padding size, no need to calculate more precisely\n        menu->setMaximumHeight(metrics.lineSpacing() * 20);\n\n        menu->setToolTipsVisible(true);\n        return menu;\n    };\n\n    if (auto menu = prepareButtonMenu(ui->actionBackward)) {\n        menu->setObjectName(\"historyMenu\");\n        connect(menu, &QMenu::aboutToShow, menu,\n                [this, menu]() { updateHistoryMenu(menu, false); });\n    }\n    if (auto menu = prepareButtonMenu(ui->actionForward)) {\n        menu->setObjectName(\"forwardHistoryMenu\");\n        connect(menu, &QMenu::aboutToShow, menu, [this, menu]() { updateHistoryMenu(menu, true); });\n    }\n}\n\nvoid MainWindow::updateHistoryMenu(QMenu *menu, bool redo)\n{\n    // Not too long so that whole screen doesn't get covered,\n    // not too short so that reasonable length c++ names can be seen most of the time\n    const int MAX_NAME_LENGTH = 64;\n\n    RzListIter *it;\n    RzCoreSeekItem *undo;\n    RzCoreLocked core(Core());\n    RzList *list = rz_core_seek_list(core);\n\n    bool history = true;\n    QList<QAction *> actions;\n    CutterRzListForeach (list, it, RzCoreSeekItem, undo) {\n        RzFlagItem *f = rz_flag_get_at(core->flags, undo->offset, true);\n        char *fname = NULL;\n        if (f) {\n            if (f->offset != undo->offset) {\n                fname = rz_str_newf(\"%s+%\" PFMT64d, f->name, undo->offset - f->offset);\n            } else {\n                fname = strdup(f->name);\n            }\n        }\n        QString name = fname;\n        RVA offset = undo->offset;\n        bool current = undo->is_current;\n        if (current) {\n            history = false;\n        }\n        if (history != redo || current) { // Include current in both directions\n            QString addressString = RzAddressString(offset);\n\n            QString toolTip =\n                    QString(\"%1 %2\").arg(addressString, name); // show non truncated name in tooltip\n\n            name.truncate(MAX_NAME_LENGTH); // TODO:#1904 use common name shortening function\n            QString label = QString(\"%1 (%2)\").arg(name, addressString);\n            if (current) {\n                label = tr(\"current position (%1)\").arg(addressString);\n            }\n            QAction *action = new QAction(label, menu);\n            action->setToolTip(toolTip);\n            actions.push_back(action);\n            if (current) {\n                QFont font;\n                font.setBold(true);\n                action->setFont(font);\n            }\n        }\n    }\n    if (!redo) {\n        std::reverse(actions.begin(), actions.end());\n    }\n    menu->clear();\n    menu->addActions(actions);\n    int steps = 0;\n    for (QAction *item : menu->actions()) {\n        if (redo) {\n            connect(item, &QAction::triggered, item, [steps]() {\n                for (int i = 0; i < steps; i++) {\n                    Core()->seekNext();\n                }\n            });\n        } else {\n            connect(item, &QAction::triggered, item, [steps]() {\n                for (int i = 0; i < steps; i++) {\n                    Core()->seekPrev();\n                }\n            });\n        }\n        ++steps;\n    }\n}\n\nvoid MainWindow::updateLayoutsMenu()\n{\n    ui->menuLayouts->clear();\n    for (auto it = layouts.begin(), end = layouts.end(); it != end; ++it) {\n        QString name = it.key();\n        if (isBuiltinLayoutName(name)) {\n            continue;\n        }\n        auto action = new QAction(it.key(), ui->menuLayouts);\n        connect(action, &QAction::triggered, this,\n                [this, name]() { setViewLayout(getViewLayout(name)); });\n        ui->menuLayouts->addAction(action);\n    }\n}\n\nvoid MainWindow::saveNamedLayout()\n{\n    bool ok = false;\n    QString name;\n    QStringList names = layouts.keys();\n    names.removeAll(LAYOUT_DEBUG);\n    names.removeAll(LAYOUT_DEFAULT);\n    while (name.isEmpty() || isBuiltinLayoutName(name)) {\n        if (ok) {\n            QMessageBox::warning(this, tr(\"Save layout error\"),\n                                 tr(\"'%1' is not a valid name.\").arg(name));\n        }\n        name = QInputDialog::getItem(this, tr(\"Save layout\"), tr(\"Enter name\"), names, -1, true,\n                                     &ok);\n        if (!ok) {\n            return;\n        }\n    }\n    layouts[name] = getViewLayout();\n    updateLayoutsMenu();\n    saveSettings();\n}\n\nvoid MainWindow::manageLayouts()\n{\n    LayoutManager layoutManger(layouts, this);\n    layoutManger.exec();\n    updateLayoutsMenu();\n}\n\nvoid MainWindow::addWidget(CutterDockWidget *widget)\n{\n    dockWidgets.push_back(widget);\n}\n\nvoid MainWindow::addMemoryDockWidget(MemoryDockWidget *widget)\n{\n    connect(widget, &QDockWidget::visibilityChanged, this, [this, widget](bool visibility) {\n        if (visibility) {\n            setCurrentMemoryWidget(widget);\n        }\n    });\n}\n\nvoid MainWindow::removeWidget(CutterDockWidget *widget)\n{\n    dockWidgets.removeAll(widget);\n    pluginDocks.removeAll(widget);\n    if (lastSyncMemoryWidget == widget) {\n        lastSyncMemoryWidget = nullptr;\n    }\n    if (lastMemoryWidget == widget) {\n        lastMemoryWidget = nullptr;\n    }\n}\n\nvoid MainWindow::showZenDocks()\n{\n    const QList<QDockWidget *> zenDocks = { functionsDock, dashboardDock, stringsDock, searchDock,\n                                            importsDock };\n    functionDockWidthToRestore = functionsDock->maximumWidth();\n    functionsDock->setMaximumWidth(200);\n    for (auto w : dockWidgets) {\n        if (zenDocks.contains(w) || isExtraMemoryWidget(w)) {\n            w->show();\n        }\n    }\n    dashboardDock->raise();\n}\n\nvoid MainWindow::showDebugDocks()\n{\n    QList<QDockWidget *> debugDocks = {\n        functionsDock, stringsDock, searchDock,    stackDock,      registersDock,\n        backtraceDock, threadsDock, memoryMapDock, breakpointDock,\n    };\n    if (QSysInfo::kernelType() == \"linux\" || Core()->currentlyRemoteDebugging) {\n        debugDocks.append(heapDock);\n    }\n    functionDockWidthToRestore = functionsDock->maximumWidth();\n    functionsDock->setMaximumWidth(200);\n    auto registerWidth = qhelpers::forceWidth(registersDock, std::min(500, this->width() / 4));\n    auto registerHeight = qhelpers::forceHeight(registersDock, std::max(100, height() / 2));\n    QDockWidget *widgetToFocus = nullptr;\n    for (auto w : dockWidgets) {\n        if (debugDocks.contains(w) || isExtraMemoryWidget(w)) {\n            w->show();\n        }\n        if (qobject_cast<DisassemblyWidget *>(w)) {\n            widgetToFocus = w;\n        }\n    }\n    registerHeight.restoreHeight(registersDock);\n    registerWidth.restoreWidth(registersDock);\n    if (widgetToFocus) {\n        widgetToFocus->raise();\n    }\n}\n\nvoid MainWindow::dockOnMainArea(QDockWidget *widget)\n{\n    QDockWidget *best = nullptr;\n    float bestScore = 1;\n    // choose best existing area for placing the new widget\n    for (auto dock : dockWidgets) {\n        if (dock->isHidden() || dock == widget || dock->isFloating()\n            || // tabifying onto floating dock using code doesn't work well\n            dock->parentWidget() != this) { // floating group isn't considered floating\n            continue;\n        }\n        float newScore = 0;\n        if (isExtraMemoryWidget(dock)) {\n            newScore += 10000000; // prefer existing disssasembly and graph widgets\n        }\n        newScore += dock->width() * dock->height(); // the bigger the better\n\n        if (newScore > bestScore) {\n            bestScore = newScore;\n            best = dock;\n        }\n    }\n    if (best) {\n        tabifyDockWidget(best, widget);\n    } else {\n        addDockWidget(Qt::TopDockWidgetArea, widget, Qt::Orientation::Horizontal);\n    }\n}\n\nvoid MainWindow::enableDebugWidgetsMenu(bool enable)\n{\n    for (QAction *action : ui->menuAddDebugWidgets->actions()) {\n        // The breakpoints menu should be available outside of debug\n        if (!action->text().contains(\"Breakpoints\")) {\n            action->setEnabled(enable);\n        }\n    }\n}\n\nCutterLayout MainWindow::getViewLayout()\n{\n    CutterLayout layout;\n    layout.geometry = saveGeometry();\n    layout.state = saveState();\n\n    for (auto dock : dockWidgets) {\n        QVariantMap properties;\n        if (auto cutterDock = qobject_cast<CutterDockWidget *>(dock)) {\n            properties = cutterDock->serializeViewProprties();\n        }\n        layout.viewProperties.insert(dock->objectName(), std::move(properties));\n    }\n    return layout;\n}\n\nCutterLayout MainWindow::getViewLayout(const QString &name)\n{\n    auto it = layouts.find(name);\n    if (it != layouts.end()) {\n        return *it;\n    }\n    return {};\n}\n\nvoid MainWindow::setViewLayout(const CutterLayout &layout)\n{\n    bool isDefault = layout.state.isEmpty() || layout.geometry.isEmpty();\n    bool isDebug = Core()->currentlyDebugging;\n\n    // make a copy to avoid iterating over container from which items are being removed\n    auto widgetsToClose = dockWidgets;\n\n    for (auto dock : widgetsToClose) {\n        dock->hide();\n        dock->close();\n        dock->setFloating(false); // tabifyDockWidget doesn't work if dock is floating\n        removeDockWidget(dock);\n    }\n\n    QStringList docksToCreate;\n    if (isDefault) {\n        docksToCreate =\n                QStringList { DisassemblyWidget::getWidgetType(), GraphWidget::getWidgetType(),\n                              HexdumpWidget::getWidgetType(), DecompilerWidget::getWidgetType() };\n    } else {\n        docksToCreate = layout.viewProperties.keys();\n    }\n\n    for (const auto &it : docksToCreate) {\n        if (std::none_of(dockWidgets.constBegin(), dockWidgets.constEnd(),\n                         [&it](QDockWidget *w) { return w->objectName() == it; })) {\n            auto className = it.split(';').at(0);\n            if (widgetTypeToConstructorMap.contains(className)) {\n                auto widget = widgetTypeToConstructorMap[className](this);\n                widget->setObjectName(it);\n                addExtraWidget(widget);\n            }\n        }\n    }\n\n    restoreDocks();\n\n    QList<QDockWidget *> newDocks;\n\n    for (auto dock : dockWidgets) {\n        auto properties = layout.viewProperties.find(dock->objectName());\n        if (properties != layout.viewProperties.end()) {\n            dock->deserializeViewProperties(*properties);\n        } else {\n            dock->deserializeViewProperties({}); // call with empty properties to reset the widget\n            newDocks.push_back(dock);\n        }\n        dock->ignoreVisibilityStatus(true);\n    }\n\n    if (!isDefault) {\n        restoreState(layout.state);\n\n        for (auto dock : newDocks) {\n            dock->hide(); // hide to prevent dockOnMainArea putting them on each other\n        }\n        for (auto dock : newDocks) {\n            dockOnMainArea(dock);\n            // Show any new docks by default.\n            // Showing new builtin docks helps discovering features added in latest release.\n            // Installing a new plugin hints that usre will likely want to use it.\n            dock->show();\n        }\n    } else {\n        if (isDebug) {\n            showDebugDocks();\n        } else {\n            showZenDocks();\n        }\n    }\n\n    for (auto dock : dockWidgets) {\n        dock->ignoreVisibilityStatus(false);\n    }\n    lastSyncMemoryWidget = nullptr;\n    lastMemoryWidget = nullptr;\n}\n\nvoid MainWindow::loadLayouts(QSettings &settings)\n{\n    this->layouts.clear();\n    int size = settings.beginReadArray(\"layouts\");\n    for (int i = 0; i < size; i++) {\n        CutterLayout layout;\n        settings.setArrayIndex(i);\n        QString name = settings.value(\"name\", \"layout\").toString();\n        layout.geometry = settings.value(\"geometry\").toByteArray();\n        layout.state = settings.value(\"state\").toByteArray();\n\n        auto docks = settings.value(\"docks\").toMap();\n        for (auto it = docks.begin(), end = docks.end(); it != end; it++) {\n            layout.viewProperties.insert(it.key(), it.value().toMap());\n        }\n\n        layouts.insert(name, std::move(layout));\n    }\n    settings.endArray();\n    updateLayoutsMenu();\n}\n\nvoid MainWindow::saveLayouts(QSettings &settings)\n{\n    settings.beginWriteArray(\"layouts\", layouts.size());\n    int arrayIndex = 0;\n    for (auto it = layouts.begin(), end = layouts.end(); it != end; ++it, ++arrayIndex) {\n        settings.setArrayIndex(arrayIndex);\n        settings.setValue(\"name\", it.key());\n        auto &layout = it.value();\n        settings.setValue(\"state\", layout.state);\n        settings.setValue(\"geometry\", layout.geometry);\n        QVariantMap properties;\n        for (auto it = layout.viewProperties.begin(), end = layout.viewProperties.end(); it != end;\n             ++it) {\n            properties.insert(it.key(), it.value());\n        }\n        settings.setValue(\"docks\", properties);\n    }\n    settings.endArray();\n}\n\nvoid MainWindow::on_actionDefault_triggered()\n{\n    if (core->currentlyDebugging) {\n        layouts[LAYOUT_DEBUG] = {};\n        setViewLayout(layouts[LAYOUT_DEBUG]);\n    } else {\n        layouts[LAYOUT_DEFAULT] = {};\n        setViewLayout(layouts[LAYOUT_DEFAULT]);\n    }\n}\n\n/**\n * @brief MainWindow::on_actionNew_triggered\n * Open a new Cutter session.\n */\nvoid MainWindow::on_actionNew_triggered()\n{\n    // Create a new Cutter process\n    static_cast<CutterApplication *>(qApp)->launchNewInstance();\n}\n\nvoid MainWindow::on_actionSave_triggered()\n{\n    showProjectSaveError(saveProject(nullptr));\n}\n\nvoid MainWindow::on_actionSaveAs_triggered()\n{\n    showProjectSaveError(saveProjectAs(nullptr));\n}\n\nvoid MainWindow::on_actionRun_Script_triggered()\n{\n    QFileDialog dialog(this);\n    dialog.setFileMode(QFileDialog::ExistingFile);\n    dialog.setViewMode(QFileDialog::Detail);\n    dialog.setDirectory(QDir::home());\n\n    const QString &fileName =\n            QDir::toNativeSeparators(dialog.getOpenFileName(this, tr(\"Select Rizin script\")));\n    if (fileName.isEmpty()) // Cancel was pressed\n        return;\n\n    RunScriptTask *runScriptTask = new RunScriptTask();\n    runScriptTask->setFileName(fileName);\n\n    AsyncTask::Ptr runScriptTaskPtr(runScriptTask);\n\n    AsyncTaskDialog *taskDialog = new AsyncTaskDialog(runScriptTaskPtr, this);\n    taskDialog->setInterruptOnClose(true);\n    taskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    taskDialog->show();\n\n    Core()->getAsyncTaskManager()->start(runScriptTaskPtr);\n}\n\n/**\n * @brief MainWindow::on_actionOpen_triggered\n * Open a file as in \"load (add) a file in current session\".\n */\nvoid MainWindow::on_actionMap_triggered()\n{\n    MapFileDialog dialog(this);\n    dialog.exec();\n}\n\nvoid MainWindow::toggleResponsive(bool maybe)\n{\n    this->responsive = maybe;\n    // Save options in settings\n    QSettings settings;\n    settings.setValue(\"responsive\", this->responsive);\n}\n\nvoid MainWindow::on_actionTabs_on_Top_triggered()\n{\n    this->on_actionTabs_triggered();\n}\n\nvoid MainWindow::on_actionReset_settings_triggered()\n{\n    QMessageBox::StandardButton ret = (QMessageBox::StandardButton)QMessageBox::question(\n            this, APPNAME, tr(\"Do you really want to clear all settings?\"),\n            QMessageBox::Ok | QMessageBox::Cancel);\n    if (ret == QMessageBox::Ok) {\n        Config()->resetAll();\n        readSettings();\n        setViewLayout(getViewLayout(Core()->currentlyDebugging ? LAYOUT_DEBUG : LAYOUT_DEFAULT));\n    }\n}\n\nvoid MainWindow::on_actionQuit_triggered()\n{\n    close();\n}\n\nvoid MainWindow::on_actionBackward_triggered()\n{\n    core->seekPrev();\n}\n\nvoid MainWindow::on_actionForward_triggered()\n{\n    core->seekNext();\n}\n\nvoid MainWindow::on_actionDisasAdd_comment_triggered()\n{\n    CommentsDialog c(this);\n    c.exec();\n}\n\nvoid MainWindow::on_actionRefresh_contents_triggered()\n{\n    refreshAll();\n}\n\nvoid MainWindow::on_actionPreferences_triggered()\n{\n    if (!findChild<PreferencesDialog *>()) {\n        auto dialog = new PreferencesDialog(this);\n        dialog->show();\n    }\n}\n\nvoid MainWindow::on_actionTabs_triggered()\n{\n    tabsOnTop = !tabsOnTop;\n    setTabLocation();\n}\n\nvoid MainWindow::on_actionBaseFind_triggered()\n{\n    auto dialog = new BaseFindDialog(this);\n    dialog->show();\n}\n\nvoid MainWindow::on_actionAbout_triggered()\n{\n    AboutDialog *a = new AboutDialog(this);\n    a->setAttribute(Qt::WA_DeleteOnClose);\n    a->open();\n}\n\nvoid MainWindow::on_actionIssue_triggered()\n{\n    openIssue();\n}\n\nvoid MainWindow::documentationClicked()\n{\n    QDesktopServices::openUrl(QUrl(\"https://cutter.re/docs/user-docs\"));\n}\n\nvoid MainWindow::on_actionRefresh_Panels_triggered()\n{\n    this->refreshAll();\n}\n\n/**\n * @brief A signal that creates an AsyncTask to re-analyze the current file\n */\nvoid MainWindow::on_actionAnalyze_triggered()\n{\n    auto *analysisTask = new AnalysisTask();\n    InitialOptions options;\n    options.analysisCmd = { { \"aaa\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Auto analysis\") } };\n    analysisTask->setOptions(options);\n    AsyncTask::Ptr analysisTaskPtr(analysisTask);\n\n    auto *taskDialog = new AsyncTaskDialog(analysisTaskPtr);\n    taskDialog->setInterruptOnClose(true);\n    taskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    taskDialog->show();\n    connect(analysisTask, &AnalysisTask::finished, this, &MainWindow::refreshAll);\n\n    Core()->getAsyncTaskManager()->start(analysisTaskPtr);\n}\n\nvoid MainWindow::on_actionImportPDB_triggered()\n{\n    QFileDialog dialog(this);\n    dialog.setWindowTitle(tr(\"Select PDB file\"));\n    dialog.setNameFilters({ tr(\"PDB file (*.pdb)\"), tr(\"All files (*)\") });\n\n    if (!dialog.exec()) {\n        return;\n    }\n\n    const QString &pdbFile = QDir::toNativeSeparators(dialog.selectedFiles().first());\n\n    if (!pdbFile.isEmpty()) {\n        core->loadPDB(pdbFile);\n        core->message(tr(\"%1 loaded.\").arg(pdbFile));\n        this->refreshAll();\n    }\n}\n\nvoid MainWindow::on_actionExport_as_code_triggered()\n{\n    QStringList filters;\n    QMap<QString, RzLangByteArrayType> typMap;\n    const bool big_endian = Core()->getConfigb(\"big_endian\");\n\n    filters << tr(\"C uin8_t array (*.c)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_C_CPP_BYTES;\n\n#define TYPE_BIG_ENDIAN(type, big_endian) big_endian ? type##_BE : type##_LE\n\n    filters << tr(\"C uin16_t array (*.c)\");\n    typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_HALFWORDS, big_endian);\n    filters << tr(\"C uin32_t array (*.c)\");\n    typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_WORDS, big_endian);\n    filters << tr(\"C uin64_t array (*.c)\");\n    typMap[filters.last()] = TYPE_BIG_ENDIAN(RZ_LANG_BYTE_ARRAY_C_CPP_DOUBLEWORDS, big_endian);\n\n#undef TYPE_BIG_ENDIAN\n\n    filters << tr(\"Go array (*.go)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_GOLANG;\n    filters << tr(\"Java array (*.java)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JAVA;\n    filters << tr(\"JSON array (*.json)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_JSON;\n    filters << tr(\"Kotlin array (*.kt)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_KOTLIN;\n\n    filters << tr(\"Javascript array (*.js)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_NODEJS;\n    filters << tr(\"ObjectiveC array (*.m)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_OBJECTIVE_C;\n    filters << tr(\"Python array (*.py)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_PYTHON;\n    filters << tr(\"Rust array (*.rs)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_RUST;\n\n    filters << tr(\"Swift array (*.swift)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_SWIFT;\n    filters << tr(\"Print 'wx' Rizin commands (*.rz)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_RIZIN;\n    filters << tr(\"Shell-script that reconstructs the bin (*.sh)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_BASH;\n    filters << tr(\"GAS .byte blob (*.asm, *.s)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_ASM;\n\n    filters << tr(\"Yara (*.yar)\");\n    typMap[filters.last()] = RZ_LANG_BYTE_ARRAY_YARA;\n    /* special case */\n    QString instructionsInComments = tr(\".bytes with instructions in comments (*.txt)\");\n    filters << instructionsInComments;\n\n    QFileDialog dialog(this, tr(\"Export as code\"));\n    dialog.setAcceptMode(QFileDialog::AcceptSave);\n    dialog.setFileMode(QFileDialog::AnyFile);\n    dialog.setNameFilters(filters);\n    dialog.selectFile(\"dump\");\n    dialog.setDefaultSuffix(\"c\");\n    if (!dialog.exec())\n        return;\n\n    QFile file(dialog.selectedFiles()[0]);\n    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {\n        qWarning() << tr(\"Can't open file\");\n        return;\n    }\n\n    TempConfig tempConfig;\n    tempConfig.set(\"io.va\", false);\n    QTextStream fileOut(&file);\n    auto ps = core->seekTemp(0);\n    auto rc = core->lock();\n    const auto size = static_cast<int>(rz_io_fd_size(rc->io, rc->file->fd));\n    auto buffer = std::vector<ut8>(size);\n    if (!rz_io_read_at_mapped(rc->io, 0, buffer.data(), size)) {\n        return;\n    }\n\n    auto string = fromOwned(\n            dialog.selectedNameFilter() != instructionsInComments\n                    ? rz_lang_byte_array(buffer.data(), size, typMap[dialog.selectedNameFilter()])\n                    : rz_core_print_bytes_with_inst(rc, buffer.data(), 0, size));\n    fileOut << string.get();\n}\n\nvoid MainWindow::on_actionApplySigFromFile_triggered()\n{\n    QStringList filters;\n    filters << tr(\"Signature File (*.sig)\");\n    filters << tr(\"Pattern File (*.pat)\");\n\n    QFileDialog dialog(this);\n    dialog.setWindowTitle(tr(\"Apply Signature From File\"));\n    dialog.setNameFilters(filters);\n\n    if (!dialog.exec()) {\n        return;\n    }\n\n    const QString &sigfile = QDir::toNativeSeparators(dialog.selectedFiles().first());\n\n    if (!sigfile.isEmpty()) {\n        core->applySignature(sigfile);\n        this->refreshAll();\n    }\n}\n\nvoid MainWindow::on_actionCreateNewSig_triggered()\n{\n    QStringList filters;\n    filters << tr(\"Signature File (*.sig)\");\n    filters << tr(\"Pattern File (*.pat)\");\n\n    QFileDialog dialog(this, tr(\"Create New Signature File\"));\n    dialog.setAcceptMode(QFileDialog::AcceptSave);\n    dialog.setFileMode(QFileDialog::AnyFile);\n    dialog.setNameFilters(filters);\n    dialog.selectFile(\"\");\n    dialog.setDefaultSuffix(\"sig\");\n    if (!dialog.exec())\n        return;\n\n    const QString &sigfile = QDir::toNativeSeparators(dialog.selectedFiles().first());\n\n    if (!sigfile.isEmpty()) {\n        core->createSignature(sigfile);\n    }\n}\n\nvoid MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked)\n{\n#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)\n    auto options = dockOptions();\n    options.setFlag(QMainWindow::DockOption::GroupedDragging, checked);\n    setDockOptions(options);\n#else\n    Q_UNUSED(checked);\n#endif\n}\n\nvoid MainWindow::seekToFunctionLastInstruction()\n{\n    Core()->seek(Core()->getLastFunctionInstruction(Core()->getOffset()));\n}\n\nvoid MainWindow::seekToFunctionStart()\n{\n    Core()->seek(Core()->getFunctionStart(Core()->getOffset()));\n}\n\nvoid MainWindow::toggleDebugView()\n{\n    MemoryWidgetType memType = getMemoryWidgetTypeToRestore();\n    if (Core()->currentlyDebugging) {\n        layouts[LAYOUT_DEFAULT] = getViewLayout();\n        setViewLayout(getViewLayout(LAYOUT_DEBUG));\n        enableDebugWidgetsMenu(true);\n    } else {\n        layouts[LAYOUT_DEBUG] = getViewLayout();\n        setViewLayout(getViewLayout(LAYOUT_DEFAULT));\n        enableDebugWidgetsMenu(false);\n    }\n    showMemoryWidget(memType);\n}\n\nvoid MainWindow::mousePressEvent(QMouseEvent *event)\n{\n    switch (event->button()) {\n    case Qt::BackButton:\n        core->seekPrev();\n        break;\n    case Qt::ForwardButton:\n        core->seekNext();\n        break;\n    default:\n        break;\n    }\n}\n\nbool MainWindow::eventFilter(QObject *obj, QEvent *event)\n{\n    // For every create event - disable context help and proceed to next event check\n    if (event->type() == QEvent::Create) {\n        if (obj->isWidgetType()) {\n            auto w = static_cast<QWidget *>(obj);\n            w->setWindowFlags(w->windowFlags() & (~Qt::WindowContextHelpButtonHint));\n        }\n    }\n\n    if (event->type() == QEvent::MouseButtonPress) {\n        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);\n        if (mouseEvent->button() == Qt::ForwardButton || mouseEvent->button() == Qt::BackButton) {\n            mousePressEvent(mouseEvent);\n            return true;\n        }\n    }\n    return false;\n}\n\nbool MainWindow::event(QEvent *event)\n{\n    if (event->type() == QEvent::FontChange || event->type() == QEvent::StyleChange\n        || event->type() == QEvent::PaletteChange) {\n#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)\n        QMetaObject::invokeMethod(Config(), \"refreshFont\", Qt::ConnectionType::QueuedConnection);\n#else\n        QMetaObject::invokeMethod(Config(), &Configuration::refreshFont,\n                                  Qt::ConnectionType::QueuedConnection);\n#endif\n    }\n    return QMainWindow::event(event);\n}\n\n/**\n * @brief Show a warning message box.\n *\n * This API can either be used in Cutter internals, or by Python plugins.\n */\nvoid MainWindow::messageBoxWarning(QString title, QString message)\n{\n    QMessageBox mb(this);\n    mb.setIcon(QMessageBox::Warning);\n    mb.setStandardButtons(QMessageBox::Ok);\n    mb.setWindowTitle(title);\n    mb.setText(message);\n    mb.exec();\n}\n\n/**\n * @brief When theme changed, change icons which have a special version for the theme.\n */\nvoid MainWindow::chooseThemeIcons()\n{\n    // List of QActions which have alternative icons in different themes\n    const QList<QPair<void *, QString>> kSupportedIconsNames {\n        { ui->actionForward, QStringLiteral(\"arrow_right.svg\") },\n        { ui->actionBackward, QStringLiteral(\"arrow_left.svg\") },\n    };\n\n    // Set the correct icon for the QAction\n    qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) {\n        static_cast<QAction *>(obj)->setIcon(icon);\n    });\n}\n\nvoid MainWindow::onZoomIn()\n{\n    Config()->setZoomFactor(Config()->getZoomFactor() + 0.1);\n}\n\nvoid MainWindow::onZoomOut()\n{\n    Config()->setZoomFactor(Config()->getZoomFactor() - 0.1);\n}\n\nvoid MainWindow::onZoomReset()\n{\n    Config()->setZoomFactor(1.0);\n}\n\nQMenu *MainWindow::getContextMenuExtensions(ContextMenuType type)\n{\n    switch (type) {\n    case ContextMenuType::Disassembly:\n        return disassemblyContextMenuExtensions;\n    case ContextMenuType::Addressable:\n        return addressableContextMenuExtensions;\n    default:\n        return nullptr;\n    }\n}\n\nvoid MainWindow::setAvailableIOModeOptions()\n{\n    switch (ioModesController.getIOMode()) {\n    case IOModesController::Mode::WRITE:\n        ui->actionWriteMode->setChecked(true);\n        break;\n    case IOModesController::Mode::CACHE:\n        ui->actionCacheMode->setChecked(true);\n        break;\n    default:\n        ui->actionReadOnly->setChecked(true);\n    }\n}\n"
  },
  {
    "path": "src/core/MainWindow.h",
    "content": "#ifndef MAINWINDOW_H\n#define MAINWINDOW_H\n\n#include \"core/Cutter.h\" // only needed for ut64\n#include \"dialogs/NewFileDialog.h\"\n#include \"dialogs/WelcomeDialog.h\"\n#include \"common/Configuration.h\"\n#include \"common/InitialOptions.h\"\n#include \"common/IOModesController.h\"\n#include \"common/CutterLayout.h\"\n#include \"MemoryDockWidget.h\"\n\n#include <memory>\n\n#include <QMainWindow>\n#include <QList>\n\nclass CutterCore;\nclass Omnibar;\nclass ProgressIndicator;\nclass PreviewWidget;\nclass Highlighter;\nclass AsciiHighlighter;\nclass VisualNavbar;\nclass FunctionsWidget;\nclass ImportsWidget;\nclass ExportsWidget;\nclass SymbolsWidget;\nclass GlobalsWidget;\nclass RelocsWidget;\nclass CommentsWidget;\nclass StringsWidget;\nclass FlagsWidget;\nclass Dashboard;\nclass QLineEdit;\nclass SdbWidget;\nclass QAction;\nclass SectionsWidget;\nclass SegmentsWidget;\nclass ConsoleWidget;\nclass EntrypointWidget;\nclass DisassemblerGraphView;\nclass ClassesWidget;\nclass ResourcesWidget;\nclass VTablesWidget;\nclass TypesWidget;\nclass HeadersWidget;\nclass FlirtWidget;\nclass SearchWidget;\nclass QDockWidget;\nclass DisassemblyWidget;\nclass GraphWidget;\nclass HexdumpWidget;\nclass DecompilerWidget;\nclass OverviewWidget;\nclass RizinGraphWidget;\nclass CallGraphWidget;\nclass HeapWidget;\n\nnamespace Ui {\nclass MainWindow;\n}\n\nclass CUTTER_EXPORT MainWindow : public QMainWindow\n{\n    Q_OBJECT\n\npublic:\n    bool responsive;\n\n    explicit MainWindow(QWidget *parent = nullptr);\n    ~MainWindow() override;\n\n    void openNewFile(InitialOptions &options, bool skipOptionsDialog = false);\n    void displayNewFileDialog();\n    void displayWelcomeDialog();\n    void closeNewFileDialog();\n    bool openProject(const QString &project_name);\n\n    RzProjectErr saveProject(bool *canceled);\n    RzProjectErr saveProjectAs(bool *canceled);\n    void showProjectSaveError(RzProjectErr err);\n\n    void closeEvent(QCloseEvent *event) override;\n    void paintEvent(QPaintEvent *event) override;\n    void readSettings();\n    void saveSettings();\n    void setFilename(const QString &fn);\n    void refreshOmniBar(const QStringList &flags);\n\n    void addWidget(CutterDockWidget *widget);\n    void addMemoryDockWidget(MemoryDockWidget *widget);\n    void removeWidget(CutterDockWidget *widget);\n    void addExtraWidget(CutterDockWidget *extraDock);\n    MemoryDockWidget *addNewMemoryWidget(MemoryWidgetType type, RVA address,\n                                         bool synchronized = true);\n\n    CUTTER_DEPRECATED(\"Action will be ignored. Use addPluginDockWidget(CutterDockWidget*) instead.\")\n    void addPluginDockWidget(CutterDockWidget *dockWidget, QAction *)\n    {\n        addPluginDockWidget(dockWidget);\n    }\n    void addPluginDockWidget(CutterDockWidget *dockWidget);\n    enum class MenuType { File, Edit, View, Windows, Debug, Help, Plugins };\n    /**\n     * @brief Getter for MainWindow's different menus\n     * @param type The type which represents the desired menu\n     * @return The requested menu or nullptr if \"type\" is invalid\n     */\n    QMenu *getMenuByType(MenuType type);\n    void addMenuFileAction(QAction *action);\n\n    QString getFilename() const { return filename; }\n    void messageBoxWarning(QString title, QString message);\n\n    QString getUniqueObjectName(const QString &widgetType) const;\n    void showMemoryWidget();\n    void showMemoryWidget(MemoryWidgetType type);\n    enum class AddressTypeHint { Function, Data, Unknown };\n    QMenu *createShowInMenu(QWidget *parent, RVA address,\n                            AddressTypeHint addressType = AddressTypeHint::Unknown);\n    void setCurrentMemoryWidget(MemoryDockWidget *memoryWidget);\n    MemoryDockWidget *getLastMemoryWidget();\n\n    /* Context menu plugins */\n    enum class ContextMenuType { Disassembly, Addressable };\n    /**\n     * @brief Fetches the pointer to a context menu extension of type\n     * @param type - the type of the context menu\n     * @return plugins submenu of the selected context menu\n     */\n    QMenu *getContextMenuExtensions(ContextMenuType type);\n\npublic slots:\n    void finalizeOpen();\n\n    void refreshAll();\n    void seekToFunctionLastInstruction();\n    void seekToFunctionStart();\n    void setTabLocation();\n\n    void on_actionTabs_triggered();\n\n    void on_actionAnalyze_triggered();\n\n    void lockDocks(bool lock);\n\n    void on_actionRun_Script_triggered();\n\n    void toggleResponsive(bool maybe);\n\n    void openNewFileFailed();\n\n    void toggleOverview(bool visibility, GraphWidget *targetGraph);\nprivate slots:\n    void on_actionBaseFind_triggered();\n    void on_actionAbout_triggered();\n    void on_actionIssue_triggered();\n    void documentationClicked();\n    void addExtraGraph();\n    void addExtraHexdump();\n    void addExtraDisassembly();\n    void addExtraDecompiler();\n\n    void on_actionRefresh_Panels_triggered();\n\n    void on_actionDisasAdd_comment_triggered();\n\n    void on_actionDefault_triggered();\n\n    void on_actionNew_triggered();\n\n    void on_actionSave_triggered();\n    void on_actionSaveAs_triggered();\n\n    void on_actionBackward_triggered();\n    void on_actionForward_triggered();\n\n    void on_actionMap_triggered();\n\n    void on_actionTabs_on_Top_triggered();\n\n    void on_actionReset_settings_triggered();\n\n    void on_actionQuit_triggered();\n\n    void on_actionRefresh_contents_triggered();\n\n    void on_actionPreferences_triggered();\n\n    void on_actionImportPDB_triggered();\n\n    void on_actionExport_as_code_triggered();\n\n    void on_actionApplySigFromFile_triggered();\n\n    void on_actionCreateNewSig_triggered();\n\n    void on_actionGrouped_dock_dragging_triggered(bool checked);\n\n    void updateTasksIndicator();\n\n    void mousePressEvent(QMouseEvent *event) override;\n    bool eventFilter(QObject *object, QEvent *event) override;\n    bool event(QEvent *event) override;\n    void toggleDebugView();\n    void chooseThemeIcons();\n\n    void onZoomIn();\n    void onZoomOut();\n    void onZoomReset();\n\n    void setAvailableIOModeOptions();\n\nprivate:\n    CutterCore *core;\n\n    bool tabsOnTop;\n    ut64 hexdumpTopOffset;\n    ut64 hexdumpBottomOffset;\n    QString filename;\n    std::unique_ptr<Ui::MainWindow> ui;\n    Highlighter *highlighter;\n    VisualNavbar *visualNavbar;\n    Omnibar *omnibar;\n    ProgressIndicator *tasksProgressIndicator;\n    QByteArray emptyState;\n    IOModesController ioModesController;\n\n    Configuration *configuration;\n\n    QList<CutterDockWidget *> dockWidgets;\n    QList<CutterDockWidget *> pluginDocks;\n    OverviewWidget *overviewDock = nullptr;\n    QAction *actionOverview = nullptr;\n    EntrypointWidget *entrypointDock = nullptr;\n    FunctionsWidget *functionsDock = nullptr;\n    ImportsWidget *importsDock = nullptr;\n    ExportsWidget *exportsDock = nullptr;\n    HeadersWidget *headersDock = nullptr;\n    TypesWidget *typesDock = nullptr;\n    SearchWidget *searchDock = nullptr;\n    SymbolsWidget *symbolsDock = nullptr;\n    GlobalsWidget *globalsDock = nullptr;\n    RelocsWidget *relocsDock = nullptr;\n    CommentsWidget *commentsDock = nullptr;\n    StringsWidget *stringsDock = nullptr;\n    FlagsWidget *flagsDock = nullptr;\n    Dashboard *dashboardDock = nullptr;\n    SdbWidget *sdbDock = nullptr;\n    SectionsWidget *sectionsDock = nullptr;\n    SegmentsWidget *segmentsDock = nullptr;\n    FlirtWidget *flirtDock = nullptr;\n    ConsoleWidget *consoleDock = nullptr;\n    ClassesWidget *classesDock = nullptr;\n    ResourcesWidget *resourcesDock = nullptr;\n    VTablesWidget *vTablesDock = nullptr;\n    CutterDockWidget *stackDock = nullptr;\n    CutterDockWidget *threadsDock = nullptr;\n    CutterDockWidget *processesDock = nullptr;\n    CutterDockWidget *registersDock = nullptr;\n    CutterDockWidget *backtraceDock = nullptr;\n    CutterDockWidget *memoryMapDock = nullptr;\n    NewFileDialog *newFileDialog = nullptr;\n    CutterDockWidget *breakpointDock = nullptr;\n    CutterDockWidget *registerRefsDock = nullptr;\n    RizinGraphWidget *rzGraphDock = nullptr;\n    CallGraphWidget *callGraphDock = nullptr;\n    CallGraphWidget *globalCallGraphDock = nullptr;\n    CutterDockWidget *heapDock = nullptr;\n\n    QMenu *disassemblyContextMenuExtensions = nullptr;\n    QMenu *addressableContextMenuExtensions = nullptr;\n\n    QMap<QString, Cutter::CutterLayout> layouts;\n\n    void initUI();\n    void initToolBar();\n    void initDocks();\n    void initBackForwardMenu();\n    void displayInitialOptionsDialog(const InitialOptions &options = InitialOptions(),\n                                     bool skipOptionsDialog = false);\n\n    Cutter::CutterLayout getViewLayout();\n    Cutter::CutterLayout getViewLayout(const QString &name);\n\n    void setViewLayout(const Cutter::CutterLayout &layout);\n    void loadLayouts(QSettings &settings);\n    void saveLayouts(QSettings &settings);\n\n    void updateMemberPointers();\n    void restoreDocks();\n    void showZenDocks();\n    void showDebugDocks();\n    /**\n     * @brief Try to guess which is the \"main\" section of layout and dock there.\n     * @param widget that needs to be docked\n     */\n    void dockOnMainArea(QDockWidget *widget);\n    void enableDebugWidgetsMenu(bool enable);\n    /**\n     * @brief Fill menu with seek history entries.\n     * @param menu\n     * @param redo set to false for undo history, true for redo.\n     */\n    void updateHistoryMenu(QMenu *menu, bool redo = false);\n    void updateLayoutsMenu();\n    void saveNamedLayout();\n    void manageLayouts();\n\n    void setOverviewData();\n    bool isOverviewActive();\n    /**\n     * @brief Check if a widget is one of debug specific dock widgets.\n     * @param dock\n     * @return true for debug specific widgets, false for all other including common dock widgets.\n     */\n    bool isDebugWidget(QDockWidget *dock) const;\n    bool isExtraMemoryWidget(QDockWidget *dock) const;\n\n    MemoryWidgetType getMemoryWidgetTypeToRestore();\n\n    /**\n     * @brief Map from a widget type (e.g. DisassemblyWidget::getWidgetType()) to the respective\n     * contructor of the widget\n     */\n    QMap<QString, std::function<CutterDockWidget *(MainWindow *)>> widgetTypeToConstructorMap;\n\n    MemoryDockWidget *lastSyncMemoryWidget = nullptr;\n    MemoryDockWidget *lastMemoryWidget = nullptr;\n    int functionDockWidthToRestore = 0;\n};\n\n#endif // MAINWINDOW_H\n"
  },
  {
    "path": "src/core/MainWindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1013</width>\n    <height>606</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Cutter</string>\n  </property>\n  <property name=\"styleSheet\">\n   <string notr=\"true\"/>\n  </property>\n  <property name=\"documentMode\">\n   <bool>true</bool>\n  </property>\n  <property name=\"dockNestingEnabled\">\n   <bool>true</bool>\n  </property>\n  <widget class=\"QMenu\" name=\"addExtraWidgets\">\n   <property name=\"title\">\n    <string>Add extra...</string>\n   </property>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menuBar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>1013</width>\n     <height>22</height>\n    </rect>\n   </property>\n   <property name=\"defaultUp\">\n    <bool>false</bool>\n   </property>\n   <property name=\"nativeMenuBar\">\n    <bool>true</bool>\n   </property>\n   <widget class=\"QMenu\" name=\"menuFile\">\n    <property name=\"geometry\">\n     <rect>\n      <x>2024</x>\n      <y>127</y>\n      <width>151</width>\n      <height>312</height>\n     </rect>\n    </property>\n    <property name=\"title\">\n     <string>File</string>\n    </property>\n    <widget class=\"QMenu\" name=\"menuSetMode\">\n     <property name=\"toolTip\">\n      <string/>\n     </property>\n     <property name=\"title\">\n      <string>Set mode</string>\n     </property>\n     <addaction name=\"actionWriteMode\"/>\n     <addaction name=\"actionCacheMode\"/>\n     <addaction name=\"actionReadOnly\"/>\n    </widget>\n    <addaction name=\"actionNew\"/>\n    <addaction name=\"actionMap\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionImportPDB\"/>\n    <addaction name=\"actionAnalyze\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionApplySigFromFile\"/>\n    <addaction name=\"actionCreateNewSig\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"menuSetMode\"/>\n    <addaction name=\"actionCommitChanges\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionSave\"/>\n    <addaction name=\"actionSaveAs\"/>\n    <addaction name=\"actionExport_as_code\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionRun_Script\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionQuit\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuView\">\n    <property name=\"title\">\n     <string>View</string>\n    </property>\n    <property name=\"tabsOnTop\" stdset=\"0\">\n     <bool>false</bool>\n    </property>\n    <widget class=\"QMenu\" name=\"menuZoom\">\n     <property name=\"title\">\n      <string>Zoom</string>\n     </property>\n     <addaction name=\"actionZoomIn\"/>\n     <addaction name=\"actionZoomOut\"/>\n     <addaction name=\"separator\"/>\n     <addaction name=\"actionZoomReset\"/>\n    </widget>\n    <widget class=\"QMenu\" name=\"menuLayouts\">\n     <property name=\"title\">\n      <string>Layouts</string>\n     </property>\n    </widget>\n    <addaction name=\"actionRefresh_contents\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionDefault\"/>\n    <addaction name=\"actionReset_settings\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionUnlock\"/>\n    <addaction name=\"actionTabs_on_Top\"/>\n    <addaction name=\"actionGrouped_dock_dragging\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"menuZoom\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionManageLayouts\"/>\n    <addaction name=\"actionSaveLayout\"/>\n    <addaction name=\"menuLayouts\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuTools\">\n    <property name=\"title\">\n     <string>Tools</string>\n    </property>\n    <addaction name=\"actionBaseFind\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuHelp\">\n    <property name=\"title\">\n     <string>Help</string>\n    </property>\n    <addaction name=\"actionAbout\"/>\n    <addaction name=\"actionIssue\"/>\n    <addaction name=\"actionDocumentation\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuEdit\">\n    <property name=\"title\">\n     <string>Edit</string>\n    </property>\n    <addaction name=\"actionBackward\"/>\n    <addaction name=\"actionForward\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionPreferences\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuWindows\">\n    <property name=\"title\">\n     <string>Windows</string>\n    </property>\n    <widget class=\"QMenu\" name=\"menuPlugins\">\n     <property name=\"title\">\n      <string>Plugins</string>\n     </property>\n    </widget>\n    <widget class=\"QMenu\" name=\"menuAddInfoWidgets\">\n     <property name=\"title\">\n      <string>Info...</string>\n     </property>\n    </widget>\n    <widget class=\"QMenu\" name=\"menuAddDebugWidgets\">\n     <property name=\"title\">\n      <string>Debug...</string>\n     </property>\n    </widget>\n    <addaction name=\"actionExtraDecompiler\"/>\n    <addaction name=\"actionExtraDisassembly\"/>\n    <addaction name=\"actionExtraGraph\"/>\n    <addaction name=\"actionExtraHexdump\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"menuAddInfoWidgets\"/>\n    <addaction name=\"menuAddDebugWidgets\"/>\n    <addaction name=\"menuPlugins\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuDebug\">\n    <property name=\"title\">\n     <string>Debug</string>\n    </property>\n   </widget>\n   <addaction name=\"menuFile\"/>\n   <addaction name=\"menuEdit\"/>\n   <addaction name=\"menuView\"/>\n   <addaction name=\"menuWindows\"/>\n   <addaction name=\"menuDebug\"/>\n   <addaction name=\"menuTools\"/>\n   <addaction name=\"menuHelp\"/>\n  </widget>\n  <widget class=\"QToolBar\" name=\"mainToolBar\">\n   <property name=\"sizePolicy\">\n    <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n     <horstretch>0</horstretch>\n     <verstretch>0</verstretch>\n    </sizepolicy>\n   </property>\n   <property name=\"contextMenuPolicy\">\n    <enum>Qt::DefaultContextMenu</enum>\n   </property>\n   <property name=\"windowTitle\">\n    <string>Main toolbar</string>\n   </property>\n   <property name=\"movable\">\n    <bool>false</bool>\n   </property>\n   <property name=\"iconSize\">\n    <size>\n     <width>16</width>\n     <height>16</height>\n    </size>\n   </property>\n   <property name=\"toolButtonStyle\">\n    <enum>Qt::ToolButtonIconOnly</enum>\n   </property>\n   <attribute name=\"toolBarArea\">\n    <enum>TopToolBarArea</enum>\n   </attribute>\n   <attribute name=\"toolBarBreak\">\n    <bool>false</bool>\n   </attribute>\n   <addaction name=\"actionBackward\"/>\n   <addaction name=\"actionForward\"/>\n  </widget>\n  <action name=\"actionDefault\">\n   <property name=\"text\">\n    <string>Reset to default layout</string>\n   </property>\n  </action>\n  <action name=\"actionZen\">\n   <property name=\"text\">\n    <string>Zen Mode</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Zen mode</string>\n   </property>\n  </action>\n  <action name=\"actionBaseFind\">\n   <property name=\"text\">\n    <string>BaseFind</string>\n   </property>\n  </action>\n  <action name=\"actionAbout\">\n   <property name=\"text\">\n    <string>About</string>\n   </property>\n  </action>\n  <action name=\"actionIssue\">\n   <property name=\"text\">\n    <string>Report an issue</string>\n   </property>\n  </action>\n  <action name=\"actionNew\">\n   <property name=\"text\">\n    <string>New Window</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+N</string>\n   </property>\n  </action>\n  <action name=\"actionClose\">\n   <property name=\"text\">\n    <string>Close</string>\n   </property>\n  </action>\n  <action name=\"actionSave_workspace\">\n   <property name=\"text\">\n    <string>Save layout</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Save layout</string>\n   </property>\n  </action>\n  <action name=\"actionDocumentation\">\n   <property name=\"text\">\n    <string>Documentation</string>\n   </property>\n  </action>\n  <action name=\"actionMap\">\n   <property name=\"text\">\n    <string>Map File</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+M</string>\n   </property>\n  </action>\n  <action name=\"actionSave\">\n   <property name=\"text\">\n    <string>Save Project</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+S</string>\n   </property>\n  </action>\n  <action name=\"actionCut\">\n   <property name=\"text\">\n    <string>Cut</string>\n   </property>\n  </action>\n  <action name=\"actionCopy\">\n   <property name=\"text\">\n    <string>Copy</string>\n   </property>\n  </action>\n  <action name=\"actionPaste\">\n   <property name=\"text\">\n    <string>Paste</string>\n   </property>\n  </action>\n  <action name=\"actionDelete\">\n   <property name=\"text\">\n    <string>Delete</string>\n   </property>\n  </action>\n  <action name=\"actionSelect_all\">\n   <property name=\"text\">\n    <string>Select all</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Select all</string>\n   </property>\n  </action>\n  <action name=\"actionFind\">\n   <property name=\"text\">\n    <string>Find</string>\n   </property>\n  </action>\n  <action name=\"actionFind_next\">\n   <property name=\"text\">\n    <string>Find next</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Find next</string>\n   </property>\n  </action>\n  <action name=\"actionFind_previous\">\n   <property name=\"text\">\n    <string>Find previous</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Find previous</string>\n   </property>\n  </action>\n  <action name=\"actionBackward\">\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/img/icons/arrow_left.svg</normaloff>:/img/icons/arrow_left.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Undo Seek</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Go back</string>\n   </property>\n  </action>\n  <action name=\"actionForward\">\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/img/icons/arrow_right.svg</normaloff>:/img/icons/arrow_right.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Redo Seek</string>\n   </property>\n  </action>\n  <action name=\"actionUnlock\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Unlock Panels</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Toggle panel locks</string>\n   </property>\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/lock</normaloff><normalon>:/unlock</normalon>:/unlock</iconset>\n   </property>\n   <property name=\"iconVisibleInMenu\">\n    <bool>false</bool>\n   </property>\n  </action>\n  <action name=\"actionTheme\">\n   <property name=\"checkable\">\n    <bool>false</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/img/icons/themes.svg</normaloff>:/img/icons/themes.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Theme</string>\n   </property>\n   <property name=\"autoRepeat\">\n    <bool>false</bool>\n   </property>\n   <property name=\"menuRole\">\n    <enum>QAction::PreferencesRole</enum>\n   </property>\n  </action>\n  <action name=\"actionTabs\">\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/img/icons/tabs.svg</normaloff>:/img/icons/tabs.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Tabs up/down</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Tabs up/down</string>\n   </property>\n  </action>\n  <action name=\"actionRefresh_Panels\">\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/img/icons/spin.svg</normaloff>:/img/icons/spin.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Refresh</string>\n   </property>\n  </action>\n  <action name=\"actionTabs_on_Top\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Show Tabs at the Top</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Toggle tab position</string>\n   </property>\n  </action>\n  <action name=\"actionDark_Theme\">\n   <property name=\"text\">\n    <string>Dark Theme</string>\n   </property>\n  </action>\n  <action name=\"actionLoad_workspace\">\n   <property name=\"text\">\n    <string>Load layout</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Load layout</string>\n   </property>\n  </action>\n  <action name=\"actionWhite_Theme\">\n   <property name=\"text\">\n    <string>Default Theme</string>\n   </property>\n  </action>\n  <action name=\"actionBindiff\">\n   <property name=\"text\">\n    <string>Bindiff</string>\n   </property>\n  </action>\n  <action name=\"actionAnalysis\">\n   <property name=\"text\">\n    <string>Analysis</string>\n   </property>\n  </action>\n  <action name=\"actionTest_menu\">\n   <property name=\"text\">\n    <string>Test menu</string>\n   </property>\n  </action>\n  <action name=\"actionHexCopy_Hexpair\">\n   <property name=\"text\">\n    <string>Copy hexpair</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Copy hexpair</string>\n   </property>\n  </action>\n  <action name=\"actionHexCopy_Text\">\n   <property name=\"text\">\n    <string>Copy text</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Copy text</string>\n   </property>\n  </action>\n  <action name=\"actionHexCopy_ASCII\">\n   <property name=\"text\">\n    <string>Copy ASCII</string>\n   </property>\n  </action>\n  <action name=\"actionHexInsert_String\">\n   <property name=\"text\">\n    <string>Insert string</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Insert string</string>\n   </property>\n  </action>\n  <action name=\"actionHexInsert_Hex\">\n   <property name=\"text\">\n    <string>Insert hex</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Insert hex</string>\n   </property>\n  </action>\n  <action name=\"actionHexEdit\">\n   <property name=\"text\">\n    <string>Edit</string>\n   </property>\n  </action>\n  <action name=\"actionHexPaste\">\n   <property name=\"text\">\n    <string>Paste</string>\n   </property>\n  </action>\n  <action name=\"actionDisas_ShowHideBytes\">\n   <property name=\"text\">\n    <string>Show/Hide bytes</string>\n   </property>\n  </action>\n  <action name=\"actionDisasSwitch_case\">\n   <property name=\"text\">\n    <string>Switch case</string>\n   </property>\n  </action>\n  <action name=\"actionDisasCopy_All\">\n   <property name=\"text\">\n    <string>Copy all</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Copy all</string>\n   </property>\n  </action>\n  <action name=\"actionDisasCopy_Bytes\">\n   <property name=\"text\">\n    <string>Copy bytes</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Copy bytes</string>\n   </property>\n  </action>\n  <action name=\"actionDisasCopy_Disasm\">\n   <property name=\"text\">\n    <string>Copy disasm</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Copy disasm</string>\n   </property>\n  </action>\n  <action name=\"actionDisplayOptions\">\n   <property name=\"text\">\n    <string>Copy disasm</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Copy disasm</string>\n   </property>\n  </action>\n  <action name=\"actionStart_Web_Server\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/img/icons/cloud.svg</normaloff>:/img/icons/cloud.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Start web server</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Start web server</string>\n   </property>\n  </action>\n  <action name=\"action1column\">\n   <property name=\"text\">\n    <string>1</string>\n   </property>\n  </action>\n  <action name=\"action2columns\">\n   <property name=\"text\">\n    <string>2</string>\n   </property>\n  </action>\n  <action name=\"action4columns\">\n   <property name=\"text\">\n    <string>4</string>\n   </property>\n  </action>\n  <action name=\"action8columns\">\n   <property name=\"text\">\n    <string>8</string>\n   </property>\n  </action>\n  <action name=\"action16columns\">\n   <property name=\"text\">\n    <string>16</string>\n   </property>\n  </action>\n  <action name=\"action32columns\">\n   <property name=\"text\">\n    <string>32</string>\n   </property>\n  </action>\n  <action name=\"action64columns\">\n   <property name=\"text\">\n    <string>64</string>\n   </property>\n  </action>\n  <action name=\"actionSyntax_AT_T_Intel\">\n   <property name=\"text\">\n    <string>Syntax AT&amp;T/Intel</string>\n   </property>\n  </action>\n  <action name=\"actionFunctionsRename\">\n   <property name=\"text\">\n    <string>Rename</string>\n   </property>\n  </action>\n  <action name=\"actionFunctionsUndefine\">\n   <property name=\"text\">\n    <string>Undefine</string>\n   </property>\n  </action>\n  <action name=\"actionDisasRename\">\n   <property name=\"text\">\n    <string>Rename</string>\n   </property>\n  </action>\n  <action name=\"actionDisasmUndefine\">\n   <property name=\"text\">\n    <string>Undefine</string>\n   </property>\n  </action>\n  <action name=\"actionDisasAdd_comment\">\n   <property name=\"text\">\n    <string>Add comment</string>\n   </property>\n  </action>\n  <action name=\"actionhide_bottomPannel\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>:/img/icons/down.svg</normaloff>:/img/icons/down.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Show/Hide bottom panel</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Show/Hide bottom panel</string>\n   </property>\n  </action>\n  <action name=\"actionRun_Script\">\n   <property name=\"text\">\n    <string>Run Rizin script</string>\n   </property>\n  </action>\n  <action name=\"actionReset_settings\">\n   <property name=\"text\">\n    <string>Reset Settings</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Reset settings</string>\n   </property>\n  </action>\n  <action name=\"actionQuit\">\n   <property name=\"text\">\n    <string>Quit</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Q</string>\n   </property>\n  </action>\n  <action name=\"actionExports\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Exports</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Show/Hide Exports panel</string>\n   </property>\n  </action>\n  <action name=\"actionRefresh_contents\">\n   <property name=\"text\">\n    <string>Refresh Contents</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Refresh contents</string>\n   </property>\n  </action>\n  <action name=\"actionDisplay_Esil\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Show ESIL rather than assembly</string>\n   </property>\n  </action>\n  <action name=\"actionDisplay_Decompiler\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Show pseudocode rather than assembly</string>\n   </property>\n  </action>\n  <action name=\"actionDisplay_Offsets\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Display offsets</string>\n   </property>\n  </action>\n  <action name=\"actionPreferences\">\n   <property name=\"text\">\n    <string>Preferences</string>\n   </property>\n  </action>\n  <action name=\"actionSaveAs\">\n   <property name=\"text\">\n    <string>Save Project As...</string>\n   </property>\n  </action>\n  <action name=\"actionGraph\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Graph</string>\n   </property>\n  </action>\n  <action name=\"actionImportPDB\">\n   <property name=\"text\">\n    <string>Import PDB</string>\n   </property>\n  </action>\n  <action name=\"actionAnalyze\">\n   <property name=\"text\">\n    <string>Analyze Program</string>\n   </property>\n  </action>\n  <action name=\"actionExport_as_code\">\n   <property name=\"text\">\n    <string>Export as code</string>\n   </property>\n  </action>\n  <action name=\"actionApplySigFromFile\">\n   <property name=\"text\">\n    <string>Apply Signature From File</string>\n   </property>\n  </action>\n  <action name=\"actionCreateNewSig\">\n   <property name=\"text\">\n    <string>Create New Signature File</string>\n   </property>\n  </action>\n  <action name=\"actionExtraHexdump\">\n   <property name=\"text\">\n    <string>Add Hexdump</string>\n   </property>\n  </action>\n  <action name=\"actionExtraDecompiler\">\n   <property name=\"text\">\n    <string>Add Decompiler</string>\n   </property>\n  </action>\n  <action name=\"actionExtraDisassembly\">\n   <property name=\"text\">\n    <string>Add Disassembly</string>\n   </property>\n  </action>\n  <action name=\"actionExtraGraph\">\n   <property name=\"text\">\n    <string>Add Graph</string>\n   </property>\n  </action>\n  <action name=\"actionGrouped_dock_dragging\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Grouped dock dragging</string>\n   </property>\n  </action>\n  <action name=\"actionZoomIn\">\n   <property name=\"text\">\n    <string>Zoom In</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl++</string>\n   </property>\n   <property name=\"shortcutContext\">\n    <enum>Qt::ApplicationShortcut</enum>\n   </property>\n  </action>\n  <action name=\"actionZoomOut\">\n   <property name=\"text\">\n    <string>Zoom Out</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+-</string>\n   </property>\n   <property name=\"shortcutContext\">\n    <enum>Qt::ApplicationShortcut</enum>\n   </property>\n  </action>\n  <action name=\"actionZoomReset\">\n   <property name=\"text\">\n    <string>Reset</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+=</string>\n   </property>\n   <property name=\"shortcutContext\">\n    <enum>Qt::ApplicationShortcut</enum>\n   </property>\n  </action>\n  <action name=\"actionCommitChanges\">\n   <property name=\"text\">\n    <string>Commit changes</string>\n   </property>\n  </action>\n  <action name=\"actionWriteMode\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Write mode</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Open the file in write mode. Every change to the file will change the original file on disk.</string>\n   </property>\n  </action>\n  <action name=\"actionCacheMode\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Cache mode</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Enable cache mode. Changes to the file would not be applied to disk unless you specifically commit them. This is a safer option.</string>\n   </property>\n  </action>\n  <action name=\"actionReadOnly\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Read-Only mode</string>\n   </property>\n  </action>\n  <action name=\"actionSaveLayout\">\n   <property name=\"text\">\n    <string>Save layout</string>\n   </property>\n  </action>\n  <action name=\"actionManageLayouts\">\n   <property name=\"text\">\n    <string>Manage layouts</string>\n   </property>\n  </action>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <resources>\n  <include location=\"resources.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/core/RizinCpp.cpp",
    "content": "#include \"RizinCpp.h\"\n"
  },
  {
    "path": "src/core/RizinCpp.h",
    "content": "/** \\file RizinCpp.h\n * Various utilities for easier and safer interactions with Rizin\n * from C++ code.\n */\n#ifndef RIZINCPP_H\n#define RIZINCPP_H\n\n#include \"rz_core.h\"\n#include <QString>\n#include <memory>\n\nstatic inline QString fromOwnedCharPtr(char *str)\n{\n    QString result(str ? str : \"\");\n    rz_mem_free(str);\n    return result;\n}\n\ntemplate<class T, class F>\nstd::unique_ptr<T, F *> fromOwned(T *data, F *freeFunction)\n{\n    return std::unique_ptr<T, F *> { data, freeFunction };\n}\n\nstatic inline std::unique_ptr<char, decltype(&rz_mem_free)> fromOwned(char *text)\n{\n    return { text, rz_mem_free };\n}\n\ntemplate<class T, void (*func)(T *)>\nclass FreeBinder\n{\npublic:\n    void operator()(T *data) { func(data); }\n};\n\ntemplate<class T, void (*func)(T *)>\nusing UniquePtrC = std::unique_ptr<T, FreeBinder<T, func>>;\n\ntemplate<typename T, void (*func)(T)>\nusing UniquePtrCP = UniquePtrC<typename std::remove_pointer<T>::type, func>;\n\nstatic inline auto fromOwned(RZ_OWN RzPVector *data)\n        -> UniquePtrCP<decltype(data), &rz_pvector_free>\n{\n    return { data, {} };\n}\n\nstatic inline auto fromOwned(RZ_OWN RzList *data) -> UniquePtrCP<decltype(data), &rz_list_free>\n{\n    return { data, {} };\n}\n\n// Rizin list iteration macros\n// deprecated, prefer using CutterPVector and CutterRzList instead\n#define CutterRzListForeach(list, it, type, x)                                                     \\\n    if (list)                                                                                      \\\n        for (it = list->head; it && ((x = static_cast<type *>(it->val))); it = it->next)\n\n#define CutterRzVectorForeach(vec, it, type)                                                       \\\n    if ((vec) && (vec)->a)                                                                         \\\n        for (it = (type *)(vec)->a;                                                                \\\n             (char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size);                     \\\n             it = (type *)((char *)it + (vec)->elem_size))\n\ntemplate<typename T>\nclass CutterPVector\n{\nprivate:\n    const RzPVector *const vec;\n\npublic:\n    class iterator\n    {\n    public:\n        using iterator_category = std::input_iterator_tag;\n        using value_type = T *;\n        using difference_type = ptrdiff_t;\n        using pointer = T **;\n        using reference = T *&;\n\n    private:\n        T **p;\n\n    public:\n        iterator(T **p) : p(p) {}\n        iterator(const iterator &o) : p(o.p) {}\n        iterator &operator++()\n        {\n            p++;\n            return *this;\n        }\n        iterator operator++(int)\n        {\n            iterator tmp(*this);\n            operator++();\n            return tmp;\n        }\n        bool operator==(const iterator &rhs) const { return p == rhs.p; }\n        bool operator!=(const iterator &rhs) const { return p != rhs.p; }\n        T *operator*() { return *p; }\n    };\n\n    CutterPVector(const RzPVector *vec) : vec(vec) {}\n    iterator begin() const { return iterator(reinterpret_cast<T **>(vec->v.a)); }\n    iterator end() const { return iterator(reinterpret_cast<T **>(vec->v.a) + vec->v.len); }\n};\n\ntemplate<typename T>\nclass CutterRzList\n{\nprivate:\n    const RzList *const list;\n\npublic:\n    class iterator\n    {\n    public:\n        using iterator_category = std::input_iterator_tag;\n        using value_type = T *;\n        using difference_type = ptrdiff_t;\n        using pointer = T **;\n        using reference = T *&;\n\n    private:\n        RzListIter *iter;\n\n    public:\n        explicit iterator(RzListIter *iter) : iter(iter) {}\n        iterator(const iterator &o) : iter(o.iter) {}\n        iterator &operator++()\n        {\n            if (!iter) {\n                return *this;\n            }\n            iter = iter->next;\n            return *this;\n        }\n        iterator operator++(int)\n        {\n            iterator tmp(*this);\n            operator++();\n            return tmp;\n        }\n        bool operator==(const iterator &rhs) const { return iter == rhs.iter; }\n        bool operator!=(const iterator &rhs) const { return iter != rhs.iter; }\n        T *operator*()\n        {\n            if (!iter) {\n                return nullptr;\n            }\n            return reinterpret_cast<T *>(iter->val);\n        }\n    };\n\n    explicit CutterRzList(const RzList *l) : list(l) {}\n    iterator begin() const\n    {\n        if (!list) {\n            return iterator(nullptr);\n        }\n        return iterator(list->head);\n    }\n    iterator end() const { return iterator(nullptr); }\n};\n\ntemplate<typename T>\nclass CutterRzIter\n{\n    UniquePtrC<RzIterator, &rz_iterator_free> rzIter;\n\npublic:\n    CutterRzIter(RzIterator *rzIter) : rzIter(rzIter)\n    {\n        // immediately attempt advancing by 1, otherwise it's hard to distinguish whether current\n        // element is null due to not having called next, or due to having run out of elements\n        if (rzIter) {\n            ++*this;\n        }\n    }\n\n    CutterRzIter<T> &operator++()\n    {\n        rz_iterator_next(rzIter.get());\n        return *this;\n    }\n    operator bool() { return rzIter && rzIter->cur; }\n    T &operator*() { return *reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }\n    T *get() { return reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }\n    T *operator->() { return reinterpret_cast<RzAnalysisBytes *>(rzIter->cur); }\n};\n\n#define CutterHtDef(xx, XX, K, VB)                                                                 \\\n    template<typename V>                                                                           \\\n    class CutterHt##XX                                                                             \\\n    {                                                                                              \\\n    private:                                                                                       \\\n        Ht##XX *const ht;                                                                          \\\n        template<typename F>                                                                       \\\n        static bool ForEachCb(void *user, K k, const VB v)                                         \\\n        {                                                                                          \\\n            return (*reinterpret_cast<F *>(user))(k, reinterpret_cast<const V *>(v));              \\\n        }                                                                                          \\\n                                                                                                   \\\n    public:                                                                                        \\\n        CutterHt##XX(Ht##XX *ht) : ht(ht) {};                                                      \\\n        template<typename F>                                                                       \\\n        void ForEach(F f)                                                                          \\\n        {                                                                                          \\\n            ht_##xx##_foreach(ht, ForEachCb<F>, &f);                                               \\\n        }                                                                                          \\\n    }\n\nCutterHtDef(sp, SP, const char *, void *);\n\n#endif // RIZINCPP_H\n"
  },
  {
    "path": "src/dialogs/AboutDialog.cpp",
    "content": "#include \"core/Cutter.h\"\n#include \"AboutDialog.h\"\n\n#include \"ui_AboutDialog.h\"\n#include \"RizinPluginsDialog.h\"\n#include \"common/Configuration.h\"\n#include \"common/BugReporting.h\"\n\n#include <QUrl>\n#include <QTimer>\n#include <QEventLoop>\n#include <QJsonObject>\n#include <QProgressBar>\n#include <QProgressDialog>\n#include <UpdateWorker.h>\n#include <QtNetwork/QNetworkRequest>\n#include <QtNetwork/QNetworkAccessManager>\n\n#include \"CutterConfig.h\"\n\nAboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n    ui->logoSvgWidget->load(Config()->getLogoFile());\n\n    QString aboutString(\n            tr(\"Version\") + \" \" CUTTER_VERSION_FULL \"<br/>\" + tr(\"Using rizin \")\n            + Core()->getRizinVersionReadable() + \"<br/>\" + buildQtVersionString() + \"<p><b>\"\n            + tr(\"Optional Features:\") + \"</b><br/>\"\n            + tr(\"Python: %1\")\n                      .arg(\n#ifdef CUTTER_ENABLE_PYTHON\n                              tr(\"ON\")\n#else\n                              tr(\"OFF\")\n#endif\n                                      )\n            + \"<br/>\"\n            + tr(\"Python Bindings: %1\")\n                      .arg(\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n                              tr(\"ON\")\n#else\n                              tr(\"OFF\")\n#endif\n                                      )\n            + \"</p>\" + \"<h2>\" + tr(\"License\") + \"</h2>\"\n            + tr(\"This Software is released under the GNU General Public License v3.0\") + \"<h2>\"\n            + tr(\"Authors\") + \"</h2>\"\n            + tr(\"Cutter is developed by the community and maintained by its core and development \"\n                 \"teams.<br/>\")\n            + tr(\"Check our <a \"\n                 \"href='https://github.com/rizinorg/cutter/graphs/contributors'>contributors \"\n                 \"page</a> for the full list of contributors.\"));\n    ui->label->setText(aboutString);\n\n    QSignalBlocker s(ui->updatesCheckBox);\n    ui->updatesCheckBox->setChecked(Config()->getAutoUpdateEnabled());\n\n    if (!CUTTER_UPDATE_WORKER_AVAILABLE) {\n        ui->updatesCheckBox->hide();\n        ui->checkForUpdatesButton->hide();\n    }\n}\n\nAboutDialog::~AboutDialog() {}\n\nvoid AboutDialog::on_buttonBox_rejected()\n{\n    close();\n}\n\nvoid AboutDialog::on_showVersionButton_clicked()\n{\n    QMessageBox popup(this);\n    popup.setWindowTitle(tr(\"Rizin version information\"));\n    popup.setTextInteractionFlags(Qt::TextSelectableByMouse);\n    auto versionInformation = Core()->getVersionInformation();\n    popup.setText(versionInformation);\n    popup.exec();\n}\n\nvoid AboutDialog::on_showPluginsButton_clicked()\n{\n    RizinPluginsDialog dialog(this);\n    dialog.exec();\n}\nvoid AboutDialog::on_Issue_clicked()\n{\n    openIssue();\n}\n\nvoid AboutDialog::on_checkForUpdatesButton_clicked()\n{\n#if CUTTER_UPDATE_WORKER_AVAILABLE\n    UpdateWorker updateWorker;\n\n    auto parentWindow = this;\n\n    QProgressDialog waitDialog(parentWindow);\n    QProgressBar *bar = new QProgressBar(&waitDialog);\n    bar->setMaximum(0);\n\n    waitDialog.setBar(bar);\n    waitDialog.setLabel(new QLabel(tr(\"Checking for updates...\"), &waitDialog));\n\n    connect(&updateWorker, &UpdateWorker::checkComplete, &waitDialog, &QProgressDialog::cancel);\n    connect(&updateWorker, &UpdateWorker::checkComplete,\n            [&updateWorker, parentWindow](const QVersionNumber &version, const QString &error) {\n                if (!error.isEmpty()) {\n                    QMessageBox::critical(parentWindow, tr(\"Error!\"), error);\n                } else {\n                    if (version <= UpdateWorker::currentVersionNumber()) {\n                        QMessageBox::information(parentWindow, tr(\"Version control\"),\n                                                 tr(\"Cutter is up to date!\"));\n                    } else {\n                        updateWorker.showUpdateDialog(false);\n                    }\n                }\n            });\n\n    updateWorker.checkCurrentVersion(7000);\n    waitDialog.exec();\n#endif\n}\n\nvoid AboutDialog::on_updatesCheckBox_stateChanged(int)\n{\n    Config()->setAutoUpdateEnabled(!Config()->getAutoUpdateEnabled());\n}\n\nstatic QString compilerString()\n{\n#if defined(Q_CC_CLANG) // must be before GNU, because clang claims to be GNU too\n    QString isAppleString;\n#    if defined(__apple_build_version__) // Apple clang has other version numbers\n    isAppleString = QLatin1String(\" (Apple)\");\n#    endif\n    return QLatin1String(\"Clang \") + QString::number(__clang_major__) + QLatin1Char('.')\n            + QString::number(__clang_minor__) + isAppleString;\n#elif defined(Q_CC_GNU)\n    return QLatin1String(\"GCC \") + QLatin1String(__VERSION__);\n#elif defined(Q_CC_MSVC)\n    if (_MSC_VER > 1999)\n        return QLatin1String(\"MSVC <unknown>\");\n    if (_MSC_VER >= 1910)\n        return QLatin1String(\"MSVC 2017\");\n    if (_MSC_VER >= 1900)\n        return QLatin1String(\"MSVC 2015\");\n#endif\n    return QLatin1String(\"<unknown compiler>\");\n}\n\nQString AboutDialog::buildQtVersionString(void)\n{\n    return tr(\"Based on Qt %1 (%2, %3 bit)\")\n            .arg(QLatin1String(qVersion()), compilerString(), QString::number(QSysInfo::WordSize));\n}\n"
  },
  {
    "path": "src/dialogs/AboutDialog.h",
    "content": "#ifndef ABOUTDIALOG_H\n#define ABOUTDIALOG_H\n\n#include <QDialog>\n#include <memory>\n#include <QtNetwork/QNetworkReply>\n\nnamespace Ui {\nclass AboutDialog;\n}\n\nclass AboutDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit AboutDialog(QWidget *parent = nullptr);\n    ~AboutDialog();\n\nprivate slots:\n    void on_buttonBox_rejected();\n    void on_showVersionButton_clicked();\n    void on_showPluginsButton_clicked();\n    void on_Issue_clicked();\n\n    /**\n     * @fn AboutDialog::on_checkForUpdatesButton_clicked()\n     *\n     * @brief Initiates process of checking for updates.\n     */\n    void on_checkForUpdatesButton_clicked();\n\n    /**\n     * @fn AboutDialog::on_updatesCheckBox_stateChanged(int state)\n     *\n     * @brief Changes value of autoUpdateEnabled option in settings.\n     */\n    void on_updatesCheckBox_stateChanged(int state);\n\nprivate:\n    std::unique_ptr<Ui::AboutDialog> ui;\n\n    QString buildQtVersionString(void);\n};\n\n#endif // ABOUTDIALOG_H\n"
  },
  {
    "path": "src/dialogs/AboutDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AboutDialog</class>\n <widget class=\"QDialog\" name=\"AboutDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>935</width>\n    <height>554</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>About Cutter</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n   <item row=\"1\" column=\"0\">\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item row=\"4\" column=\"0\">\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Close</set>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <property name=\"horizontalSpacing\">\n      <number>5</number>\n     </property>\n     <property name=\"verticalSpacing\">\n      <number>15</number>\n     </property>\n     <item row=\"4\" column=\"1\" colspan=\"2\">\n      <widget class=\"QCheckBox\" name=\"updatesCheckBox\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Check for updates on start</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QPushButton\" name=\"checkForUpdatesButton\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Check for updates</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\" colspan=\"4\" alignment=\"Qt::AlignHCenter\">\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:11pt; font-weight:600;&quot;&gt;Cutter is a free and open-source reverse engineering platform powered by Rizin&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:11pt;&quot;&gt;Read more on &lt;/span&gt;&lt;a href=&quot;https://cutter.re&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#2980b9;&quot;&gt;cutter.re&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"2\" colspan=\"2\" alignment=\"Qt::AlignHCenter\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt; font-weight:600;&quot;&gt;Cutter&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"4\">\n      <widget class=\"QPushButton\" name=\"Issue\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Report an issue</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"2\">\n      <widget class=\"QPushButton\" name=\"showPluginsButton\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Show Rizin plugin information</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"3\">\n      <widget class=\"QPushButton\" name=\"showVersionButton\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Show version information</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"3\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"styleSheet\">\n      <string notr=\"true\">s</string>\n     </property>\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n     </property>\n     <property name=\"margin\">\n      <number>5</number>\n     </property>\n     <property name=\"openExternalLinks\">\n      <bool>true</bool>\n     </property>\n     <property name=\"textInteractionFlags\">\n      <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"0\" alignment=\"Qt::AlignHCenter\">\n    <widget class=\"QSvgWidget\" name=\"logoSvgWidget\" native=\"true\">\n     <property name=\"enabled\">\n      <bool>true</bool>\n     </property>\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>96</width>\n       <height>96</height>\n      </size>\n     </property>\n     <property name=\"maximumSize\">\n      <size>\n       <width>96</width>\n       <height>96</height>\n      </size>\n     </property>\n     <property name=\"focusPolicy\">\n      <enum>Qt::NoFocus</enum>\n     </property>\n     <property name=\"contextMenuPolicy\">\n      <enum>Qt::DefaultContextMenu</enum>\n     </property>\n     <property name=\"toolTipDuration\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>QSvgWidget</class>\n   <extends>QWidget</extends>\n   <header>QSvgWidget</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n <slots>\n  <signal>signal1()</signal>\n  <slot>on_reportIssueButton_clicked()</slot>\n </slots>\n</ui>\n"
  },
  {
    "path": "src/dialogs/ArenaInfoDialog.cpp",
    "content": "#include \"ArenaInfoDialog.h\"\n#include \"ui_ArenaInfoDialog.h\"\n\nArenaInfoDialog::ArenaInfoDialog(Arena &arena, QWidget *parent)\n    : QDialog(parent), ui(new Ui::ArenaInfoDialog), arena(arena)\n{\n    ui->setupUi(this);\n    setWindowTitle(\"Arena @ \" + RzAddressString(arena.offset));\n    updateContents();\n}\n\nvoid ArenaInfoDialog::updateContents()\n{\n    ui->lineEditTop->setText(RzAddressString(arena.top));\n    ui->lineEditLastRem->setText(RzAddressString(arena.last_remainder));\n    ui->lineEditNext->setText(RzAddressString(arena.next));\n    ui->lineEditNextfree->setText(RzAddressString(arena.next_free));\n    ui->lineEditSysMem->setText(RzAddressString(arena.system_mem));\n    ui->lineEditMaxMem->setText(RzAddressString(arena.max_system_mem));\n}\n\nArenaInfoDialog::~ArenaInfoDialog()\n{\n    delete ui;\n}\n"
  },
  {
    "path": "src/dialogs/ArenaInfoDialog.h",
    "content": "#ifndef ARENAINFODIALOG_H\n#define ARENAINFODIALOG_H\n\n#include <QDialog>\n#include <CutterDescriptions.h>\n\nnamespace Ui {\nclass ArenaInfoDialog;\n}\n\nclass ArenaInfoDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit ArenaInfoDialog(Arena &arena, QWidget *parent = nullptr);\n    ~ArenaInfoDialog();\n    void updateContents();\n\nprivate:\n    Ui::ArenaInfoDialog *ui;\n    Arena arena;\n};\n\n#endif // ARENAINFODIALOG_H\n"
  },
  {
    "path": "src/dialogs/ArenaInfoDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n    <class>ArenaInfoDialog</class>\n    <widget class=\"QDialog\" name=\"ArenaInfoDialog\">\n        <property name=\"geometry\">\n            <rect>\n                <x>0</x>\n                <y>0</y>\n                <width>400</width>\n                <height>202</height>\n            </rect>\n        </property>\n        <property name=\"windowTitle\">\n            <string>Dialog</string>\n        </property>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n            <item>\n                <layout class=\"QFormLayout\" name=\"formLayout\">\n                    <item row=\"0\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label\">\n                            <property name=\"text\">\n                                <string>Top</string>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"1\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_2\">\n                            <property name=\"text\">\n                                <string>Next</string>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"2\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_3\">\n                            <property name=\"text\">\n                                <string>Next free</string>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"3\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_4\">\n                            <property name=\"text\">\n                                <string>System Memory</string>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"4\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_5\">\n                            <property name=\"text\">\n                                <string>Max Memory</string>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"0\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"lineEditTop\">\n                            <property name=\"readOnly\">\n                                <bool>true</bool>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"1\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"lineEditNext\">\n                            <property name=\"readOnly\">\n                                <bool>true</bool>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"2\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"lineEditNextfree\">\n                            <property name=\"readOnly\">\n                                <bool>true</bool>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"3\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"lineEditSysMem\">\n                            <property name=\"readOnly\">\n                                <bool>true</bool>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"4\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"lineEditMaxMem\">\n                            <property name=\"readOnly\">\n                                <bool>true</bool>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"5\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_6\">\n                            <property name=\"text\">\n                                <string>Last Remainder</string>\n                            </property>\n                        </widget>\n                    </item>\n                    <item row=\"5\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"lineEditLastRem\">\n                            <property name=\"readOnly\">\n                                <bool>true</bool>\n                            </property>\n                        </widget>\n                    </item>\n                </layout>\n            </item>\n        </layout>\n    </widget>\n    <resources/>\n    <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/AsyncTaskDialog.cpp",
    "content": "\n#include \"AsyncTaskDialog.h\"\n#include \"common/AsyncTask.h\"\n\n#include \"ui_AsyncTaskDialog.h\"\n\nAsyncTaskDialog::AsyncTaskDialog(AsyncTask::Ptr task, QWidget *parent)\n    : QDialog(parent), ui(new Ui::AsyncTaskDialog), task(task)\n{\n    ui->setupUi(this);\n\n    QString title = task->getTitle();\n    if (!title.isNull()) {\n        setWindowTitle(title);\n    }\n\n    connect(task.data(), &AsyncTask::logChanged, this, &AsyncTaskDialog::updateLog);\n    connect(task.data(), &AsyncTask::finished, this, [this]() { close(); });\n\n    updateLog(task->getLog());\n\n    connect(&timer, &QTimer::timeout, this, &AsyncTaskDialog::updateProgressTimer);\n    timer.setInterval(1000);\n    timer.setSingleShot(false);\n    timer.start();\n\n    updateProgressTimer();\n}\n\nAsyncTaskDialog::~AsyncTaskDialog() {}\n\nvoid AsyncTaskDialog::updateLog(const QString &log)\n{\n    ui->logTextEdit->setPlainText(log);\n}\n\nvoid AsyncTaskDialog::updateProgressTimer()\n{\n    int secondsElapsed = (task->getElapsedTime() + 500) / 1000;\n    int minutesElapsed = secondsElapsed / 60;\n    int hoursElapsed = minutesElapsed / 60;\n\n    QString label;\n    if (hoursElapsed) {\n        label += tr(\"%n hours\", nullptr, hoursElapsed);\n        label += \" \";\n    }\n    if (minutesElapsed) {\n        label += tr(\"%n minutes\", nullptr, minutesElapsed % 60);\n        label += \" \";\n    }\n    label += tr(\"%n seconds\", nullptr, secondsElapsed % 60);\n\n    ui->timeLabel->setText(tr(\"Running for %1\", \"time\").arg(label));\n}\n\nvoid AsyncTaskDialog::closeEvent(QCloseEvent *event)\n{\n    if (interruptOnClose) {\n        task->interrupt();\n        task->wait();\n    }\n\n    QWidget::closeEvent(event);\n}\n\nvoid AsyncTaskDialog::reject()\n{\n    task->interrupt();\n}\n"
  },
  {
    "path": "src/dialogs/AsyncTaskDialog.h",
    "content": "\n#ifndef ASYNCTASKDIALOG_H\n#define ASYNCTASKDIALOG_H\n\n#include <memory>\n\n#include <QDialog>\n#include <QTimer>\n\n#include \"common/AsyncTask.h\"\n\nnamespace Ui {\nclass AsyncTaskDialog;\n}\n\nclass AsyncTaskDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    AsyncTaskDialog(AsyncTask::Ptr task, QWidget *parent = nullptr);\n    ~AsyncTaskDialog();\n\n    void setInterruptOnClose(bool v) { interruptOnClose = v; }\n    bool getInterruptOnClose() { return interruptOnClose; }\n\npublic slots:\n    void reject() override;\n\nprivate slots:\n    void updateLog(const QString &log);\n    void updateProgressTimer();\n\nprotected:\n    void closeEvent(QCloseEvent *event) override;\n\nprivate:\n    std::unique_ptr<Ui::AsyncTaskDialog> ui;\n    AsyncTask::Ptr task;\n    QTimer timer;\n\n    bool interruptOnClose = false;\n};\n\n#endif // ASYNCTASKDIALOG_H\n"
  },
  {
    "path": "src/dialogs/AsyncTaskDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AsyncTaskDialog</class>\n <widget class=\"QDialog\" name=\"AsyncTaskDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Cutter</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"timeLabel\">\n     <property name=\"text\">\n      <string>Time</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QProgressBar\" name=\"progressBar\">\n     <property name=\"maximum\">\n      <number>0</number>\n     </property>\n     <property name=\"textVisible\">\n      <bool>false</bool>\n     </property>\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPlainTextEdit\" name=\"logTextEdit\">\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>AsyncTaskDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>AsyncTaskDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/AttachProcDialog.cpp",
    "content": "#include \"core/MainWindow.h\"\n#include \"core/Cutter.h\"\n#include \"AttachProcDialog.h\"\n#include \"ui_AttachProcDialog.h\"\n\n#include \"common/Helpers.h\"\n\n#include <QScrollBar>\n\n// ------------\n// ProcessModel\n// ------------\nProcessModel::ProcessModel(QObject *parent) : QAbstractListModel(parent)\n{\n    updateData();\n}\n\nvoid ProcessModel::updateData()\n{\n    beginResetModel();\n\n    processes = Core()->getProcesses();\n\n    endResetModel();\n}\n\nint ProcessModel::rowCount(const QModelIndex &) const\n{\n    return processes.count();\n}\n\nint ProcessModel::columnCount(const QModelIndex &) const\n{\n    return ProcessModel::ColumnCount;\n}\n\nQVariant ProcessModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= processes.count())\n        return QVariant();\n\n    const ProcessDescription &proc = processes.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case PidColumn:\n            return proc.pid;\n        case UidColumn:\n            return proc.uid;\n        case StatusColumn:\n            return proc.status;\n        case PathColumn:\n            return proc.path;\n        default:\n            return QVariant();\n        }\n    case ProcDescriptionRole:\n        return QVariant::fromValue(proc);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant ProcessModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case PidColumn:\n            return tr(\"PID\");\n        case UidColumn:\n            return tr(\"UID\");\n        case StatusColumn:\n            return tr(\"Status\");\n        case PathColumn:\n            return tr(\"Path\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nbool ProcessModel::lessThan(const ProcessDescription &leftProc, const ProcessDescription &rightProc,\n                            int column)\n{\n    switch (column) {\n    case ProcessModel::PidColumn:\n        return leftProc.pid < rightProc.pid;\n    case ProcessModel::UidColumn:\n        return leftProc.uid < rightProc.uid;\n    case ProcessModel::StatusColumn:\n        return leftProc.status < rightProc.status;\n    case ProcessModel::PathColumn:\n        return leftProc.path < rightProc.path;\n    default:\n        break;\n    }\n\n    return leftProc.pid < rightProc.pid;\n}\n\n// ------------------------------\n// ProcessBeingAnalysedProxyModel\n// ------------------------------\nProcessBeingAnalysedProxyModel::ProcessBeingAnalysedProxyModel(ProcessModel *sourceModel,\n                                                               QObject *parent)\n    : QSortFilterProxyModel(parent)\n{\n    setSourceModel(sourceModel);\n\n    // @SEE: Should there be a getFilename() in Core()? Not the first time I use this\n    processBeingAnalysedFilename = processPathToFilename(Core()->getConfig(\"file.path\"));\n}\n\nQString ProcessBeingAnalysedProxyModel::processPathToFilename(const QString &path) const\n{\n    // removes the arguments and gets filename from the process path\n    return path.section(QLatin1Char(' '), 0, 0).section(QLatin1Char('/'), -1);\n}\n\nbool ProcessBeingAnalysedProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    ProcessDescription item =\n            index.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();\n\n    QString procFilename = processPathToFilename(item.path);\n    return procFilename == processBeingAnalysedFilename;\n}\n\nbool ProcessBeingAnalysedProxyModel::lessThan(const QModelIndex &left,\n                                              const QModelIndex &right) const\n{\n    ProcessDescription leftProc =\n            left.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();\n    ProcessDescription rightProc =\n            right.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();\n\n    return ProcessModel::lessThan(leftProc, rightProc, left.column());\n}\n\n// -----------------\n// ProcessProxyModel\n// -----------------\nProcessProxyModel::ProcessProxyModel(ProcessModel *sourceModel, QObject *parent)\n    : QSortFilterProxyModel(parent)\n{\n    setSourceModel(sourceModel);\n}\n\nbool ProcessProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    ProcessDescription item =\n            index.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();\n    return qhelpers::filterStringContains(item.path, this);\n}\n\nbool ProcessProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    ProcessDescription leftProc =\n            left.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();\n    ProcessDescription rightProc =\n            right.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();\n\n    return ProcessModel::lessThan(leftProc, rightProc, left.column());\n}\n\n// ----------------\n// AttachProcDialog\n// ----------------\nAttachProcDialog::AttachProcDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AttachProcDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    processModel = new ProcessModel(this);\n    processProxyModel = new ProcessProxyModel(processModel, this);\n    processBeingAnalyzedProxyModel = new ProcessBeingAnalysedProxyModel(processModel, this);\n\n    // View of all processes\n    auto allView = ui->allProcView;\n    allView->setModel(processProxyModel);\n    allView->sortByColumn(ProcessModel::PidColumn, Qt::DescendingOrder);\n\n    // View of the processes with the same name as the one being analyzed\n    auto smallView = ui->procBeingAnalyzedView;\n    smallView->setModel(processBeingAnalyzedProxyModel);\n    smallView->setCurrentIndex(smallView->model()->index(0, 0));\n\n    // To get the 'FocusIn' events\n    allView->installEventFilter(this);\n    smallView->installEventFilter(this);\n\n    // focus on filter line\n    ui->filterLineEdit->setFocus();\n    connect(ui->filterLineEdit, &QLineEdit::textChanged, processProxyModel,\n            &QSortFilterProxyModel::setFilterWildcard);\n\n    // Update the processes every 'updateIntervalMs' seconds\n    timer = new QTimer(this);\n    connect(timer, &QTimer::timeout, this, &AttachProcDialog::updateModelData);\n    timer->start(updateIntervalMs);\n}\n\nAttachProcDialog::~AttachProcDialog()\n{\n    timer->stop();\n    delete timer;\n    delete processBeingAnalyzedProxyModel;\n    delete processProxyModel;\n    delete processModel;\n}\n\nvoid AttachProcDialog::updateModelData()\n{\n    auto allView = ui->allProcView;\n    auto smallView = ui->procBeingAnalyzedView;\n\n    // Save the old selection and scroll position so that we can update and\n    // model and then restore it.\n    bool allViewHadSelection = allView->selectionModel()->hasSelection();\n    bool smallViewHadSelection = smallView->selectionModel()->hasSelection();\n    int allViewPrevScrollPos = 0;\n    int smallViewPrevScrollPos = 0;\n    int allViewPrevPID = 0;\n    int smallViewPrevPID = 0;\n\n    if (allViewHadSelection) {\n        allViewPrevScrollPos = allView->verticalScrollBar()->value();\n        allViewPrevPID = allView->selectionModel()\n                                 ->currentIndex()\n                                 .data(ProcessModel::ProcDescriptionRole)\n                                 .value<ProcessDescription>()\n                                 .pid;\n    }\n    if (smallViewHadSelection) {\n        smallViewPrevScrollPos = smallView->verticalScrollBar()->value();\n        smallViewPrevPID = smallView->selectionModel()\n                                   ->currentIndex()\n                                   .data(ProcessModel::ProcDescriptionRole)\n                                   .value<ProcessDescription>()\n                                   .pid;\n    }\n\n    // Let the model update\n    processModel->updateData();\n\n    // Restore the selection and scroll position\n    if (allViewHadSelection) {\n        QModelIndexList idx =\n                allView->model()->match(allView->model()->index(0, 0), Qt::DisplayRole,\n                                        QVariant::fromValue(allViewPrevPID));\n        if (!idx.isEmpty()) {\n            allView->setCurrentIndex(idx.first());\n            allView->verticalScrollBar()->setValue(allViewPrevScrollPos);\n        }\n    }\n    if (smallViewHadSelection) {\n        QModelIndexList idx =\n                smallView->model()->match(smallView->model()->index(0, 0), Qt::DisplayRole,\n                                          QVariant::fromValue(smallViewPrevPID));\n\n        if (!idx.isEmpty()) {\n            smallView->setCurrentIndex(idx.first());\n            smallView->verticalScrollBar()->setValue(smallViewPrevScrollPos);\n        }\n    }\n\n    // Init selection if nothing was ever selected yet, and a new process with the same name\n    // as the one being analysed was launched.\n    if (!allView->selectionModel()->hasSelection()\n        && !smallView->selectionModel()->hasSelection()) {\n        smallView->setCurrentIndex(smallView->model()->index(0, 0));\n    }\n}\n\nvoid AttachProcDialog::on_buttonBox_accepted() {}\n\nvoid AttachProcDialog::on_buttonBox_rejected()\n{\n    close();\n}\n\nbool AttachProcDialog::eventFilter(QObject *obj, QEvent *event)\n{\n    if (event->type() == QEvent::FocusIn) {\n        if (obj == ui->allProcView) {\n            ui->procBeingAnalyzedView->selectionModel()->clearSelection();\n            wasAllProcViewLastPressed = true;\n            return true;\n        } else if (obj == ui->procBeingAnalyzedView) {\n            ui->allProcView->selectionModel()->clearSelection();\n            wasAllProcViewLastPressed = false;\n            return true;\n        }\n    }\n\n    return false;\n}\n\nint AttachProcDialog::getPID()\n{\n    int pid;\n\n    // Here we need to know which table was selected last to get the proper PID\n    if (wasAllProcViewLastPressed && ui->allProcView->selectionModel()->hasSelection()) {\n        pid = ui->allProcView->selectionModel()\n                      ->currentIndex()\n                      .data(ProcessModel::ProcDescriptionRole)\n                      .value<ProcessDescription>()\n                      .pid;\n    } else if (!wasAllProcViewLastPressed\n               && ui->procBeingAnalyzedView->selectionModel()->hasSelection()) {\n        pid = ui->procBeingAnalyzedView->selectionModel()\n                      ->currentIndex()\n                      .data(ProcessModel::ProcDescriptionRole)\n                      .value<ProcessDescription>()\n                      .pid;\n    } else {\n        // Error attaching. No process selected! Happens when you press ENTER but\n        // there was no process with the same name as the one being analyzed.\n        pid = -1;\n    }\n\n    return pid;\n}\n\nvoid AttachProcDialog::on_allProcView_doubleClicked(const QModelIndex &index)\n{\n    Q_UNUSED(index);\n\n    accept();\n}\n\nvoid AttachProcDialog::on_procBeingAnalyzedView_doubleClicked(const QModelIndex &index)\n{\n    Q_UNUSED(index);\n\n    accept();\n}\n"
  },
  {
    "path": "src/dialogs/AttachProcDialog.h",
    "content": "#pragma once\n\n#include \"core/Cutter.h\"\n#include <QDialog>\n#include <memory>\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n#include <QTimer>\n\nnamespace Ui {\nclass AttachProcDialog;\n}\n\nclass MainWindow;\nclass QTreeWidget;\nclass QTreeWidgetItem;\n\nclass ProcessModel : public QAbstractListModel\n{\n    Q_OBJECT\n\nprivate:\n    QList<ProcessDescription> processes;\n\npublic:\n    enum Column { PidColumn = 0, UidColumn, StatusColumn, PathColumn, ColumnCount };\n    enum Role { ProcDescriptionRole = Qt::UserRole };\n\n    ProcessModel(QObject *parent = 0);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const;\n\n    QVariant data(const QModelIndex &index, int role) const;\n    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n    static bool lessThan(const ProcessDescription &left, const ProcessDescription &right,\n                         int column);\n\npublic slots:\n    void updateData();\n};\n\nclass ProcessProxyModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    ProcessProxyModel(ProcessModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass ProcessBeingAnalysedProxyModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    ProcessBeingAnalysedProxyModel(ProcessModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n\nprivate:\n    QString processBeingAnalysedFilename;\n    QString processPathToFilename(const QString &path) const;\n};\n\nclass AttachProcDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit AttachProcDialog(QWidget *parent = nullptr);\n    ~AttachProcDialog();\n\n    int getPID();\n\nprivate slots:\n    void on_buttonBox_accepted();\n    void on_buttonBox_rejected();\n    void on_allProcView_doubleClicked(const QModelIndex &index);\n    void on_procBeingAnalyzedView_doubleClicked(const QModelIndex &index);\n    void updateModelData();\n\nprivate:\n    std::unique_ptr<Ui::AttachProcDialog> ui;\n    bool eventFilter(QObject *obj, QEvent *event);\n\n    ProcessModel *processModel;\n    ProcessProxyModel *processProxyModel;\n    ProcessBeingAnalysedProxyModel *processBeingAnalyzedProxyModel;\n\n    // whether the 'small table' or 'table with all procs' was last focused\n    bool wasAllProcViewLastPressed = false;\n\n    QTimer *timer;\n    const int updateIntervalMs = 1000;\n};\n"
  },
  {
    "path": "src/dialogs/AttachProcDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AttachProcDialog</class>\n <widget class=\"QDialog\" name=\"AttachProcDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>800</width>\n    <height>600</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Select process to attach...</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"spacing\">\n    <number>2</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>2</number>\n   </property>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <widget class=\"QGroupBox\" name=\"groupBox_2\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>16777215</width>\n         <height>150</height>\n        </size>\n       </property>\n       <property name=\"title\">\n        <string>Processes with same name as currently open file:</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n        <item>\n         <widget class=\"QTreeView\" name=\"procBeingAnalyzedView\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Expanding\" vsizetype=\"MinimumExpanding\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"maximumSize\">\n           <size>\n            <width>16777215</width>\n            <height>16777215</height>\n           </size>\n          </property>\n          <property name=\"styleSheet\">\n           <string notr=\"true\">QTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n          </property>\n          <property name=\"frameShape\">\n           <enum>QFrame::NoFrame</enum>\n          </property>\n          <property name=\"lineWidth\">\n           <number>0</number>\n          </property>\n          <property name=\"indentation\">\n           <number>8</number>\n          </property>\n          <property name=\"sortingEnabled\">\n           <bool>true</bool>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"groupBox\">\n       <property name=\"title\">\n        <string>All processes:</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n        <item>\n         <widget class=\"QTreeView\" name=\"allProcView\">\n          <property name=\"maximumSize\">\n           <size>\n            <width>16777215</width>\n            <height>16777215</height>\n           </size>\n          </property>\n          <property name=\"styleSheet\">\n           <string notr=\"true\">QTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n          </property>\n          <property name=\"frameShape\">\n           <enum>QFrame::NoFrame</enum>\n          </property>\n          <property name=\"lineWidth\">\n           <number>0</number>\n          </property>\n          <property name=\"indentation\">\n           <number>8</number>\n          </property>\n          <property name=\"sortingEnabled\">\n           <bool>true</bool>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLineEdit\" name=\"filterLineEdit\">\n     <property name=\"placeholderText\">\n      <string>Quick Filter</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>AttachProcDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>AttachProcDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/Base64EnDecodedWriteDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>Base64EnDecodedWriteDialog</class>\n <widget class=\"QDialog\" name=\"Base64EnDecodedWriteDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>535</width>\n    <height>217</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Base64 Encode/Decode</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"label\">\n         <property name=\"text\">\n          <string>String:</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLineEdit\" name=\"base64LineEdit\"/>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n       <item>\n        <widget class=\"QRadioButton\" name=\"decodeRB\">\n         <property name=\"text\">\n          <string>Decode</string>\n         </property>\n         <attribute name=\"buttonGroup\">\n          <string notr=\"true\">modeButtonGroup</string>\n         </attribute>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QRadioButton\" name=\"encodeRB\">\n         <property name=\"text\">\n          <string>Encode</string>\n         </property>\n         <attribute name=\"buttonGroup\">\n          <string notr=\"true\">modeButtonGroup</string>\n         </attribute>\n        </widget>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>Base64EnDecodedWriteDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>229</x>\n     <y>356</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>Base64EnDecodedWriteDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>297</x>\n     <y>362</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n <buttongroups>\n  <buttongroup name=\"modeButtonGroup\"/>\n </buttongroups>\n</ui>\n"
  },
  {
    "path": "src/dialogs/BreakpointsDialog.cpp",
    "content": "#include \"BreakpointsDialog.h\"\n#include \"ui_BreakpointsDialog.h\"\n#include \"Cutter.h\"\n#include \"Helpers.h\"\n\n#include <QPushButton>\n#include <QCompleter>\n#include <QCheckBox>\n\nBreakpointsDialog::BreakpointsDialog(bool editMode, QWidget *parent)\n    : QDialog(parent), ui(new Ui::BreakpointsDialog), editMode(editMode)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    connect(ui->breakpointPosition, &QLineEdit::textChanged, this,\n            &BreakpointsDialog::refreshOkButton);\n    refreshOkButton();\n\n    if (editMode) {\n        setWindowTitle(tr(\"Edit breakpoint\"));\n    } else {\n        setWindowTitle(tr(\"New breakpoint\"));\n    }\n\n    struct\n    {\n        QString label;\n        QString tooltip;\n        BreakpointDescription::PositionType type;\n    } positionTypes[] = {\n        { tr(\"Address\"), tr(\"Address or expression calculated when creating breakpoint\"),\n          BreakpointDescription::Address },\n        { tr(\"Named\"), tr(\"Expression - stored as expression\"), BreakpointDescription::Named },\n        { tr(\"Module offset\"), tr(\"Offset relative to module\"), BreakpointDescription::Module },\n    };\n    int index = 0;\n    for (auto &item : positionTypes) {\n        ui->positionType->addItem(item.label, item.type);\n        ui->positionType->setItemData(index, item.tooltip, Qt::ToolTipRole);\n        index++;\n    }\n\n    connect(ui->positionType,\n            static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,\n            &BreakpointsDialog::onTypeChanged);\n    onTypeChanged();\n\n    auto modules = Core()->getMemoryMap();\n    QSet<QString> moduleNames;\n    for (const auto &module : modules) {\n        moduleNames.insert(module.fileName);\n    }\n    for (const auto &module : moduleNames) {\n        ui->moduleName->addItem(module);\n    }\n    ui->moduleName->setCurrentText(\"\");\n    // Suggest completion when user tries to enter file name not only full path\n    ui->moduleName->completer()->setFilterMode(Qt::MatchContains);\n\n    ui->breakpointCondition->setCompleter(nullptr); // Don't use examples for completing\n    configureCheckboxRestrictions();\n}\n\nBreakpointsDialog::BreakpointsDialog(const BreakpointDescription &breakpoint, QWidget *parent)\n    : BreakpointsDialog(true, parent)\n{\n    switch (breakpoint.type) {\n    case BreakpointDescription::Address:\n        ui->breakpointPosition->setText(RzAddressString(breakpoint.addr));\n        break;\n    case BreakpointDescription::Named:\n        ui->breakpointPosition->setText(breakpoint.positionExpression);\n        break;\n    case BreakpointDescription::Module:\n        ui->breakpointPosition->setText(QString::number(breakpoint.moduleDelta));\n        ui->moduleName->setCurrentText(breakpoint.positionExpression);\n        break;\n    }\n    for (int i = 0; i < ui->positionType->count(); i++) {\n        if (ui->positionType->itemData(i) == breakpoint.type) {\n            ui->positionType->setCurrentIndex(i);\n        }\n    }\n    ui->breakpointCommand->setPlainText(breakpoint.command);\n    ui->breakpointCondition->setEditText(breakpoint.condition);\n    if (breakpoint.hw) {\n        ui->radioHardware->setChecked(true);\n        ui->hwRead->setChecked(breakpoint.permission & RZ_PERM_R);\n        ui->hwWrite->setChecked(breakpoint.permission & RZ_PERM_W);\n        ui->hwExecute->setChecked(breakpoint.permission & RZ_PERM_X);\n        ui->breakpointSize->setCurrentText(QString::number(breakpoint.size));\n    } else {\n        ui->radioSoftware->setChecked(true);\n    }\n    ui->checkTrace->setChecked(breakpoint.trace);\n    ui->checkEnabled->setChecked(breakpoint.enabled);\n    refreshOkButton();\n}\n\nBreakpointsDialog::BreakpointsDialog(RVA address, QWidget *parent)\n    : BreakpointsDialog(false, parent)\n{\n    if (address != RVA_INVALID) {\n        ui->breakpointPosition->setText(RzAddressString(address));\n    }\n    refreshOkButton();\n}\n\nBreakpointsDialog::~BreakpointsDialog() {}\n\nBreakpointDescription BreakpointsDialog::getDescription()\n{\n    BreakpointDescription breakpoint;\n    auto positionType =\n            ui->positionType->currentData().value<BreakpointDescription::PositionType>();\n    switch (positionType) {\n    case BreakpointDescription::Address:\n        breakpoint.addr = Core()->math(ui->breakpointPosition->text());\n        break;\n    case BreakpointDescription::Named:\n        breakpoint.positionExpression = ui->breakpointPosition->text().trimmed();\n        break;\n    case BreakpointDescription::Module:\n        breakpoint.moduleDelta = static_cast<int64_t>(Core()->math(ui->breakpointPosition->text()));\n        breakpoint.positionExpression = ui->moduleName->currentText().trimmed();\n        break;\n    }\n    breakpoint.type = positionType;\n\n    breakpoint.size = Core()->num(ui->breakpointSize->currentText());\n    breakpoint.condition = ui->breakpointCondition->currentText().trimmed();\n    breakpoint.command = ui->breakpointCommand->toPlainText().trimmed();\n    if (ui->radioHardware->isChecked()) {\n        breakpoint.hw = true;\n        breakpoint.permission = getHwPermissions();\n    } else {\n        breakpoint.hw = false;\n    }\n    breakpoint.trace = ui->checkTrace->isChecked();\n    breakpoint.enabled = ui->checkEnabled->isChecked();\n    return breakpoint;\n}\n\nvoid BreakpointsDialog::createNewBreakpoint(RVA address, QWidget *parent)\n{\n    BreakpointsDialog editDialog(address, parent);\n    if (editDialog.exec() == QDialog::Accepted) {\n        Core()->addBreakpoint(editDialog.getDescription());\n    }\n}\n\nvoid BreakpointsDialog::editBreakpoint(const BreakpointDescription &breakpoint, QWidget *parent)\n{\n    BreakpointsDialog editDialog(breakpoint, parent);\n    if (editDialog.exec() == QDialog::Accepted) {\n        Core()->updateBreakpoint(breakpoint.index, editDialog.getDescription());\n    }\n}\n\nvoid BreakpointsDialog::refreshOkButton()\n{\n    auto button = ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok);\n    button->setDisabled(ui->breakpointPosition->text().isEmpty());\n}\n\nvoid BreakpointsDialog::onTypeChanged()\n{\n    bool moduleEnabled = ui->positionType->currentData() == QVariant(BreakpointDescription::Module);\n    ui->moduleLabel->setEnabled(moduleEnabled);\n    ui->moduleName->setEnabled(moduleEnabled);\n    ui->breakpointPosition->setPlaceholderText(\n            ui->positionType->currentData(Qt::ToolTipRole).toString());\n}\n\nvoid BreakpointsDialog::configureCheckboxRestrictions()\n{\n    auto atLeastOneChecked = [this]() {\n        if (this->getHwPermissions() == 0) {\n            this->ui->hwExecute->setChecked(true);\n        }\n    };\n    auto rwRule = [this, atLeastOneChecked](bool checked) {\n        if (checked) {\n            this->ui->hwExecute->setChecked(false);\n        } else {\n            atLeastOneChecked();\n        }\n    };\n    connect(ui->hwRead, &QCheckBox::toggled, this, rwRule);\n    connect(ui->hwWrite, &QCheckBox::toggled, this, rwRule);\n    auto execRule = [this, atLeastOneChecked](bool checked) {\n        if (checked) {\n            this->ui->hwRead->setChecked(false);\n            this->ui->hwWrite->setChecked(false);\n        } else {\n            atLeastOneChecked();\n        }\n    };\n    connect(ui->hwExecute, &QCheckBox::toggled, this, execRule);\n}\n\nint BreakpointsDialog::getHwPermissions()\n{\n    int result = 0;\n    if (ui->hwRead->isChecked()) {\n        result |= RZ_PERM_R;\n    }\n    if (ui->hwWrite->isChecked()) {\n        result |= RZ_PERM_W;\n    }\n    if (ui->hwExecute->isChecked()) {\n        result |= RZ_PERM_X;\n    }\n    return result;\n}\n"
  },
  {
    "path": "src/dialogs/BreakpointsDialog.h",
    "content": "#pragma once\n\n#include <QDialog>\n#include <memory>\n#include \"CutterDescriptions.h\"\n\nnamespace Ui {\nclass BreakpointsDialog;\n}\n\nclass BreakpointsDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit BreakpointsDialog(bool editMode = false, QWidget *parent = nullptr);\n    BreakpointsDialog(const BreakpointDescription &editableBreakpoint, QWidget *parent = nullptr);\n    BreakpointsDialog(RVA address, QWidget *parent = nullptr);\n    ~BreakpointsDialog();\n\n    BreakpointDescription getDescription();\n\n    static void createNewBreakpoint(RVA address = RVA_INVALID, QWidget *parent = nullptr);\n    static void editBreakpoint(const BreakpointDescription &breakpoint, QWidget *parent = nullptr);\n\nprivate:\n    std::unique_ptr<Ui::BreakpointsDialog> ui;\n    bool editMode = false;\n\n    void refreshOkButton();\n    void onTypeChanged();\n    void configureCheckboxRestrictions();\n    int getHwPermissions();\n};\n"
  },
  {
    "path": "src/dialogs/BreakpointsDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BreakpointsDialog</class>\n <widget class=\"QDialog\" name=\"BreakpointsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>610</width>\n    <height>437</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Add/Edit breakpoint</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"topGroup\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Position</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>breakpointPosition</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QWidget\" name=\"widget\" native=\"true\">\n       <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n        <property name=\"spacing\">\n         <number>0</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QComboBox\" name=\"positionType\"/>\n        </item>\n        <item>\n         <widget class=\"QLineEdit\" name=\"breakpointPosition\"/>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>Condition</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>breakpointCondition</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"breakpointCondition\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"editable\">\n        <bool>true</bool>\n       </property>\n       <property name=\"currentText\">\n        <string/>\n       </property>\n       <property name=\"currentIndex\">\n        <number>-1</number>\n       </property>\n       <property name=\"insertPolicy\">\n        <enum>QComboBox::NoInsert</enum>\n       </property>\n       <property name=\"frame\">\n        <bool>true</bool>\n       </property>\n       <item>\n        <property name=\"text\">\n         <string>?v $.rax-0x6  # break when rax is 6</string>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"moduleLabel\">\n       <property name=\"text\">\n        <string>Module</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>moduleName</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"moduleName\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"editable\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QGroupBox\" name=\"groupBox\">\n       <property name=\"title\">\n        <string>Type/Options</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n        <item>\n         <widget class=\"QCheckBox\" name=\"checkEnabled\">\n          <property name=\"text\">\n           <string>Enabled</string>\n          </property>\n          <property name=\"checked\">\n           <bool>true</bool>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QRadioButton\" name=\"radioSoftware\">\n          <property name=\"text\">\n           <string>Software</string>\n          </property>\n          <property name=\"checked\">\n           <bool>true</bool>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QRadioButton\" name=\"radioHardware\">\n          <property name=\"text\">\n           <string>Hardware</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QWidget\" name=\"hwConfigBox\">\n          <property name=\"enabled\">\n           <bool>false</bool>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n           <property name=\"spacing\">\n            <number>0</number>\n           </property>\n           <property name=\"leftMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"topMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"rightMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"bottomMargin\">\n            <number>0</number>\n           </property>\n           <item>\n            <widget class=\"QCheckBox\" name=\"hwRead\">\n             <property name=\"text\">\n              <string>Read</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"hwWrite\">\n             <property name=\"text\">\n              <string>Write</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"hwExecute\">\n             <property name=\"text\">\n              <string>Execute</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n             <item>\n              <widget class=\"QLabel\" name=\"label_4\">\n               <property name=\"text\">\n                <string>Size</string>\n               </property>\n               <property name=\"buddy\">\n                <cstring>breakpointSize</cstring>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QComboBox\" name=\"breakpointSize\">\n               <item>\n                <property name=\"text\">\n                 <string>1</string>\n                </property>\n               </item>\n               <item>\n                <property name=\"text\">\n                 <string>2</string>\n                </property>\n               </item>\n               <item>\n                <property name=\"text\">\n                 <string>4</string>\n                </property>\n               </item>\n               <item>\n                <property name=\"text\">\n                 <string>8</string>\n                </property>\n               </item>\n              </widget>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </widget>\n        </item>\n        <item>\n         <spacer name=\"verticalSpacer\">\n          <property name=\"orientation\">\n           <enum>Qt::Vertical</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>20</width>\n            <height>40</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"groupBox_2\">\n       <property name=\"title\">\n        <string>Action</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n        <item>\n         <widget class=\"QCheckBox\" name=\"checkTrace\">\n          <property name=\"text\">\n           <string>Trace</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QFormLayout\" name=\"formLayout_3\">\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_5\">\n            <property name=\"text\">\n             <string>Command</string>\n            </property>\n            <property name=\"buddy\">\n             <cstring>breakpointCommand</cstring>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QPlainTextEdit\" name=\"breakpointCommand\"/>\n          </item>\n         </layout>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>positionType</tabstop>\n  <tabstop>breakpointPosition</tabstop>\n  <tabstop>moduleName</tabstop>\n  <tabstop>breakpointCondition</tabstop>\n  <tabstop>checkEnabled</tabstop>\n  <tabstop>radioSoftware</tabstop>\n  <tabstop>radioHardware</tabstop>\n  <tabstop>checkTrace</tabstop>\n  <tabstop>breakpointCommand</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>BreakpointsDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>260</x>\n     <y>450</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>BreakpointsDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>328</x>\n     <y>450</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>radioHardware</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>hwConfigBox</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>77</x>\n     <y>246</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>77</x>\n     <y>320</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/CommentsDialog.cpp",
    "content": "#include \"CommentsDialog.h\"\n#include \"ui_CommentsDialog.h\"\n\n#include <QErrorMessage>\n\n#include \"core/Cutter.h\"\n\nCommentsDialog::CommentsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CommentsDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    // Event filter for capturing Ctrl/Cmd+Return\n    ui->textEdit->installEventFilter(this);\n}\n\nCommentsDialog::~CommentsDialog() {}\n\nvoid CommentsDialog::on_buttonBox_accepted() {}\n\nvoid CommentsDialog::on_buttonBox_rejected()\n{\n    close();\n}\n\nQString CommentsDialog::getComment()\n{\n    QString ret = ui->textEdit->document()->toPlainText();\n    return ret;\n}\n\nvoid CommentsDialog::setComment(const QString &comment)\n{\n    ui->textEdit->document()->setPlainText(comment);\n}\n\nvoid CommentsDialog::addOrEditComment(RVA offset, QWidget *parent)\n{\n    QString comment = Core()->getCommentAt(offset);\n    CommentsDialog dialog(parent);\n\n    if (comment.isNull() || comment.isEmpty()) {\n        dialog.setWindowTitle(tr(\"Add Comment at %1\").arg(RzAddressString(offset)));\n    } else {\n        dialog.setWindowTitle(tr(\"Edit Comment at %1\").arg(RzAddressString(offset)));\n    }\n\n    dialog.setComment(comment);\n    if (dialog.exec()) {\n        comment = dialog.getComment();\n        if (comment.isEmpty()) {\n            Core()->delComment(offset);\n        } else {\n            Core()->setComment(offset, comment);\n        }\n    }\n}\n\nbool CommentsDialog::eventFilter(QObject * /*obj*/, QEvent *event)\n{\n    if (event->type() == QEvent::KeyPress) {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n\n        // Confirm comment by pressing Ctrl/Cmd+Return\n        if ((keyEvent->modifiers() & Qt::ControlModifier)\n            && ((keyEvent->key() == Qt::Key_Enter) || (keyEvent->key() == Qt::Key_Return))) {\n            this->accept();\n            return true;\n        }\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "src/dialogs/CommentsDialog.h",
    "content": "#ifndef COMMENTSDIALOG_H\n#define COMMENTSDIALOG_H\n\n#include <QDialog>\n#include <memory>\n\n#include \"core/CutterCommon.h\"\n\nnamespace Ui {\nclass CommentsDialog;\n}\n\nclass CommentsDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit CommentsDialog(QWidget *parent = nullptr);\n    ~CommentsDialog();\n\n    QString getComment();\n    void setComment(const QString &comment);\n\n    static void addOrEditComment(RVA offset, QWidget *parent);\nprivate slots:\n    void on_buttonBox_accepted();\n\n    void on_buttonBox_rejected();\n\nprivate:\n    std::unique_ptr<Ui::CommentsDialog> ui;\n\n    bool eventFilter(QObject *obj, QEvent *event);\n};\n\n#endif // COMMENTSDIALOG_H\n"
  },
  {
    "path": "src/dialogs/CommentsDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>CommentsDialog</class>\n <widget class=\"QDialog\" name=\"CommentsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>118</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Comment</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"spacing\">\n    <number>2</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>2</number>\n   </property>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <widget class=\"QPlainTextEdit\" name=\"textEdit\"/>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>CommentsDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>CommentsDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/DuplicateFromOffsetDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DuplicateFromOffsetDialog</class>\n <widget class=\"QDialog\" name=\"DuplicateFromOffsetDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>289</width>\n    <height>323</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Duplicate from offset</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Offset:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"offsetLE\">\n       <property name=\"inputMask\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"maxLength\">\n        <number>16</number>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>N bytes:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QSpinBox\" name=\"nBytesSB\">\n       <property name=\"minimum\">\n        <number>1</number>\n       </property>\n       <property name=\"maximum\">\n        <number>999999999</number>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QScrollArea\" name=\"scrollArea\">\n     <property name=\"verticalScrollBarPolicy\">\n      <enum>Qt::ScrollBarAsNeeded</enum>\n     </property>\n     <property name=\"horizontalScrollBarPolicy\">\n      <enum>Qt::ScrollBarAlwaysOff</enum>\n     </property>\n     <property name=\"widgetResizable\">\n      <bool>true</bool>\n     </property>\n     <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n      <property name=\"geometry\">\n       <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>269</width>\n        <height>208</height>\n       </rect>\n      </property>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n       <item>\n        <widget class=\"QLabel\" name=\"bytesLabel\">\n         <property name=\"text\">\n          <string/>\n         </property>\n         <property name=\"scaledContents\">\n          <bool>true</bool>\n         </property>\n         <property name=\"alignment\">\n          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n         <property name=\"textInteractionFlags\">\n          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>DuplicateFromOffsetDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>DuplicateFromOffsetDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/EditFunctionDialog.cpp",
    "content": "#include \"EditFunctionDialog.h\"\n#include \"ui_EditFunctionDialog.h\"\n\nEditFunctionDialog::EditFunctionDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::EditFunctionDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n}\n\nEditFunctionDialog::~EditFunctionDialog() {}\n\nQString EditFunctionDialog::getNameText()\n{\n    QString ret = ui->nameLineEdit->text();\n    return ret;\n}\n\nvoid EditFunctionDialog::setNameText(const QString &name)\n{\n    ui->nameLineEdit->setText(name);\n}\n\nQString EditFunctionDialog::getStartAddrText()\n{\n    QString ret = ui->startLineEdit->text();\n    return ret;\n}\n\nvoid EditFunctionDialog::setStartAddrText(const QString &startAddr)\n{\n    ui->startLineEdit->setText(startAddr);\n}\n\nQString EditFunctionDialog::getStackSizeText()\n{\n    QString ret = ui->stackSizeLineEdit->text();\n    return ret;\n}\n\nvoid EditFunctionDialog::setStackSizeText(const QString &stackSize)\n{\n    ui->stackSizeLineEdit->setText(stackSize);\n}\n\nvoid EditFunctionDialog::setCallConList(const QStringList &callConList)\n{\n    ui->callConComboBox->addItems(callConList);\n}\n\nvoid EditFunctionDialog::setCallConSelected(const QString &selected)\n{\n    ui->callConComboBox->setCurrentText(selected);\n}\n\nQString EditFunctionDialog::getCallConSelected()\n{\n    return ui->callConComboBox->currentText();\n}\n\nvoid EditFunctionDialog::on_buttonBox_accepted() {}\n\nvoid EditFunctionDialog::on_buttonBox_rejected()\n{\n    close();\n}\n"
  },
  {
    "path": "src/dialogs/EditFunctionDialog.h",
    "content": "#ifndef EDITFUNCTIONDIALOG_H\n#define EDITFUNCTIONDIALOG_H\n\n#include <QDialog>\n#include <memory>\n\nnamespace Ui {\nclass EditFunctionDialog;\n}\n\nclass EditFunctionDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit EditFunctionDialog(QWidget *parent = nullptr);\n    ~EditFunctionDialog();\n    QString getNameText();\n    void setNameText(const QString &name);\n    QString getStartAddrText();\n    void setStartAddrText(const QString &startAddr);\n    QString getEndAddrText();\n    void setEndAddrText(const QString &endAddr);\n    QString getStackSizeText();\n    void setStackSizeText(const QString &stackSize);\n    void setCallConList(const QStringList &callConList);\n    void setCallConSelected(const QString &selected);\n    QString getCallConSelected();\n\nprivate slots:\n    void on_buttonBox_accepted();\n\n    void on_buttonBox_rejected();\n\nprivate:\n    std::unique_ptr<Ui::EditFunctionDialog> ui;\n};\n\n#endif // EDITFUNCTIONDIALOG_H\n"
  },
  {
    "path": "src/dialogs/EditFunctionDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>EditFunctionDialog</class>\n <widget class=\"QDialog\" name=\"EditFunctionDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>318</width>\n    <height>192</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit Function</string>\n  </property>\n  <property name=\"toolTipDuration\">\n   <number>0</number>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"spacing\">\n    <number>2</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>2</number>\n   </property>\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"nameLabel\">\n       <property name=\"text\">\n        <string>Name of function</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"nameLineEdit\"/>\n     </item>\n     <item row=\"4\" column=\"0\">\n      <widget class=\"QLabel\" name=\"startLabel\">\n       <property name=\"text\">\n        <string>Start address</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"startLineEdit\"/>\n     </item>\n     <item row=\"5\" column=\"0\">\n      <widget class=\"QLabel\" name=\"stackSizeLabel\">\n       <property name=\"text\">\n        <string>Stack size</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"stackSizeLineEdit\"/>\n     </item>\n     <item row=\"6\" column=\"0\">\n      <widget class=\"QLabel\" name=\"callConLabel\">\n       <property name=\"text\">\n        <string>Calling convention</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"callConComboBox\"/>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>EditFunctionDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>158</x>\n     <y>142</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>158</x>\n     <y>78</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>EditFunctionDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>158</x>\n     <y>142</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>158</x>\n     <y>78</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/EditInstructionDialog.cpp",
    "content": "#include \"EditInstructionDialog.h\"\n#include \"ui_EditInstructionDialog.h\"\n#include \"core/Cutter.h\"\n\n#include <QCheckBox>\n\nEditInstructionDialog::EditInstructionDialog(InstructionEditMode editMode, QWidget *parent)\n    : QDialog(parent), ui(new Ui::EditInstructionDialog), editMode(editMode)\n{\n    ui->setupUi(this);\n    ui->lineEdit->setMinimumWidth(400);\n    ui->instructionLabel->setWordWrap(true);\n    if (editMode == EDIT_TEXT) {\n        ui->fillWithNops->setVisible(true);\n        ui->fillWithNops->setCheckState(Qt::Checked);\n    } else {\n        ui->fillWithNops->setVisible(false);\n    }\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    connect(ui->lineEdit, &QLineEdit::textEdited, this, &EditInstructionDialog::updatePreview);\n}\n\nEditInstructionDialog::~EditInstructionDialog() {}\n\nvoid EditInstructionDialog::on_buttonBox_accepted() {}\n\nvoid EditInstructionDialog::on_buttonBox_rejected()\n{\n    close();\n}\n\nbool EditInstructionDialog::needsNops() const\n{\n    if (editMode != EDIT_TEXT) {\n        return false;\n    }\n\n    return ui->fillWithNops->checkState() == Qt::Checked;\n}\n\nQString EditInstructionDialog::getInstruction() const\n{\n    return ui->lineEdit->text();\n}\n\nvoid EditInstructionDialog::setInstruction(const QString &instruction)\n{\n    ui->lineEdit->setText(instruction);\n    ui->lineEdit->selectAll();\n    updatePreview(instruction);\n}\n\nvoid EditInstructionDialog::updatePreview(const QString &input)\n{\n    QString result;\n\n    if (editMode == EDIT_NONE) {\n        ui->instructionLabel->setText(\"\");\n        return;\n    } else if (editMode == EDIT_BYTES) {\n        QByteArray data = CutterCore::hexStringToBytes(input);\n        result = Core()->disassemble(data).replace('\\n', \"; \");\n    } else if (editMode == EDIT_TEXT) {\n        QByteArray data = Core()->assemble(input);\n        result = CutterCore::bytesToHexString(data).trimmed();\n    }\n\n    if (result.isEmpty() || result.contains(\"invalid\")) {\n        ui->instructionLabel->setText(\"Unknown Instruction\");\n    } else {\n        ui->instructionLabel->setText(result);\n    }\n}\n"
  },
  {
    "path": "src/dialogs/EditInstructionDialog.h",
    "content": "#ifndef EDITINSTRUCTIONDIALOG_H\n#define EDITINSTRUCTIONDIALOG_H\n\n#include <QDialog>\n#include <memory>\n\nnamespace Ui {\nclass EditInstructionDialog;\n}\n\nenum InstructionEditMode { EDIT_NONE, EDIT_BYTES, EDIT_TEXT };\n\nclass EditInstructionDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit EditInstructionDialog(InstructionEditMode isEditingBytes, QWidget *parent = nullptr);\n    ~EditInstructionDialog();\n\n    QString getInstruction() const;\n    void setInstruction(const QString &instruction);\n    bool needsNops() const;\n\nprivate slots:\n    void on_buttonBox_accepted();\n    void on_buttonBox_rejected();\n\n    void updatePreview(const QString &input);\n\nprivate:\n    std::unique_ptr<Ui::EditInstructionDialog> ui;\n    // defines if the user is editing bytes or asm\n    InstructionEditMode editMode;\n};\n\n#endif // EDITINSTRUCTIONDIALOG_H\n"
  },
  {
    "path": "src/dialogs/EditInstructionDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>EditInstructionDialog</class>\n <widget class=\"QDialog\" name=\"EditInstructionDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>151</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit Instruction</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>false</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"spacing\">\n    <number>2</number>\n   </property>\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetFixedSize</enum>\n   </property>\n   <property name=\"leftMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>2</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>2</number>\n   </property>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <property name=\"leftMargin\">\n      <number>5</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>5</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>5</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>5</number>\n     </property>\n     <item>\n      <spacer name=\"verticalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Vertical</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>40</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"lineEdit\"/>\n     </item>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <property name=\"leftMargin\">\n        <number>5</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QLabel\" name=\"instructionLabel\">\n         <property name=\"baseSize\">\n          <size>\n           <width>1000</width>\n           <height>10</height>\n          </size>\n         </property>\n         <property name=\"layoutDirection\">\n          <enum>Qt::RightToLeft</enum>\n         </property>\n         <property name=\"text\">\n          <string>Unknown Instruction</string>\n         </property>\n         <property name=\"textInteractionFlags\">\n          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <widget class=\"QCheckBox\" name=\"fillWithNops\">\n       <property name=\"text\">\n        <string>Fill all remaining bytes with NOP opcodes</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"verticalSpacer_1\">\n       <property name=\"orientation\">\n        <enum>Qt::Vertical</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>40</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>EditInstructionDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>EditInstructionDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/EditMethodDialog.cpp",
    "content": "#include \"EditMethodDialog.h\"\n#include \"ui_EditMethodDialog.h\"\n\n#include <QComboBox>\n\nEditMethodDialog::EditMethodDialog(bool classFixed, QWidget *parent)\n    : QDialog(parent), ui(new Ui::EditMethodDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    if (classFixed) {\n        classLabel = new QLabel(this);\n        ui->formLayout->setItem(0, QFormLayout::FieldRole, new QWidgetItem(classLabel));\n    } else {\n        classComboBox = new QComboBox(this);\n        ui->formLayout->setItem(0, QFormLayout::FieldRole, new QWidgetItem(classComboBox));\n        for (auto &cls : Core()->getAllAnalysisClasses(true)) {\n            classComboBox->addItem(cls, cls);\n        }\n    }\n\n    updateVirtualUI();\n    validateInput();\n\n    connect(ui->virtualCheckBox, &QCheckBox::stateChanged, this,\n            &EditMethodDialog::updateVirtualUI);\n    connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditMethodDialog::validateInput);\n    connect(ui->realNameEdit, &QLineEdit::textChanged, this, &EditMethodDialog::updateName);\n    connect(ui->autoRenameCheckBox, &QCheckBox::stateChanged, this,\n            &EditMethodDialog::updateAutoRenameEnabled);\n}\n\nEditMethodDialog::~EditMethodDialog() {}\n\nvoid EditMethodDialog::on_buttonBox_accepted() {}\n\nvoid EditMethodDialog::on_buttonBox_rejected()\n{\n    close();\n}\n\nvoid EditMethodDialog::updateVirtualUI()\n{\n    bool enabled = ui->virtualCheckBox->isChecked();\n    ui->vtableOffsetEdit->setEnabled(enabled);\n    ui->vtableOffsetLabel->setEnabled(enabled);\n}\n\nvoid EditMethodDialog::validateInput()\n{\n    for (auto button : ui->buttonBox->buttons()) {\n        if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {\n            button->setEnabled(inputValid());\n            return;\n        }\n    }\n}\n\nvoid EditMethodDialog::updateName()\n{\n    if (ui->autoRenameCheckBox->isChecked()) {\n        ui->nameEdit->setText(convertRealNameToName(ui->realNameEdit->text()));\n    }\n\n    validateInput();\n}\n\nvoid EditMethodDialog::updateAutoRenameEnabled()\n{\n    ui->nameEdit->setEnabled(!ui->autoRenameCheckBox->isChecked());\n\n    if (ui->autoRenameCheckBox->isChecked()) {\n        ui->nameEdit->setText(convertRealNameToName(ui->realNameEdit->text()));\n    }\n}\n\nbool EditMethodDialog::inputValid()\n{\n    if (ui->nameEdit->text().isEmpty() || ui->realNameEdit->text().isEmpty()) {\n        return false;\n    }\n    // TODO: do more checks here, for example for name clashes\n    return true;\n}\n\nQString EditMethodDialog::convertRealNameToName(const QString &realName)\n{\n    return fromOwnedCharPtr(rz_str_sanitize_sdb_key(realName.toUtf8().constData()));\n}\n\nvoid EditMethodDialog::setClass(const QString &className)\n{\n    if (classComboBox) {\n        if (className.isEmpty()) {\n            classComboBox->setCurrentIndex(0);\n            return;\n        }\n\n        for (int i = 0; i < classComboBox->count(); i++) {\n            QString cls = classComboBox->itemData(i).toString();\n            if (cls == className) {\n                classComboBox->setCurrentIndex(i);\n                break;\n            }\n        }\n    } else {\n        classLabel->setText(className);\n        fixedClass = className;\n    }\n\n    validateInput();\n}\n\nvoid EditMethodDialog::setMethod(const AnalysisMethodDescription &desc)\n{\n    ui->nameEdit->setText(desc.name);\n    ui->realNameEdit->setText(desc.realName);\n    ui->addressEdit->setText(desc.addr != RVA_INVALID ? RzAddressString(desc.addr) : nullptr);\n\n    if (desc.vtableOffset >= 0) {\n        ui->virtualCheckBox->setChecked(true);\n        ui->vtableOffsetEdit->setText(QString::number(desc.vtableOffset));\n    } else {\n        ui->virtualCheckBox->setChecked(false);\n        ui->vtableOffsetEdit->setText(nullptr);\n    }\n\n    // Check if auto-rename should be enabled\n    bool enableAutoRename = ui->nameEdit->text().isEmpty()\n            || ui->nameEdit->text() == convertRealNameToName(ui->realNameEdit->text());\n    ui->autoRenameCheckBox->setChecked(enableAutoRename);\n\n    // Set focus to real name edit widget if auto-rename is enabled\n    if (enableAutoRename) {\n        ui->realNameEdit->setFocus();\n    }\n\n    updateVirtualUI();\n    validateInput();\n}\n\nQString EditMethodDialog::getClass() const\n{\n    if (classComboBox) {\n        int index = classComboBox->currentIndex();\n        if (index < 0) {\n            return nullptr;\n        }\n        return classComboBox->itemData(index).toString();\n    } else {\n        return fixedClass;\n    }\n}\n\nAnalysisMethodDescription EditMethodDialog::getMethod() const\n{\n    AnalysisMethodDescription ret;\n    ret.name = ui->nameEdit->text();\n    ret.realName = ui->realNameEdit->text();\n    ret.addr = Core()->num(ui->addressEdit->text());\n    if (!ui->virtualCheckBox->isChecked()) {\n        ret.vtableOffset = -1;\n    } else {\n        ret.vtableOffset = Core()->num(ui->vtableOffsetEdit->text());\n    }\n    return ret;\n}\n\nbool EditMethodDialog::showDialog(const QString &title, bool classFixed, QString *className,\n                                  AnalysisMethodDescription *desc, QWidget *parent)\n{\n    EditMethodDialog dialog(classFixed, parent);\n    dialog.setWindowTitle(title);\n    dialog.setClass(*className);\n    dialog.setMethod(*desc);\n    int result = dialog.exec();\n    *className = dialog.getClass();\n    *desc = dialog.getMethod();\n    return result == QDialog::DialogCode::Accepted;\n}\n\nvoid EditMethodDialog::newMethod(QString className, const QString &meth, QWidget *parent)\n{\n    AnalysisMethodDescription desc;\n    desc.name = convertRealNameToName(meth);\n    desc.realName = meth;\n    desc.vtableOffset = -1;\n    desc.addr = Core()->getOffset();\n\n    if (!showDialog(tr(\"Create Method\"), false, &className, &desc, parent)) {\n        return;\n    }\n\n    Core()->setAnalysisMethod(className, desc);\n}\n\nvoid EditMethodDialog::editMethod(const QString &className, const QString &meth, QWidget *parent)\n{\n    AnalysisMethodDescription desc;\n    if (!Core()->getAnalysisMethod(className, meth, &desc)) {\n        return;\n    }\n\n    QString classNameCopy = className;\n    if (!showDialog(tr(\"Edit Method\"), false, &classNameCopy, &desc, parent)) {\n        return;\n    }\n    if (desc.name != meth) {\n        Core()->renameAnalysisMethod(className, meth, desc.name);\n    }\n    Core()->setAnalysisMethod(className, desc);\n}\n"
  },
  {
    "path": "src/dialogs/EditMethodDialog.h",
    "content": "#ifndef EDITMETHODDIALOG_H\n#define EDITMETHODDIALOG_H\n\n#include <QDialog>\n#include <memory>\n\n#include \"core/Cutter.h\"\n\nnamespace Ui {\nclass EditMethodDialog;\n}\n\nclass QComboBox;\nclass EditMethodDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    /**\n     * @param classFixed whether the user should be able to change the class. If false, a QComboBox\n     * will be shown, otherwise a plain QLabel.\n     */\n    explicit EditMethodDialog(bool classFixed, QWidget *parent = nullptr);\n    ~EditMethodDialog();\n\n    void setClass(const QString &className);\n    void setMethod(const AnalysisMethodDescription &desc);\n\n    QString getClass() const;\n    AnalysisMethodDescription getMethod() const;\n\n    /**\n     * @brief Helper function to display the dialog\n     *\n     * @param title title of the dialog\n     * @param classFixed whether the user should be able to change the class\n     * @param className initial class name, will be overwritten if the user changed the class\n     * @param desc initial data for the method information\n     * @return whether the dialog was accepted by the user\n     */\n    static bool showDialog(const QString &title, bool classFixed, QString *className,\n                           AnalysisMethodDescription *desc, QWidget *parent = nullptr);\n\n    /**\n     * @brief Show the dialog to add a new method a given class\n     */\n    static void newMethod(QString className = nullptr, const QString &meth = QString(),\n                          QWidget *parent = nullptr);\n\n    /**\n     * @brief Show the dialog to edit a given method of a given class\n     */\n    static void editMethod(const QString &className, const QString &meth,\n                           QWidget *parent = nullptr);\n\nprivate slots:\n    void on_buttonBox_accepted();\n    void on_buttonBox_rejected();\n\n    void updateVirtualUI();\n    void validateInput();\n    void updateName();\n    void updateAutoRenameEnabled();\n\nprivate:\n    std::unique_ptr<Ui::EditMethodDialog> ui;\n\n    QComboBox *classComboBox = nullptr;\n    QLabel *classLabel = nullptr;\n    /**\n     * This will only be used when the dialog was created with classFixed = true in order to\n     * remember the class name.\n     */\n    QString fixedClass;\n\n    bool inputValid();\n    static QString convertRealNameToName(const QString &realName);\n};\n\n#endif // EDITMETHODDIALOG_H\n"
  },
  {
    "path": "src/dialogs/EditMethodDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>EditMethodDialog</class>\n <widget class=\"QDialog\" name=\"EditMethodDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>461</width>\n    <height>238</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Method</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Class:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"nameLabel\">\n       <property name=\"text\">\n        <string>Unique Identifier (name):</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"QLineEdit\" name=\"nameEdit\"/>\n       </item>\n       <item>\n        <widget class=\"QCheckBox\" name=\"autoRenameCheckBox\">\n         <property name=\"text\">\n          <string>Auto-Rename</string>\n         </property>\n         <property name=\"checked\">\n          <bool>false</bool>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"realNameLabel\">\n       <property name=\"text\">\n        <string>Display Name (realname):</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"realNameEdit\"/>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"addressLabel\">\n       <property name=\"text\">\n        <string>Address:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"addressEdit\"/>\n     </item>\n     <item row=\"4\" column=\"0\">\n      <widget class=\"QLabel\" name=\"virtualLabel\">\n       <property name=\"text\">\n        <string>Virtual:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"1\">\n      <widget class=\"QCheckBox\" name=\"virtualCheckBox\">\n       <property name=\"text\">\n        <string/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"0\">\n      <widget class=\"QLabel\" name=\"vtableOffsetLabel\">\n       <property name=\"text\">\n        <string>Offset in VTable:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"vtableOffsetEdit\"/>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>nameEdit</tabstop>\n  <tabstop>autoRenameCheckBox</tabstop>\n  <tabstop>realNameEdit</tabstop>\n  <tabstop>addressEdit</tabstop>\n  <tabstop>virtualCheckBox</tabstop>\n  <tabstop>vtableOffsetEdit</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>EditMethodDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>EditMethodDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/EditRegProfileDialog.cpp",
    "content": "#include \"EditRegProfileDialog.h\"\n#include \"ui_EditRegProfileDialog.h\"\n\n#include <QRegularExpression>\n#include <QTextStream>\n#include <QMessageBox>\n#include <QMenu>\n\nEditRegProfileDialog::EditRegProfileDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::EditRegProfileDialog)\n{\n    ui->setupUi(this);\n\n    aliasModel = new QStandardItemModel(0, ALIAS_MAX_COL, this);\n    aliasModel->setHorizontalHeaderLabels({ tr(\"Alias\"), tr(\"Register\") });\n    ui->aliasTable->setModel(aliasModel);\n\n    regModel = new QStandardItemModel(0, REG_MAX_COL, this);\n    regModel->setHorizontalHeaderLabels({ tr(\"Type\"), tr(\"Name\"), tr(\"Size\"), tr(\"Offset\"),\n                                          tr(\"Packed\"), tr(\"Flags/Comment\") });\n    ui->regTable->setModel(regModel);\n\n    ui->aliasTable->verticalHeader()->hide();\n    ui->regTable->verticalHeader()->hide();\n    ui->aliasTable->horizontalHeader()->setStretchLastSection(true);\n    ui->regTable->horizontalHeader()->setStretchLastSection(true);\n\n    connect(ui->addRowBtn, &QPushButton::clicked, this, &EditRegProfileDialog::onAddRow);\n    connect(ui->removeRowBtn, &QPushButton::clicked, this, &EditRegProfileDialog::onRemoveRow);\n    connect(ui->tabWidget, &QTabWidget::currentChanged, this, &EditRegProfileDialog::onTabChanged);\n\n    ui->aliasTable->setContextMenuPolicy(Qt::CustomContextMenu);\n    ui->regTable->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(ui->aliasTable, &QTableView::customContextMenuRequested, this,\n            &EditRegProfileDialog::showContextMenu);\n    connect(ui->regTable, &QTableView::customContextMenuRequested, this,\n            &EditRegProfileDialog::showContextMenu);\n}\n\nEditRegProfileDialog::~EditRegProfileDialog() {}\n\nEditRegProfileDialog::TabContext EditRegProfileDialog::getActiveTab() const\n{\n    if (ui->tabWidget->currentIndex() == TAB_ALIASES) {\n        return { ui->aliasTable, aliasModel };\n    }\n    return { ui->regTable, regModel };\n}\n\nvoid EditRegProfileDialog::setProfileData(const QString &data)\n{\n    ui->rawProfileEdit->setPlainText(data);\n    parseToTables(data);\n}\n\nQString EditRegProfileDialog::getProfleData() const\n{\n    if (ui->tabWidget->currentIndex() == TAB_RAW) {\n        return ui->rawProfileEdit->toPlainText();\n    }\n    return tablesToText();\n}\n\nvoid EditRegProfileDialog::parseToTables(const QString &data)\n{\n    aliasModel->removeRows(0, aliasModel->rowCount());\n    regModel->removeRows(0, regModel->rowCount());\n    ui->aliasTable->clearSpans();\n    ui->regTable->clearSpans();\n\n    static const QRegularExpression re(\"\\\\s+\");\n    const QStringList lines = data.split('\\n');\n\n    bool parsingRegisters = false;\n\n    for (const QString &line : lines) {\n        QString trimmed = line.trimmed();\n        if (trimmed.isEmpty()) {\n            continue;\n        }\n\n        if (!parsingRegisters && !trimmed.startsWith('#') && !trimmed.startsWith('=')) {\n            parsingRegisters = true;\n        }\n\n        // handle comments\n        if (trimmed.startsWith('#')) {\n            QStandardItem *commentItem = new QStandardItem(trimmed);\n            commentItem->setData(true, CommentRole);\n\n            if (parsingRegisters) {\n                QList<QStandardItem *> items = { commentItem };\n                for (int i = 1; i < REG_MAX_COL; ++i) {\n                    items << new QStandardItem(\"\");\n                }\n                int rowIdx = regModel->rowCount();\n                regModel->appendRow(items);\n                ui->regTable->setSpan(rowIdx, 0, 1, REG_MAX_COL);\n            } else {\n                QList<QStandardItem *> items = { commentItem, new QStandardItem(\"\") };\n                int rowIdx = aliasModel->rowCount();\n                aliasModel->appendRow(items);\n                ui->aliasTable->setSpan(rowIdx, 0, 1, ALIAS_MAX_COL);\n            }\n            continue;\n        }\n\n        if (trimmed.startsWith('=')) {\n            QStringList parts = trimmed.mid(1).split(re);\n            QList<QStandardItem *> items;\n            for (int i = 0; i < ALIAS_MAX_COL; ++i) {\n                items << new QStandardItem(i < parts.size() ? parts[i] : \"\");\n            }\n            aliasModel->appendRow(items);\n        } else {\n            QStringList parts = trimmed.split(re);\n            QList<QStandardItem *> items;\n            for (int i = 0; i < REG_MAX_COL; ++i) {\n                items << new QStandardItem(i < parts.size() ? parts[i] : \"\");\n            }\n            regModel->appendRow(items);\n        }\n    }\n}\n\nQString EditRegProfileDialog::tablesToText() const\n{\n    QString out;\n    QTextStream ts(&out);\n\n    // Aliases\n    for (int i = 0; i < aliasModel->rowCount(); ++i) {\n        QStandardItem *firstItem = aliasModel->item(i, 0);\n        if (firstItem && firstItem->data(CommentRole).toBool()) {\n            ts << firstItem->text() << \"\\n\";\n        } else {\n            ts << \"=\";\n            for (int j = 0; j < ALIAS_MAX_COL; ++j) {\n                ts << aliasModel->index(i, j).data().toString().trimmed();\n                if (j < ALIAS_MAX_COL - 1) {\n                    ts << \"\\t\";\n                }\n            }\n            ts << \"\\n\";\n        }\n    }\n\n    // Registers\n    for (int i = 0; i < regModel->rowCount(); ++i) {\n        QStandardItem *firstItem = regModel->item(i, 0);\n        if (firstItem && firstItem->data(CommentRole).toBool()) {\n            ts << firstItem->text() << \"\\n\";\n        } else {\n            for (int j = 0; j < REG_MAX_COL; ++j) {\n                ts << regModel->index(i, j).data().toString().trimmed();\n                if (j < REG_MAX_COL - 1) {\n                    ts << \"\\t\";\n                }\n            }\n            ts << \"\\n\";\n        }\n    }\n    return out;\n}\n\nvoid EditRegProfileDialog::onAddRow()\n{\n    TabContext tabCtx = getActiveTab();\n    auto model = tabCtx.model;\n    auto view = tabCtx.view;\n    QModelIndex current = view->currentIndex();\n    int row = current.isValid() ? current.row() + 1 : model->rowCount();\n    model->insertRow(row);\n\n    QModelIndex newIdx = model->index(row, 0);\n    view->setCurrentIndex(newIdx);\n    view->edit(newIdx);\n}\n\nvoid EditRegProfileDialog::onRemoveRow()\n{\n    TabContext tabCtx = getActiveTab();\n    auto model = tabCtx.model;\n    auto view = tabCtx.view;\n    QModelIndex current = view->currentIndex();\n    if (current.isValid()) {\n        model->removeRow(current.row());\n    }\n}\n\nvoid EditRegProfileDialog::onTabChanged(int index)\n{\n    bool raw = (index == TAB_RAW);\n    if (raw) {\n        ui->rawProfileEdit->setPlainText(tablesToText());\n    } else {\n        parseToTables(ui->rawProfileEdit->toPlainText());\n    }\n    ui->addRowBtn->setVisible(!raw);\n    ui->removeRowBtn->setVisible(!raw);\n}\n\nvoid EditRegProfileDialog::showContextMenu(const QPoint &pos)\n{\n    TabContext tabCtx = getActiveTab();\n    auto view = tabCtx.view;\n    auto model = tabCtx.model;\n\n    QModelIndex index = view->indexAt(pos);\n    int row = index.isValid() ? index.row() : model->rowCount();\n\n    QMenu menu(this);\n    QAction *addAbove = menu.addAction(tr(\"Add Row Above\"));\n    QAction *addBelow = menu.addAction(tr(\"Add Row Below\"));\n    menu.addSeparator();\n    QAction *remove = menu.addAction(tr(\"Remove Row\"));\n\n    remove->setEnabled(index.isValid());\n    QAction *selected = menu.exec(view->viewport()->mapToGlobal(pos));\n\n    if (!selected) {\n        return;\n    }\n    if (selected == addAbove) {\n        model->insertRow(row);\n        view->setCurrentIndex(model->index(row, 0));\n    } else if (selected == addBelow) {\n        model->insertRow(row + 1);\n        view->setCurrentIndex(model->index(row + 1, 0));\n    } else if (selected == remove) {\n        model->removeRow(row);\n    }\n}\n"
  },
  {
    "path": "src/dialogs/EditRegProfileDialog.h",
    "content": "#ifndef EDITREGPROFILEDIALOG_H\n#define EDITREGPROFILEDIALOG_H\n\n#include <QDialog>\n#include <QStandardItemModel>\n#include <QTableView>\n#include <memory>\n\nnamespace Ui {\nclass EditRegProfileDialog;\n}\n\n/**\n * @brief Dialog for editing Rizin register profiles using tables or raw text\n */\nclass EditRegProfileDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit EditRegProfileDialog(QWidget *parent = nullptr);\n    ~EditRegProfileDialog();\n\n    /**\n     * @brief Loads a raw profile string into the editor\n     * @param data The raw Rizin profile string to parse\n     */\n    void setProfileData(const QString &data);\n\n    /**\n     * @brief Retrieves the current profile data from the active editor tab\n     * @return The formatted profile string\n     */\n    QString getProfleData() const;\n\nprivate slots:\n    /**\n     * @brief Adds a new row under the cursor, or at the end if no row is selected\n     */\n    void onAddRow();\n\n    /**\n     * @brief Deletes the currently selected row\n     */\n    void onRemoveRow();\n\n    /**\n     * @brief Syncs data between tables and the raw text editor when switching tabs\n     * @param index The index of the newly selected tab\n     */\n    void onTabChanged(int index);\n\n    /**\n     * @brief Shows a custom context menu for adding or removing rows at the clicked position\n     * @param pos The position where the context menu was requested, relative to the table viewport\n     */\n    void showContextMenu(const QPoint &pos);\n\nprivate:\n    enum CustomRole { CommentRole = Qt::UserRole + 1 };\n    enum TabIndex { TAB_ALIASES, TAB_REGISTERS, TAB_RAW };\n    enum AliasCol { ALIAS_NAME = 0, ALIAS_REG, ALIAS_MAX_COL };\n    enum RegCol {\n        REG_TYPE = 0,\n        REG_NAME,\n        REG_SIZE,\n        REG_OFFSET,\n        REG_PACKED,\n        REG_FLAGS,\n        REG_MAX_COL,\n    };\n\n    struct TabContext\n    {\n        QTableView *view;\n        QStandardItemModel *model;\n    };\n\n    QStandardItemModel *aliasModel;\n    QStandardItemModel *regModel;\n\n    /**\n     * @brief Detects which tab is active\n     * @return A TabContext containing the active view and model\n     */\n    TabContext getActiveTab() const;\n\n    /**\n     * @brief Converts raw profile string to table entries\n     * @param data The string to be parsed into table enries\n     */\n    void parseToTables(const QString &data);\n\n    /**\n     * @brief Converts table entries to raw profile string\n     * @return A tab-separated string representation of the tables\n     */\n    QString tablesToText() const;\n\n    std::unique_ptr<Ui::EditRegProfileDialog> ui;\n};\n\n#endif\n"
  },
  {
    "path": "src/dialogs/EditRegProfileDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>EditRegProfileDialog</class>\n <widget class=\"QDialog\" name=\"EditRegProfileDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>800</width>\n    <height>600</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit Register Profile</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"mainLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <widget class=\"QWidget\" name=\"tabAlias\">\n      <attribute name=\"title\">\n       <string>Aliases</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QTableView\" name=\"aliasTable\">\n         <attribute name=\"horizontalHeaderStretchLastSection\">\n          <bool>true</bool>\n         </attribute>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"tabRegs\">\n      <attribute name=\"title\">\n       <string>Registers</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QTableView\" name=\"regTable\">\n         <attribute name=\"horizontalHeaderStretchLastSection\">\n          <bool>true</bool>\n         </attribute>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"tabRaw\">\n      <attribute name=\"title\">\n       <string>Raw Profile</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\">\n       <item>\n        <widget class=\"QPlainTextEdit\" name=\"rawProfileEdit\">\n         <property name=\"font\">\n          <font>\n           <family>Monospace</family>\n          </font>\n         </property>\n         <property name=\"lineWrapMode\">\n          <enum>QPlainTextEdit::LineWrapMode::NoWrap</enum>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"actionLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addRowBtn\">\n       <property name=\"text\">\n        <string>Add Row</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"removeRowBtn\">\n       <property name=\"text\">\n        <string>Remove Row</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"hSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Orientation::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>0</width>\n         <height>0</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>EditRegProfileDialog</receiver>\n   <slot>accept()</slot>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>EditRegProfileDialog</receiver>\n   <slot>reject()</slot>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/EditStringDialog.cpp",
    "content": "#include \"EditStringDialog.h\"\n#include \"ui_EditStringDialog.h\"\n\nEditStringDialog::EditStringDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::EditStringDialog {})\n{\n    ui->setupUi(this);\n    ui->spinBox_size->setMinimum(0);\n    ui->lineEdit_address->setMinimumWidth(150);\n    ui->spinBox_size->setFocus();\n    ui->comboBox_type->addItems({ \"Auto\", \"ASCII/Latin1\", \"UTF-8\" });\n    connect(ui->checkBox_autoSize, &QCheckBox::toggled, ui->spinBox_size, &QSpinBox::setDisabled);\n}\n\nEditStringDialog::~EditStringDialog() {}\n\nvoid EditStringDialog::setStringStartAddress(uint64_t address)\n{\n    ui->lineEdit_address->setText(QString::number(address, 16));\n}\n\nbool EditStringDialog::getStringStartAddress(uint64_t &returnValue) const\n{\n    bool status = false;\n    returnValue = ui->lineEdit_address->text().toLongLong(&status, 16);\n    return status;\n}\n\nvoid EditStringDialog::setStringSizeValue(uint32_t size)\n{\n    ui->spinBox_size->setValue(size);\n}\n\nint EditStringDialog::getStringSizeValue() const\n{\n    if (ui->checkBox_autoSize->isChecked()) {\n        return -1;\n    }\n\n    return ui->spinBox_size->value();\n}\n\nEditStringDialog::StringType EditStringDialog::getStringType() const\n{\n    const int indexVal = ui->comboBox_type->currentIndex();\n\n    switch (indexVal) {\n    case 0: {\n        return EditStringDialog::StringType::Auto;\n    }\n    case 1: {\n        return EditStringDialog::StringType::ASCII_LATIN1;\n    }\n    case 2: {\n        return EditStringDialog::StringType::UTF8;\n    }\n    default:\n        return EditStringDialog::StringType::Auto;\n    }\n}\n"
  },
  {
    "path": "src/dialogs/EditStringDialog.h",
    "content": "#ifndef EDITSTRINGDIALOG_H\n#define EDITSTRINGDIALOG_H\n\n#include <memory>\n#include <QDialog>\n\nnamespace Ui {\nclass EditStringDialog;\n}\n\nclass EditStringDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    enum class StringType { Auto, ASCII_LATIN1, UTF8 };\n    explicit EditStringDialog(QWidget *parent = nullptr);\n    ~EditStringDialog();\n\n    /**\n     * @brief Sets the address of the first byte of potential string in the dialog\n     *\n     * @param address The address of the bytearray where string is located\n     */\n    void setStringStartAddress(uint64_t address);\n    /**\n     * @brief Returns the address of the first byte of potential string in the dialog\n     *\n     * @param[out] returnValue The address of the bytearray where string is located\n     * @return whether the call successful or not\n     */\n    bool getStringStartAddress(uint64_t &returnValue) const;\n\n    /**\n     * @brief Sets the size of string in the dialog\n     *\n     * @param size The size of string in the dialog\n     */\n    void setStringSizeValue(uint32_t size);\n    /**\n     * @brief Returns the size of string in the dialog\n     *\n     * @return -1 on error otherwise the size of string from the dialog\n     */\n    int getStringSizeValue() const;\n\n    /**\n     * @brief Returns the type of string from the dialog\n     *\n     * @return The type of string from the dialog\n     */\n    StringType getStringType() const;\n\nprivate:\n    std::unique_ptr<Ui::EditStringDialog> ui;\n};\n\n#endif // EDITSTRINGDIALOG_H\n"
  },
  {
    "path": "src/dialogs/EditStringDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>EditStringDialog</class>\n <widget class=\"QDialog\" name=\"EditStringDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::WindowModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>243</width>\n    <height>109</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit string</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QDialogButtonBox\" name=\"dialogButtonBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"0\" colspan=\"2\">\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"QLabel\" name=\"label_size\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n         <property name=\"text\">\n          <string>Size:</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"label_type\">\n         <property name=\"text\">\n          <string>Type:</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"label_address\">\n         <property name=\"text\">\n          <string>Address:</string>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n       <item>\n        <widget class=\"QSpinBox\" name=\"spinBox_size\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>150</width>\n           <height>24</height>\n          </size>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QComboBox\" name=\"comboBox_type\"/>\n       </item>\n       <item>\n        <widget class=\"QLineEdit\" name=\"lineEdit_address\">\n         <property name=\"text\">\n          <string/>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item alignment=\"Qt::AlignTop\">\n      <widget class=\"QCheckBox\" name=\"checkBox_autoSize\">\n       <property name=\"text\">\n        <string>Auto</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>spinBox_size</tabstop>\n  <tabstop>checkBox_autoSize</tabstop>\n  <tabstop>comboBox_type</tabstop>\n  <tabstop>lineEdit_address</tabstop>\n  <tabstop>dialogButtonBox</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>dialogButtonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>EditStringDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>51</x>\n     <y>94</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>174</x>\n     <y>67</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>dialogButtonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>EditStringDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>143</x>\n     <y>92</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>153</x>\n     <y>71</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/EditVariablesDialog.cpp",
    "content": "#include \"EditVariablesDialog.h\"\n#include \"ui_EditVariablesDialog.h\"\n\n#include <QMetaType>\n#include <QComboBox>\n#include <QMetaType>\n#include <QPushButton>\n\nEditVariablesDialog::EditVariablesDialog(RVA offset, QString initialVar, QWidget *parent)\n    : QDialog(parent), ui(new Ui::EditVariablesDialog), functionAddress(RVA_INVALID)\n{\n    ui->setupUi(this);\n    connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &EditVariablesDialog::applyFields);\n    connect<void (QComboBox::*)(int)>(ui->dropdownLocalVars, &QComboBox::currentIndexChanged, this,\n                                      &EditVariablesDialog::updateFields);\n\n    auto core = Core()->lock();\n    RzAnalysisFunction *f = rz_analysis_get_function_at(core->analysis, offset);\n    QString fcnName = f->name;\n    functionAddress = offset;\n    setWindowTitle(tr(\"Edit Variables in Function: %1\").arg(fcnName));\n\n    variables = Core()->getVariables(offset);\n    int currentItemIndex = -1;\n    int index = 0;\n    for (const VariableDescription &var : variables) {\n        ui->dropdownLocalVars->addItem(var.name, QVariant::fromValue(var));\n        if (var.name == initialVar) {\n            currentItemIndex = index;\n        }\n        index++;\n    }\n    ui->dropdownLocalVars->setCurrentIndex(currentItemIndex);\n    if (currentItemIndex != -1) {\n        ui->nameEdit->setFocus();\n    }\n\n    populateTypesComboBox();\n    updateFields();\n}\n\nEditVariablesDialog::~EditVariablesDialog()\n{\n    delete ui;\n}\n\nbool EditVariablesDialog::empty() const\n{\n    return ui->dropdownLocalVars->count() == 0;\n}\n\nvoid EditVariablesDialog::applyFields()\n{\n    if (ui->dropdownLocalVars->currentIndex() < 0) {\n        // nothing was selected or list is empty\n        return;\n    }\n    VariableDescription desc = ui->dropdownLocalVars->currentData().value<VariableDescription>();\n\n    RzCoreLocked core(Core());\n    RzAnalysisFunction *fcn = Core()->functionIn(core->offset);\n    if (!fcn) {\n        return;\n    }\n\n    RzAnalysisVar *v = rz_analysis_function_get_var_byname(fcn, desc.name.toUtf8().constData());\n    if (!v) {\n        return;\n    }\n\n    char *error_msg = NULL;\n    RzType *v_type = rz_type_parse_string_single(\n            core->analysis->typedb->parser, ui->typeComboBox->currentText().toUtf8().constData(),\n            &error_msg);\n    if (!v_type || error_msg) {\n        return;\n    }\n    rz_analysis_var_set_type(v, v_type, true);\n\n    // TODO Remove all those replace once rizin command parser is fixed\n    QString newName = ui->nameEdit->text()\n                              .replace(QLatin1Char(' '), QLatin1Char('_'))\n                              .replace(QLatin1Char('\\\\'), QLatin1Char('_'))\n                              .replace(QLatin1Char('/'), QLatin1Char('_'));\n    if (newName != desc.name) {\n        Core()->renameFunctionVariable(newName, desc.name, functionAddress);\n    }\n\n    // Refresh the views to reflect the changes to vars\n    emit Core()->refreshCodeViews();\n}\n\nvoid EditVariablesDialog::updateFields()\n{\n    bool hasSelection = ui->dropdownLocalVars->currentIndex() >= 0;\n    auto okButton = ui->buttonBox->button(QDialogButtonBox::Ok);\n    okButton->setEnabled(hasSelection);\n    if (!hasSelection) {\n        ui->nameEdit->clear();\n        return;\n    }\n    VariableDescription desc = ui->dropdownLocalVars->currentData().value<VariableDescription>();\n    ui->nameEdit->setText(desc.name);\n    ui->typeComboBox->setCurrentText(desc.type);\n}\n\nstatic void addTypeDescriptionsToComboBox(QComboBox *comboBox, QList<TypeDescription> list)\n{\n    for (const TypeDescription &thisType : list) {\n        comboBox->addItem(thisType.type);\n    }\n    comboBox->insertSeparator(comboBox->count());\n}\n\nvoid EditVariablesDialog::populateTypesComboBox()\n{\n    addTypeDescriptionsToComboBox(ui->typeComboBox, Core()->getAllStructs());\n    addTypeDescriptionsToComboBox(ui->typeComboBox, Core()->getAllPrimitiveTypes());\n    addTypeDescriptionsToComboBox(ui->typeComboBox, Core()->getAllEnums());\n    addTypeDescriptionsToComboBox(ui->typeComboBox, Core()->getAllTypedefs());\n}\n"
  },
  {
    "path": "src/dialogs/EditVariablesDialog.h",
    "content": "#ifndef EDITVARIABLESDIALOG_H\n#define EDITVARIABLESDIALOG_H\n\n#include \"core/Cutter.h\"\n#include <QDialog>\n\nnamespace Ui {\nclass EditVariablesDialog;\n}\n\nclass EditVariablesDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit EditVariablesDialog(RVA offset, QString initialVar = QString(),\n                                 QWidget *parent = nullptr);\n    ~EditVariablesDialog();\n\n    bool empty() const;\nprivate slots:\n    void applyFields();\n    void updateFields();\n\nprivate:\n    Ui::EditVariablesDialog *ui;\n    RVA functionAddress;\n    QList<VariableDescription> variables;\n\n    void populateTypesComboBox();\n};\n\n#endif // EDITVARIABLESDIALOG_H\n"
  },
  {
    "path": "src/dialogs/EditVariablesDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>EditVariablesDialog</class>\n <widget class=\"QDialog\" name=\"EditVariablesDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>435</width>\n    <height>137</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>Modify:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"dropdownLocalVars\"/>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"nameEdit\"/>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"nameLabel\">\n       <property name=\"text\">\n        <string>Name:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"labelSetTypeTo\">\n       <property name=\"text\">\n        <string>Type:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"typeComboBox\">\n       <property name=\"editable\">\n        <bool>true</bool>\n       </property>\n       <property name=\"maxVisibleItems\">\n        <number>15</number>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>EditVariablesDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>EditVariablesDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/FlagDialog.cpp",
    "content": "#include \"FlagDialog.h\"\n#include \"ui_FlagDialog.h\"\n\n#include <QIntValidator>\n#include \"core/Cutter.h\"\n\nFlagDialog::FlagDialog(RVA offset, QWidget *parent, QString flagNameHint)\n    : QDialog(parent),\n      ui(new Ui::FlagDialog),\n      offset(offset),\n      flagName(flagNameHint),\n      flagOffset(RVA_INVALID)\n{\n    // Setup UI\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n    if (!flagName.isEmpty()) {\n        flagOffset = offset;\n    } else {\n        RzCoreLocked core(Core());\n        RzFlagItem *flag = rz_flag_get_i(core->flags, offset);\n        if (flag) {\n            flagName = QString(flag->name);\n            flagOffset = flag->offset;\n        }\n    }\n\n    auto size_validator = new QIntValidator(ui->sizeEdit);\n    size_validator->setBottom(1);\n    ui->sizeEdit->setValidator(size_validator);\n    if (!flagName.isEmpty()) {\n        ui->nameEdit->setText(flagName);\n        ui->labelAction->setText(tr(\"Edit flag at %1\").arg(RzAddressString(offset)));\n    } else {\n        ui->labelAction->setText(tr(\"Add flag at %1\").arg(RzAddressString(offset)));\n    }\n\n    // Connect slots\n    connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &FlagDialog::buttonBoxAccepted);\n    connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &FlagDialog::buttonBoxRejected);\n}\n\nFlagDialog::~FlagDialog() {}\n\nvoid FlagDialog::buttonBoxAccepted()\n{\n    RVA size = ui->sizeEdit->text().toULongLong();\n    QString name = ui->nameEdit->text();\n\n    if (name.isEmpty()) {\n        if (flagOffset != RVA_INVALID) {\n            // Empty name and flag exists -> delete the flag\n            Core()->delFlag(flagName);\n        } else {\n            // Flag was not existing and we gave an empty name, do nothing\n        }\n    } else {\n        if (flagOffset != RVA_INVALID) {\n            // Name provided and flag exists -> rename the flag\n            Core()->renameFlag(flagName, name);\n        } else {\n            // Name provided and flag does not exist -> create the flag\n            Core()->addFlag(offset, name, size);\n        }\n    }\n    close();\n    this->setResult(QDialog::Accepted);\n}\n\nvoid FlagDialog::buttonBoxRejected()\n{\n    close();\n    this->setResult(QDialog::Rejected);\n}\n"
  },
  {
    "path": "src/dialogs/FlagDialog.h",
    "content": "#ifndef FLAGDIALOG_H\n#define FLAGDIALOG_H\n\n#include <QDialog>\n#include <memory>\n#include \"core/CutterCommon.h\"\n\nnamespace Ui {\nclass FlagDialog;\n}\n\nclass FlagDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit FlagDialog(RVA offset, QWidget *parent = nullptr, QString flagNameHint = {});\n    ~FlagDialog();\n\nprivate slots:\n    void buttonBoxAccepted();\n    void buttonBoxRejected();\n\nprivate:\n    std::unique_ptr<Ui::FlagDialog> ui;\n    RVA offset;\n    QString flagName;\n    ut64 flagOffset;\n};\n\n#endif // FLAGDIALOG_H\n"
  },
  {
    "path": "src/dialogs/FlagDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>FlagDialog</class>\n <widget class=\"QDialog\" name=\"FlagDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>452</width>\n    <height>121</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Add Flag</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"labelAction\">\n     <property name=\"text\">\n      <string>Add flag at</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <property name=\"horizontalSpacing\">\n      <number>12</number>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"labelFlag\">\n       <property name=\"font\">\n        <font>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>Flag:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"nameEdit\">\n       <property name=\"frame\">\n        <bool>false</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string notr=\"true\"/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"sizeEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>100</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string>1</string>\n       </property>\n       <property name=\"frame\">\n        <bool>false</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string notr=\"true\"/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"labelSize\">\n       <property name=\"font\">\n        <font>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>Size:</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/GlibcHeapBinsDialog.cpp",
    "content": "#include <HeapBinsGraphView.h>\n#include \"GlibcHeapBinsDialog.h\"\n#include \"ui_GlibcHeapBinsDialog.h\"\n#include \"GlibcHeapInfoDialog.h\"\n\nGlibcHeapBinsDialog::GlibcHeapBinsDialog(RVA m_state, MainWindow *main, QWidget *parent)\n    : QDialog(parent),\n      ui(new Ui::GlibcHeapBinsDialog),\n      m_state(m_state),\n      binsModel(new BinsModel(m_state, this)),\n      main(main)\n{\n    ui->setupUi(this);\n    ui->viewBins->setModel(binsModel);\n    ui->viewBins->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);\n    ui->viewBins->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);\n    ui->viewBins->verticalHeader()->hide();\n    ui->viewBins->resizeColumnsToContents();\n\n    connect(ui->viewBins->selectionModel(), &QItemSelectionModel::currentChanged, this,\n            &GlibcHeapBinsDialog::onCurrentChanged);\n    connect(ui->lineEdit, &QLineEdit::returnPressed, this,\n            &GlibcHeapBinsDialog::showHeapInfoDialog);\n\n    binsModel->reload();\n    ui->viewBins->resizeColumnsToContents();\n    graphView = nullptr;\n    this->setWindowTitle(tr(\"Bins info for arena @ \") + RzAddressString(m_state));\n}\n\nGlibcHeapBinsDialog::~GlibcHeapBinsDialog()\n{\n    delete ui;\n}\n\nvoid GlibcHeapBinsDialog::onCurrentChanged(const QModelIndex &current, const QModelIndex &prev)\n{\n    Q_UNUSED(current);\n    Q_UNUSED(prev);\n    auto currentIndex = ui->viewBins->selectionModel()->currentIndex();\n    setChainInfo(currentIndex.row());\n    setGraphView(currentIndex.row());\n}\n\nvoid GlibcHeapBinsDialog::setChainInfo(int index)\n{\n    // get chunks for the selected bin and construct chain info string\n    RzListIter *iter;\n    RzHeapChunkListItem *item;\n    RzList *chunks = binsModel->getChunks(index);\n    QString chainInfo;\n    CutterRzListForeach (chunks, iter, RzHeapChunkListItem, item) {\n        chainInfo += \" → \" + RzAddressString(item->addr);\n    }\n\n    // Add bin message at the end of the list\n    // responsible for messages like corrupted list, double free\n    QString message = binsModel->getBinMessage(index);\n    if (!message.isEmpty()) {\n        chainInfo += \" \" + message;\n    }\n\n    ui->chainInfoEdit->setPlainText(chainInfo);\n}\n\nvoid GlibcHeapBinsDialog::showHeapInfoDialog()\n{\n    QString str = ui->lineEdit->text();\n    if (!str.isEmpty()) {\n        // summon glibcHeapInfoDialog box with the offset entered\n        RVA offset = Core()->math(str);\n        if (!offset) {\n            ui->lineEdit->setText(QString());\n            return;\n        }\n\n        GlibcHeapInfoDialog dialog(offset, QString(), this);\n        dialog.exec();\n    }\n}\n\nvoid GlibcHeapBinsDialog::setGraphView(int index)\n{\n    if (graphView) {\n        ui->horizontalLayout->removeWidget(graphView);\n        delete graphView;\n    }\n    graphView = new HeapBinsGraphView(this, binsModel->values[index], main);\n    ui->horizontalLayout->addWidget(graphView);\n    graphView->refreshView();\n}\n\nBinsModel::BinsModel(RVA arena_addr, QObject *parent)\n    : QAbstractTableModel(parent), arena_addr(arena_addr)\n{\n}\n\nvoid BinsModel::reload()\n{\n    beginResetModel();\n    clearData();\n    values = Core()->getHeapBins(arena_addr);\n    endResetModel();\n}\n\nint BinsModel::rowCount(const QModelIndex &) const\n{\n    return values.size();\n}\n\nint BinsModel::columnCount(const QModelIndex &) const\n{\n    return ColumnCount;\n}\n\nQVariant BinsModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid() || index.row() >= values.count())\n        return QVariant();\n\n    const auto &item = values.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case BinNumColumn:\n            return item->bin_num;\n        case FdColumn:\n            return (item->fd == 0) ? tr(\"N/A\") : RzAddressString(item->fd);\n        case BkColumn:\n            return (item->bk == 0) ? tr(\"N/A\") : RzAddressString(item->bk);\n        case TypeColumn:\n            return tr(item->type);\n        case CountColumn:\n            return rz_list_length(item->chunks);\n        case SizeColumn:\n            return (item->size == 0) ? tr(\"N/A\") : RzHexString(item->size);\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\nQVariant BinsModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n    Q_UNUSED(orientation);\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case BinNumColumn:\n            return tr(\"#\");\n        case FdColumn:\n            return tr(\"Fd\");\n        case BkColumn:\n            return tr(\"Bk\");\n        case TypeColumn:\n            return tr(\"Type\");\n        case CountColumn:\n            return tr(\"Chunks count\");\n        case SizeColumn:\n            return tr(\"Chunks size\");\n        default:\n            return QVariant();\n        }\n\n    case Qt::ToolTipRole:\n        switch (section) {\n        case BinNumColumn:\n            return tr(\"Bin number in RZ_GLIBC_NBINS or fastbinsY array\");\n        case FdColumn:\n            return tr(\"Pointer to first chunk of the bin\");\n        case BkColumn:\n            return tr(\"Pointer to last chunk of the bin\");\n        case TypeColumn:\n            return tr(\"Type of bin\");\n        case CountColumn:\n            return tr(\"Number of chunks in the bin\");\n        case SizeColumn:\n            return tr(\"Size of all chunks in the bin\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nvoid BinsModel::clearData()\n{\n    for (auto item : values) {\n        rz_heap_bin_free(item);\n    }\n}\n\nRzList *BinsModel::getChunks(int index)\n{\n    return values[index]->chunks;\n}\n\nQString BinsModel::getBinMessage(int index)\n{\n    if (values[index]->message) {\n        return QString(values[index]->message);\n    } else {\n        return QString();\n    }\n}\n"
  },
  {
    "path": "src/dialogs/GlibcHeapBinsDialog.h",
    "content": "#ifndef GLIBCHEAPBINSDIALOG_H\n#define GLIBCHEAPBINSDIALOG_H\n\n#include <QDialog>\n#include <QAbstractTableModel>\n#include \"core/Cutter.h\"\n#include <MainWindow.h>\n#include <HeapBinsGraphView.h>\n\nnamespace Ui {\nclass GlibcHeapBinsDialog;\n}\n\nclass BinsModel : public QAbstractTableModel\n{\n    Q_OBJECT\npublic:\n    explicit BinsModel(RVA arena_addr, QObject *parent = nullptr);\n    enum Column {\n        TypeColumn = 0,\n        BinNumColumn,\n        FdColumn,\n        BkColumn,\n        CountColumn,\n        SizeColumn,\n        ColumnCount\n    };\n    void reload();\n    int rowCount(const QModelIndex &parent) const override;\n    int columnCount(const QModelIndex &parent) const override;\n    void clearData();\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;\n    RVA arena_addr = 0;\n    RzList *getChunks(int index);\n    QString getBinMessage(int index);\n    QVector<RzHeapBin *> values;\n\nprivate:\n};\n\nclass GlibcHeapBinsDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit GlibcHeapBinsDialog(RVA i, MainWindow *main, QWidget *parent);\n    ~GlibcHeapBinsDialog();\n    void onCurrentChanged(const QModelIndex &current, const QModelIndex &prev);\n    void setChainInfo(int index);\n    void setGraphView(int index);\nprivate slots:\n    void showHeapInfoDialog();\n\nprivate:\n    Ui::GlibcHeapBinsDialog *ui;\n    RVA m_state;\n    BinsModel *binsModel {};\n    HeapBinsGraphView *graphView;\n    MainWindow *main;\n};\n\n#endif // GLIBCHEAPBINSDIALOG_H\n"
  },
  {
    "path": "src/dialogs/GlibcHeapBinsDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GlibcHeapBinsDialog</class>\n <widget class=\"QDialog\" name=\"GlibcHeapBinsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>883</width>\n    <height>544</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n     <item>\n      <widget class=\"QTableView\" name=\"viewBins\"/>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Chain info:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPlainTextEdit\" name=\"chainInfoEdit\">\n       <property name=\"readOnly\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout3\">\n       <item>\n        <widget class=\"QLabel\" name=\"label_2\">\n         <property name=\"text\">\n          <string>Detailed chunk info:</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLineEdit\" name=\"lineEdit\">\n         <property name=\"placeholderText\">\n          <string>Enter chunk base address and press enter</string>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/GlibcHeapInfoDialog.cpp",
    "content": "#include \"GlibcHeapInfoDialog.h\"\n\n#include <utility>\n#include \"ui_GlibcHeapInfoDialog.h\"\n\nGlibcHeapInfoDialog::GlibcHeapInfoDialog(RVA offset, QString status, QWidget *parent)\n    : QDialog(parent), ui(new Ui::GlibcHeapInfoDialog), offset(offset), status(std::move(status))\n{\n    ui->setupUi(this);\n\n    // set window title\n    QString windowTitle = tr(\"Chunk @ \") + RzAddressString(offset);\n    if (!this->status.isEmpty()) {\n        windowTitle += QString(\"(\" + this->status + \")\");\n    }\n    this->setWindowTitle(windowTitle);\n\n    connect(ui->saveButton, &QPushButton::clicked, this, &GlibcHeapInfoDialog::saveChunkInfo);\n\n    updateFields();\n}\n\nGlibcHeapInfoDialog::~GlibcHeapInfoDialog()\n{\n    delete ui;\n}\n\nvoid GlibcHeapInfoDialog::updateFields()\n{\n    // get data about the heap chunk from the API\n    RzHeapChunkSimple *chunk = Core()->getHeapChunk(offset);\n    if (!chunk) {\n        return;\n    }\n\n    // Update the information in the widget\n    this->ui->baseEdit->setText(RzAddressString(offset));\n    this->ui->sizeEdit->setText(RzHexString(chunk->size));\n    this->ui->bkEdit->setText(RzAddressString(chunk->bk));\n    this->ui->fdEdit->setText(RzAddressString(chunk->fd));\n    this->ui->bknsEdit->setText(RzAddressString(chunk->bk_nextsize));\n    this->ui->fdnsEdit->setText(RzAddressString(chunk->fd_nextsize));\n    this->ui->prevSizeEdit->setText(RzHexString(chunk->prev_size));\n    if (chunk->is_mmapped) {\n        this->ui->rbIM->setChecked(true);\n    } else {\n        this->ui->rbIM->setChecked(false);\n    }\n    if (chunk->prev_inuse) {\n        this->ui->rbPI->setChecked(true);\n    } else {\n        this->ui->rbPI->setChecked(false);\n    }\n    if (chunk->non_main_arena) {\n        this->ui->rbNMA->setChecked(true);\n    } else {\n        this->ui->rbNMA->setChecked(false);\n    }\n\n    free(chunk);\n}\n\nvoid GlibcHeapInfoDialog::saveChunkInfo()\n{\n    QMessageBox msgBox(this);\n    msgBox.setText(\"Do you want to overwrite chunk metadata?\");\n    msgBox.setInformativeText(\n            \"Any field which cannot be converted to a valid integer will be saved as zero\");\n    msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);\n    msgBox.setDefaultButton(QMessageBox::Save);\n\n    int ret = msgBox.exec();\n    switch (ret) {\n    case QMessageBox::Save:\n        // Save was clicked\n        RzHeapChunkSimple chunkSimple;\n        chunkSimple.size = Core()->math(ui->sizeEdit->text());\n        chunkSimple.fd = Core()->math(ui->fdEdit->text());\n        chunkSimple.bk = Core()->math(ui->bkEdit->text());\n        chunkSimple.fd_nextsize = Core()->math(ui->fdnsEdit->text());\n        chunkSimple.bk_nextsize = Core()->math(ui->bknsEdit->text());\n        chunkSimple.addr = offset;\n        if (ui->rbIM->isChecked()) {\n            chunkSimple.is_mmapped = true;\n        } else {\n            chunkSimple.is_mmapped = false;\n        }\n        if (ui->rbNMA->isChecked()) {\n            chunkSimple.non_main_arena = true;\n        } else {\n            chunkSimple.non_main_arena = false;\n        }\n        if (ui->rbPI->isChecked()) {\n            chunkSimple.prev_inuse = true;\n        } else {\n            chunkSimple.prev_inuse = false;\n        }\n        if (Core()->writeHeapChunk(&chunkSimple)) {\n            updateFields();\n            QMessageBox::information(this, tr(\"Chunk saved\"),\n                                     tr(\"Chunk header successfully overwritten\"));\n        } else {\n            QMessageBox::information(this, tr(\"Chunk not saved\"),\n                                     tr(\"Chunk header not successfully overwritten\"));\n        }\n        break;\n    case QMessageBox::Cancel:\n        // Cancel was clicked\n        break;\n    }\n}"
  },
  {
    "path": "src/dialogs/GlibcHeapInfoDialog.h",
    "content": "#ifndef HEAPINFODIALOG_H\n#define HEAPINFODIALOG_H\n\n#include <QDialog>\n#include \"core/Cutter.h\"\n\nnamespace Ui {\nclass GlibcHeapInfoDialog;\n}\n\nclass GlibcHeapInfoDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit GlibcHeapInfoDialog(RVA offset, QString status, QWidget *parent = nullptr);\n    ~GlibcHeapInfoDialog();\n\nprivate slots:\n    void saveChunkInfo();\n\nprivate:\n    Ui::GlibcHeapInfoDialog *ui;\n    void updateFields();\n    RVA offset;\n    QString status;\n};\n\n#endif // HEAPINFODIALOG_H\n"
  },
  {
    "path": "src/dialogs/GlibcHeapInfoDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GlibcHeapInfoDialog</class>\n <widget class=\"QDialog\" name=\"GlibcHeapInfoDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>453</width>\n    <height>263</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Base</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"baseEdit\">\n       <property name=\"toolTip\">\n        <string>Base address of the chunk</string>\n       </property>\n       <property name=\"readOnly\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_4\">\n       <property name=\"text\">\n        <string>Size</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"sizeEdit\">\n       <property name=\"toolTip\">\n        <string>Size of the heap chunk including metadata</string>\n       </property>\n       <property name=\"readOnly\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>Fd</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"fdEdit\">\n       <property name=\"toolTip\">\n        <string>Link to next free chunk in bin's linked list</string>\n       </property>\n       <property name=\"readOnly\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_5\">\n       <property name=\"text\">\n        <string>Bk</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"bkEdit\">\n       <property name=\"toolTip\">\n        <string>Link to previous free chunk in bin's linked list</string>\n       </property>\n       <property name=\"readOnly\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"fdnsEdit\">\n       <property name=\"toolTip\">\n        <string>Link to next larger free chunk (only for large chunks)</string>\n       </property>\n       <property name=\"readOnly\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Fd-nextsize&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_6\">\n       <property name=\"text\">\n        <string>Bk-nextsize</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"bknsEdit\">\n       <property name=\"toolTip\">\n        <string>Link to next smaller free chunk (for large chunks)</string>\n       </property>\n       <property name=\"readOnly\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"prevSizeEdit\">\n       <property name=\"toolTip\">\n        <string>Size of previous chunk (if free)</string>\n       </property>\n       <property name=\"readOnly\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_7\">\n       <property name=\"text\">\n        <string>PrevSize</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QRadioButton\" name=\"rbNMA\">\n       <property name=\"toolTip\">\n        <string>If the chunk was obtained from a non-main arena</string>\n       </property>\n       <property name=\"text\">\n        <string>NON_MAIN_ARENA</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>true</bool>\n       </property>\n       <property name=\"autoExclusive\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QRadioButton\" name=\"rbIM\">\n       <property name=\"toolTip\">\n        <string>The chunk was obtained with mmap()</string>\n       </property>\n       <property name=\"text\">\n        <string>IS_MMAPED</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>true</bool>\n       </property>\n       <property name=\"autoExclusive\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QRadioButton\" name=\"rbPI\">\n       <property name=\"toolTip\">\n        <string>Previous adjacent chunk is in use</string>\n       </property>\n       <property name=\"text\">\n        <string>PREV_INUSE</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>true</bool>\n       </property>\n       <property name=\"autoExclusive\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"saveButton\">\n     <property name=\"text\">\n      <string>Save</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/GlobalVariableDialog.cpp",
    "content": "#include \"GlobalVariableDialog.h\"\n#include \"ui_GlobalVariableDialog.h\"\n\n#include <QIntValidator>\n#include \"core/Cutter.h\"\n\nGlobalVariableDialog::GlobalVariableDialog(RVA offset, QWidget *parent)\n    : QDialog(parent),\n      ui(new Ui::GlobalVariableDialog),\n      offset(offset),\n      globalVariableName(\"\"),\n      globalVariableOffset(RVA_INVALID)\n{\n    // Setup UI\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n    RzCoreLocked core = Core()->lock();\n    RzAnalysisVarGlobal *globalVariable =\n            rz_analysis_var_global_get_byaddr_at(core->analysis, offset);\n    if (globalVariable) {\n        globalVariableName = QString(globalVariable->name);\n        globalVariableOffset = globalVariable->addr;\n    }\n\n    if (globalVariable) {\n        ui->nameEdit->setText(globalVariable->name);\n        QString globalVarType = Core()->getGlobalVariableType(globalVariable->name);\n        ui->typeEdit->setText(globalVarType);\n        ui->labelAction->setText(tr(\"Edit global variable at %1\").arg(RzAddressString(offset)));\n    } else {\n        ui->labelAction->setText(tr(\"Add global variable at %1\").arg(RzAddressString(offset)));\n    }\n\n    // Connect slots\n    connect(ui->buttonBox, &QDialogButtonBox::accepted, this,\n            &GlobalVariableDialog::buttonBoxAccepted);\n    connect(ui->buttonBox, &QDialogButtonBox::rejected, this,\n            &GlobalVariableDialog::buttonBoxRejected);\n}\n\nGlobalVariableDialog::~GlobalVariableDialog() {}\n\nvoid GlobalVariableDialog::buttonBoxAccepted()\n{\n    QString name = ui->nameEdit->text();\n    QString typ = ui->typeEdit->text();\n\n    if (name.isEmpty()) {\n        if (globalVariableOffset != RVA_INVALID) {\n            // Empty name and global variable exists -> delete the global variable\n            Core()->delGlobalVariable(globalVariableOffset);\n        } else {\n            // GlobalVariable was not existing and we gave an empty name, do nothing\n        }\n    } else {\n        if (globalVariableOffset != RVA_INVALID) {\n            // Name provided and global variable exists -> rename the global variable\n            Core()->modifyGlobalVariable(globalVariableOffset, name, typ);\n        } else {\n            // Name provided and global variable does not exist -> create the global variable\n            Core()->addGlobalVariable(offset, name, typ);\n        }\n    }\n    close();\n    this->setResult(QDialog::Accepted);\n}\n\nvoid GlobalVariableDialog::buttonBoxRejected()\n{\n    close();\n    this->setResult(QDialog::Rejected);\n}\n"
  },
  {
    "path": "src/dialogs/GlobalVariableDialog.h",
    "content": "#ifndef GLOBALVARIABLEDIALOG_H\n#define GLOBALVARIABLEDIALOG_H\n\n#include <QDialog>\n#include <memory>\n#include \"core/CutterCommon.h\"\n\nnamespace Ui {\nclass GlobalVariableDialog;\n}\n\nclass GlobalVariableDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit GlobalVariableDialog(RVA offset, QWidget *parent = nullptr);\n    ~GlobalVariableDialog();\n\nprivate slots:\n    void buttonBoxAccepted();\n    void buttonBoxRejected();\n\nprivate:\n    std::unique_ptr<Ui::GlobalVariableDialog> ui;\n    RVA offset;\n    QString globalVariableName;\n    RVA globalVariableOffset;\n    QString typ;\n};\n\n#endif // GLOBALVARIABLEDIALOG_H\n"
  },
  {
    "path": "src/dialogs/GlobalVariableDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GlobalVariableDialog</class>\n <widget class=\"QDialog\" name=\"GlobalVariableDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>452</width>\n    <height>121</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Add Global Variable</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"labelAction\">\n     <property name=\"text\">\n      <string>Add global variable at</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <property name=\"horizontalSpacing\">\n      <number>12</number>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"labelName\">\n       <property name=\"font\">\n        <font>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>Name:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"nameEdit\">\n       <property name=\"frame\">\n        <bool>false</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string notr=\"true\"/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"typeEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>100</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string>int</string>\n       </property>\n       <property name=\"frame\">\n        <bool>false</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string notr=\"true\"/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"labelType\">\n       <property name=\"font\">\n        <font>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>Type:</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/HexdumpRangeDialog.cpp",
    "content": "#include \"HexdumpRangeDialog.h\"\n#include \"ui_HexdumpRangeDialog.h\"\n\n#include <QRegularExpressionValidator>\n#include <QPushButton>\n#include <cstdint>\n#include \"core/Cutter.h\"\n\nHexdumpRangeDialog::HexdumpRangeDialog(QWidget *parent, bool allowEmpty)\n    : QDialog(parent), ui(new Ui::HexdumpRangeDialog), allowEmpty(allowEmpty)\n{\n    ui->setupUi(this);\n    QRegularExpressionValidator *v =\n            new QRegularExpressionValidator(QRegularExpression(\"(?:0[xX])?[0-9a-fA-F]+\"), this);\n    ui->lengthLineEdit->setValidator(v);\n    ui->startAddressLineEdit->setValidator(v);\n    ui->endAddressLineEdit->setValidator(v);\n\n    // subscribe to a text change slot\n    connect(ui->startAddressLineEdit, &QLineEdit::textEdited, this,\n            &HexdumpRangeDialog::textEdited);\n    connect(ui->endAddressLineEdit, &QLineEdit::textEdited, this, &HexdumpRangeDialog::textEdited);\n    connect(ui->lengthLineEdit, &QLineEdit::textEdited, this, &HexdumpRangeDialog::textEdited);\n    connect(ui->endAddressRadioButton, &QRadioButton::clicked, this,\n            &HexdumpRangeDialog::on_radioButtonClicked);\n    connect(ui->lengthRadioButton, &QRadioButton::clicked, this,\n            &HexdumpRangeDialog::on_radioButtonClicked);\n}\n\nHexdumpRangeDialog::~HexdumpRangeDialog()\n{\n    delete ui;\n}\n\nbool HexdumpRangeDialog::empty()\n{\n    return emptyRange;\n}\n\nunsigned long long HexdumpRangeDialog::getStartAddress() const\n{\n    return startAddress;\n}\n\nunsigned long long HexdumpRangeDialog::getEndAddress() const\n{\n    return endAddress;\n}\n\nbool HexdumpRangeDialog::getEndAddressRadioButtonChecked() const\n{\n    return ui->endAddressRadioButton->isChecked();\n}\n\nbool HexdumpRangeDialog::getLengthRadioButtonChecked() const\n{\n    return ui->lengthRadioButton->isChecked();\n}\n\nvoid HexdumpRangeDialog::setStartAddress(ut64 start)\n{\n    ui->startAddressLineEdit->setText(QString(\"0x%1\").arg(start, 0, 16));\n}\n\nvoid HexdumpRangeDialog::open(ut64 start)\n{\n    setStartAddress(start);\n    setModal(false);\n    show();\n    activateWindow();\n    raise();\n}\n\nbool HexdumpRangeDialog::validate()\n{\n    bool warningVisibile = false;\n    startAddress = Core()->math(ui->startAddressLineEdit->text());\n    endAddress = 0;\n    ut64 length = 0;\n    emptyRange = true;\n    if (ui->endAddressRadioButton->isChecked()) {\n        endAddress = Core()->math(ui->endAddressLineEdit->text());\n        if (endAddress > startAddress) {\n            length = endAddress - startAddress;\n            ui->lengthLineEdit->setText(QString(\"0x%1\").arg(length, 0, 16));\n            this->endAddress = endAddress - 1;\n            emptyRange = false;\n        } else if (endAddress == startAddress) {\n            ui->lengthLineEdit->setText(\"0\");\n            return allowEmpty;\n        } else {\n            ui->lengthLineEdit->setText(\"Invalid\");\n            return false;\n        }\n    } else {\n        // we edited the length, so update the end address to be start address + length\n        length = Core()->math(ui->lengthLineEdit->text());\n        if (length == 0) {\n            ui->endAddressLineEdit->setText(\"Empty\");\n            return allowEmpty;\n        } else if (UINT64_MAX - startAddress < length - 1) {\n            ui->endAddressLineEdit->setText(\"Invalid\");\n            return false;\n        } else {\n            endAddress = startAddress + length - 1;\n            emptyRange = false;\n            if (endAddress == UINT64_MAX) {\n                ui->endAddressLineEdit->setText(QString(\"2^64\"));\n            } else {\n                ui->endAddressLineEdit->setText(QString(\"0x%1\").arg(endAddress + 1, 0, 16));\n            }\n        }\n    }\n\n    // Warn the user for potentially heavy operation\n    if (length > 0x25000) {\n        warningVisibile = true;\n    }\n\n    ui->selectionWarningLabel->setVisible(warningVisibile);\n    return true;\n}\n\nvoid HexdumpRangeDialog::textEdited()\n{\n    bool valid = validate();\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);\n}\n\nvoid HexdumpRangeDialog::on_radioButtonClicked(bool checked)\n{\n    if (sender() == ui->endAddressRadioButton && checked == true) {\n        ui->lengthLineEdit->setEnabled(false);\n        ui->endAddressLineEdit->setEnabled(true);\n        ui->endAddressLineEdit->setFocus();\n    } else if (sender() == ui->lengthRadioButton && checked == true) {\n        ui->lengthLineEdit->setEnabled(true);\n        ui->endAddressLineEdit->setEnabled(false);\n        ui->lengthLineEdit->setFocus();\n    }\n}\n"
  },
  {
    "path": "src/dialogs/HexdumpRangeDialog.h",
    "content": "#ifndef HEXDUMPRANGEDIALOG_H\n#define HEXDUMPRANGEDIALOG_H\n\n#include \"core/CutterCommon.h\"\n#include <QDialog>\n\nnamespace Ui {\nclass HexdumpRangeDialog;\n}\n\nclass HexdumpRangeDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit HexdumpRangeDialog(QWidget *parent = nullptr, bool allowEmpty = false);\n    ~HexdumpRangeDialog();\n    bool empty();\n    ut64 getStartAddress() const;\n    ut64 getEndAddress() const;\n\n    void setStartAddress(ut64 start);\n    void open(ut64 start);\n\npublic slots:\n    void textEdited();\n\nprivate:\n    bool getEndAddressRadioButtonChecked() const;\n    bool getLengthRadioButtonChecked() const;\n    bool validate();\n\n    Ui::HexdumpRangeDialog *ui;\n    bool emptyRange = true;\n    ut64 startAddress;\n    ut64 endAddress;\n    bool allowEmpty = false;\n\nprivate slots:\n    void on_radioButtonClicked(bool checked);\n};\n\n#endif // HEXDUMPRANGEDIALOG_H\n"
  },
  {
    "path": "src/dialogs/HexdumpRangeDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>HexdumpRangeDialog</class>\n <widget class=\"QDialog\" name=\"HexdumpRangeDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>455</width>\n    <height>201</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Select Block</string>\n  </property>\n  <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n   <property name=\"geometry\">\n    <rect>\n     <x>10</x>\n     <y>160</y>\n     <width>341</width>\n     <height>32</height>\n    </rect>\n   </property>\n   <property name=\"orientation\">\n    <enum>Qt::Horizontal</enum>\n   </property>\n   <property name=\"standardButtons\">\n    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n   </property>\n  </widget>\n  <widget class=\"QWidget\" name=\"gridLayoutWidget\">\n   <property name=\"geometry\">\n    <rect>\n     <x>29</x>\n     <y>19</y>\n     <width>401</width>\n     <height>121</height>\n    </rect>\n   </property>\n   <layout class=\"QGridLayout\" name=\"gridLayout\">\n    <item row=\"2\" column=\"0\">\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n      <item>\n       <widget class=\"QRadioButton\" name=\"endAddressRadioButton\">\n        <property name=\"toolTip\">\n         <string>Exclusive end address</string>\n        </property>\n        <property name=\"text\">\n         <string>End Address:</string>\n        </property>\n        <property name=\"checked\">\n         <bool>true</bool>\n        </property>\n        <attribute name=\"buttonGroup\">\n         <string notr=\"true\">buttonGroup</string>\n        </attribute>\n       </widget>\n      </item>\n     </layout>\n    </item>\n    <item row=\"0\" column=\"0\">\n     <widget class=\"QLabel\" name=\"startAddressLabel\">\n      <property name=\"text\">\n       <string>Start Address:</string>\n      </property>\n     </widget>\n    </item>\n    <item row=\"0\" column=\"1\">\n     <widget class=\"QLineEdit\" name=\"startAddressLineEdit\">\n      <property name=\"maxLength\">\n       <number>18</number>\n      </property>\n     </widget>\n    </item>\n    <item row=\"2\" column=\"1\">\n     <widget class=\"QLineEdit\" name=\"endAddressLineEdit\">\n      <property name=\"toolTip\">\n       <string>Exclusive end address</string>\n      </property>\n      <property name=\"maxLength\">\n       <number>18</number>\n      </property>\n     </widget>\n    </item>\n    <item row=\"3\" column=\"1\">\n     <widget class=\"QLineEdit\" name=\"lengthLineEdit\">\n      <property name=\"enabled\">\n       <bool>false</bool>\n      </property>\n      <property name=\"maxLength\">\n       <number>10</number>\n      </property>\n     </widget>\n    </item>\n    <item row=\"3\" column=\"0\">\n     <widget class=\"QRadioButton\" name=\"lengthRadioButton\">\n      <property name=\"text\">\n       <string>Length:</string>\n      </property>\n      <attribute name=\"buttonGroup\">\n       <string notr=\"true\">buttonGroup</string>\n      </attribute>\n     </widget>\n    </item>\n    <item row=\"4\" column=\"1\">\n     <widget class=\"QLabel\" name=\"selectionWarningLabel\">\n      <property name=\"enabled\">\n       <bool>true</bool>\n      </property>\n      <property name=\"visible\">\n       <bool>false</bool>\n      </property>\n      <property name=\"text\">\n       <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff8585;&quot;&gt;Big selection might cause a delay&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n      </property>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>HexdumpRangeDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>HexdumpRangeDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n <buttongroups>\n  <buttongroup name=\"buttonGroup\"/>\n </buttongroups>\n</ui>\n"
  },
  {
    "path": "src/dialogs/IncrementDecrementDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>IncrementDecrementDialog</class>\n <widget class=\"QDialog\" name=\"IncrementDecrementDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>241</width>\n    <height>258</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Increment/Decrement</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"label\">\n         <property name=\"text\">\n          <string>Interpret as</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QComboBox\" name=\"nBytesCB\"/>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n       <item>\n        <widget class=\"QLabel\" name=\"valueLabel\">\n         <property name=\"text\">\n          <string>Value:</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLineEdit\" name=\"valueLE\">\n         <property name=\"inputMask\">\n          <string notr=\"true\"/>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <widget class=\"QRadioButton\" name=\"incrementRB\">\n       <property name=\"text\">\n        <string>Increment</string>\n       </property>\n       <attribute name=\"buttonGroup\">\n        <string notr=\"true\">buttonGroup</string>\n       </attribute>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QRadioButton\" name=\"decrementRB\">\n       <property name=\"text\">\n        <string>Decrement</string>\n       </property>\n       <attribute name=\"buttonGroup\">\n        <string notr=\"true\">buttonGroup</string>\n       </attribute>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>IncrementDecrementDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>IncrementDecrementDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n <buttongroups>\n  <buttongroup name=\"buttonGroup\"/>\n </buttongroups>\n</ui>\n"
  },
  {
    "path": "src/dialogs/InitialOptionsDialog.cpp",
    "content": "#include \"common/AsyncTask.h\"\n#include \"InitialOptionsDialog.h\"\n#include \"ui_InitialOptionsDialog.h\"\n\n#include \"core/MainWindow.h\"\n#include \"dialogs/NewFileDialog.h\"\n#include \"dialogs/AsyncTaskDialog.h\"\n#include \"common/Helpers.h\"\n\n#include <QSettings>\n#include <QFileInfo>\n#include <QFileDialog>\n#include <QCloseEvent>\n\n#include \"core/Cutter.h\"\n#include \"common/AnalysisTask.h\"\n#include \"CutterApplication.h\"\n\nQString CommandDescription::translatedDescription() const\n{\n    return QCoreApplication::translate(\"InitialOptionsDialog\", description);\n}\n\nInitialOptionsDialog::InitialOptionsDialog(MainWindow *main)\n    : QDialog(nullptr), // parent must not be main\n      ui(new Ui::InitialOptionsDialog),\n      main(main),\n      core(Core())\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n    ui->logoSvgWidget->load(Config()->getLogoFile());\n\n    // Fill the plugins combo\n    asmPlugins = core->getRAsmPluginDescriptions();\n    for (const auto &plugin : asmPlugins) {\n        ui->archComboBox->addItem(plugin.name, plugin.name);\n    }\n\n    setTooltipWithConfigHelp(ui->archComboBox, \"asm.arch\");\n\n    // cpu combo box\n    ui->cpuComboBox->lineEdit()->setPlaceholderText(tr(\"Auto\"));\n    setTooltipWithConfigHelp(ui->cpuComboBox, \"asm.cpu\");\n\n    updateCPUComboBox();\n\n    // os combo box\n    for (const auto &plugin : Core()->getConfigOptions(\"asm.os\")) {\n        ui->kernelComboBox->addItem(plugin, plugin);\n    }\n\n    setTooltipWithConfigHelp(ui->kernelComboBox, \"asm.os\");\n    setTooltipWithConfigHelp(ui->bitsComboBox, \"asm.bits\");\n\n    for (const auto &plugin : core->getBinPluginDescriptions(true, false)) {\n        ui->formatComboBox->addItem(plugin.name, QVariant::fromValue(plugin));\n    }\n\n    analysisCommands = {\n        { { \"aa\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze all symbols\") },\n          new QCheckBox(),\n          true },\n        { { \"aar\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze instructions for references\") },\n          new QCheckBox(),\n          true },\n        { { \"aac\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze function calls\") },\n          new QCheckBox(),\n          true },\n        { { \"aab\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze all basic blocks\") },\n          new QCheckBox(),\n          false },\n        { { \"aao\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze all objc references\") },\n          new QCheckBox(),\n          false },\n        { { \"avrr\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Recover class information from RTTI\") },\n          new QCheckBox(),\n          false },\n        { { \"aan\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Autoname functions based on context\") },\n          new QCheckBox(),\n          false },\n        { { \"aae\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Emulate code to find computed references\") },\n          new QCheckBox(),\n          false },\n        { { \"aafr\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze all consecutive functions\") },\n          new QCheckBox(),\n          false },\n        { { \"aaft\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Type and Argument matching analysis\") },\n          new QCheckBox(),\n          false },\n        { { \"aaT\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze code after trap-sleds\") },\n          new QCheckBox(),\n          false },\n        { { \"aap\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze function preludes\") },\n          new QCheckBox(),\n          false },\n        { { \"e! analysis.jmp.tbl\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze jump tables in switch statements\") },\n          new QCheckBox(),\n          false },\n        { { \"e! analysis.pushret\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Analyze PUSH+RET as JMP\") },\n          new QCheckBox(),\n          false },\n        { { \"e! analysis.hasnext\",\n            QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Continue analysis after each function\") },\n          new QCheckBox(),\n          false }\n    };\n\n    // Per each checkbox, set a tooltip desccribing it\n    AnalysisCommands item;\n    foreach (item, analysisCommands) {\n        item.checkbox->setText(item.commandDesc.translatedDescription());\n        item.checkbox->setToolTip(item.commandDesc.command);\n        item.checkbox->setChecked(item.checked);\n        ui->verticalLayout_7->addWidget(item.checkbox);\n    }\n\n    ui->hideFrame->setVisible(false);\n    ui->analysisoptionsFrame->setVisible(false);\n    ui->advancedAnlysisLine->setVisible(false);\n\n    updatePDBLayout();\n\n    connect(ui->pdbCheckBox, &QCheckBox::stateChanged, this,\n            &InitialOptionsDialog::updatePDBLayout);\n\n    updateScriptLayout();\n\n    connect(ui->scriptCheckBox, &QCheckBox::stateChanged, this,\n            &InitialOptionsDialog::updateScriptLayout);\n\n    connect(ui->cancelButton, &QPushButton::clicked, this, &InitialOptionsDialog::reject);\n\n    ui->programLineEdit->setText(main->getFilename());\n}\n\nInitialOptionsDialog::~InitialOptionsDialog() {}\n\nvoid InitialOptionsDialog::updateCPUComboBox()\n{\n    QString currentText = ui->cpuComboBox->lineEdit()->text();\n    ui->cpuComboBox->clear();\n\n    QString arch = getSelectedArch();\n    QStringList cpus;\n    if (!arch.isEmpty()) {\n        auto pluginDescr = std::find_if(\n                asmPlugins.begin(), asmPlugins.end(),\n                [&](const RzAsmPluginDescription &plugin) { return plugin.name == arch; });\n        if (pluginDescr != asmPlugins.end()) {\n#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)\n            cpus = pluginDescr->cpus.split(\",\", Qt::SkipEmptyParts);\n#else\n            cpus = pluginDescr->cpus.split(\",\", QString::SkipEmptyParts);\n#endif\n        }\n    }\n\n    ui->cpuComboBox->addItem(\"\");\n    ui->cpuComboBox->addItems(cpus);\n\n    ui->cpuComboBox->lineEdit()->setText(currentText);\n}\n\nQList<QString> InitialOptionsDialog::getAnalysisCommands(const InitialOptions &options)\n{\n    QList<QString> commands;\n    for (auto &commandDesc : options.analysisCmd) {\n        commands << commandDesc.command;\n    }\n    return commands;\n}\n\nvoid InitialOptionsDialog::loadOptions(const InitialOptions &options)\n{\n    if (options.analysisCmd.isEmpty()) {\n        analysisLevel = 0;\n    } else if (options.analysisCmd.first().command == \"aaa\") {\n        analysisLevel = 1;\n    } else if (options.analysisCmd.first().command == \"aaaa\") {\n        analysisLevel = 2;\n    } else {\n        analysisLevel = 3;\n        AnalysisCommands item;\n        QList<QString> commands = getAnalysisCommands(options);\n        foreach (item, analysisCommands) {\n            qInfo() << item.commandDesc.command;\n            item.checkbox->setChecked(commands.contains(item.commandDesc.command));\n        }\n    }\n\n    if (!options.script.isEmpty()) {\n        ui->scriptCheckBox->setChecked(true);\n        ui->scriptLineEdit->setText(options.script);\n        analysisLevel = 0;\n    } else {\n        ui->scriptCheckBox->setChecked(false);\n        ui->scriptLineEdit->setText(\"\");\n    }\n\n    ui->analysisSlider->setValue(analysisLevel);\n\n    shellcode = options.shellcode;\n\n    if (!options.forceBinPlugin.isEmpty()) {\n        ui->formatComboBox->setCurrentText(options.forceBinPlugin);\n    } else {\n        ui->formatComboBox->setCurrentIndex(0);\n    }\n\n    if (options.binLoadAddr != RVA_INVALID) {\n        ui->entry_loadOffset->setText(RzAddressString(options.binLoadAddr));\n    }\n\n    if (options.mapAddr != RVA_INVALID) {\n        ui->entry_mapOffset->setText(RzAddressString(options.mapAddr));\n    }\n\n    ui->vaCheckBox->setChecked(options.useVA);\n    ui->writeCheckBox->setChecked(options.writeEnabled);\n\n    if (!options.arch.isNull() && !options.arch.isEmpty()) {\n        ui->archComboBox->setCurrentText(options.arch);\n    }\n\n    if (!options.cpu.isNull() && !options.cpu.isEmpty()) {\n        ui->cpuComboBox->setCurrentText(options.cpu);\n    }\n\n    if (options.bits > 0) {\n        ui->bitsComboBox->setCurrentText(QString::asprintf(\"%d\", options.bits));\n    }\n\n    if (!options.os.isNull() && !options.os.isEmpty()) {\n        ui->kernelComboBox->setCurrentText(options.os);\n    }\n\n    if (!options.forceBinPlugin.isNull() && !options.forceBinPlugin.isEmpty()) {\n        ui->formatComboBox->setCurrentText(options.forceBinPlugin);\n    }\n\n    if (!options.loadBinInfo) {\n        ui->binCheckBox->setChecked(false);\n    }\n\n    ui->writeCheckBox->setChecked(options.writeEnabled);\n\n    switch (options.endian) {\n    case InitialOptions::Endianness::Little:\n        ui->endiannessComboBox->setCurrentIndex(1);\n        break;\n    case InitialOptions::Endianness::Big:\n        ui->endiannessComboBox->setCurrentIndex(2);\n        break;\n    default:\n        break;\n    }\n\n    ui->demangleCheckBox->setChecked(options.demangle);\n\n    if (!options.pdbFile.isNull() && !options.pdbFile.isEmpty()) {\n        ui->pdbCheckBox->setChecked(true);\n        ui->pdbLineEdit->setText(options.pdbFile);\n    }\n}\n\nvoid InitialOptionsDialog::setTooltipWithConfigHelp(QWidget *w, const char *config)\n{\n    w->setToolTip(QString(\"%1 (%2)\").arg(core->getConfigDescription(config)).arg(config));\n}\n\nQString InitialOptionsDialog::getSelectedArch() const\n{\n    QVariant archValue = ui->archComboBox->currentData();\n    return archValue.isValid() ? archValue.toString() : nullptr;\n}\n\nQString InitialOptionsDialog::getSelectedCPU() const\n{\n    QString cpu = ui->cpuComboBox->currentText();\n    if (cpu.isNull() || cpu.isEmpty()) {\n        return nullptr;\n    }\n    return cpu;\n}\n\nint InitialOptionsDialog::getSelectedBits() const\n{\n    QString sel_bits = ui->bitsComboBox->currentText();\n    if (sel_bits != \"Auto\") { // TODO: #3187 using potentially trtanslated text for logic\n        return sel_bits.toInt();\n    }\n\n    return 0;\n}\n\nInitialOptions::Endianness InitialOptionsDialog::getSelectedEndianness() const\n{\n    switch (ui->endiannessComboBox->currentIndex()) {\n    case 1:\n        return InitialOptions::Endianness::Little;\n    case 2:\n        return InitialOptions::Endianness::Big;\n    default:\n        return InitialOptions::Endianness::Auto;\n    }\n}\n\nQString InitialOptionsDialog::getSelectedOS() const\n{\n    QVariant os = ui->kernelComboBox->currentData();\n    return os.isValid() ? os.toString() : nullptr;\n}\n\nQList<CommandDescription> InitialOptionsDialog::getSelectedAdvancedAnalCmds() const\n{\n    QList<CommandDescription> advanced = QList<CommandDescription>();\n    if (ui->analysisSlider->value() == 3) {\n        AnalysisCommands item;\n        foreach (item, analysisCommands) {\n            if (item.checkbox->isChecked()) {\n                advanced << item.commandDesc;\n            }\n        }\n    }\n    return advanced;\n}\n\nvoid InitialOptionsDialog::setupAndStartAnalysis()\n{\n    InitialOptions options;\n\n    options.filename = main->getFilename();\n    if (!options.filename.isEmpty()) {\n        main->setWindowTitle(\"Cutter – \" + options.filename);\n    }\n    options.shellcode = this->shellcode;\n\n    // Where the bin header is located in the file (-B)\n    if (ui->entry_loadOffset->text().length() > 0) {\n        options.binLoadAddr = Core()->math(ui->entry_loadOffset->text());\n    }\n\n    options.mapAddr =\n            Core()->math(ui->entry_mapOffset->text()); // Where to map the file once loaded (-m)\n    options.arch = getSelectedArch();\n    options.cpu = getSelectedCPU();\n    options.bits = getSelectedBits();\n    options.os = getSelectedOS();\n    options.writeEnabled = ui->writeCheckBox->isChecked();\n    options.loadBinInfo = !ui->binCheckBox->isChecked();\n    options.useVA = ui->vaCheckBox->isChecked();\n    QVariant forceBinPluginData = ui->formatComboBox->currentData();\n    if (!forceBinPluginData.isNull()) {\n        RzBinPluginDescription pluginDesc = forceBinPluginData.value<RzBinPluginDescription>();\n        options.forceBinPlugin = pluginDesc.name;\n    }\n    options.demangle = ui->demangleCheckBox->isChecked();\n    if (ui->pdbCheckBox->isChecked()) {\n        options.pdbFile = ui->pdbLineEdit->text();\n    }\n    if (ui->scriptCheckBox->isChecked()) {\n        options.script = ui->scriptLineEdit->text();\n    }\n\n    options.endian = getSelectedEndianness();\n\n    int level = ui->analysisSlider->value();\n    switch (level) {\n    case 1:\n        options.analysisCmd = { { \"aaa\",\n                                  QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Auto analysis\") } };\n        break;\n    case 2:\n        options.analysisCmd = {\n            { \"aaaa\", QT_TRANSLATE_NOOP(\"InitialOptionsDialog\", \"Auto analysis (experimental)\") }\n        };\n        break;\n    case 3:\n        options.analysisCmd = getSelectedAdvancedAnalCmds();\n        break;\n    default:\n        options.analysisCmd = {};\n        break;\n    }\n\n    AnalysisTask *analysisTask = new AnalysisTask();\n    analysisTask->setOptions(options);\n\n    MainWindow *main = this->main;\n    connect(analysisTask, &AnalysisTask::openFileFailed, main, &MainWindow::openNewFileFailed);\n    connect(analysisTask, &AsyncTask::finished, main, [analysisTask, main]() {\n        if (analysisTask->getOpenFileFailed()) {\n            return;\n        }\n        main->finalizeOpen();\n    });\n\n    AsyncTask::Ptr analysisTaskPtr(analysisTask);\n\n    AsyncTaskDialog *taskDialog = new AsyncTaskDialog(analysisTaskPtr);\n    taskDialog->setInterruptOnClose(true);\n    taskDialog->setAttribute(Qt::WA_DeleteOnClose);\n    taskDialog->show();\n\n    Core()->getAsyncTaskManager()->start(analysisTaskPtr);\n\n    done(0);\n\n    static_cast<CutterApplication *>(qApp)->setInitialOptions(options);\n}\n\nvoid InitialOptionsDialog::on_okButton_clicked()\n{\n    ui->okButton->setEnabled(false);\n    setupAndStartAnalysis();\n}\n\nvoid InitialOptionsDialog::closeEvent(QCloseEvent *event)\n{\n    event->accept();\n}\n\nQString InitialOptionsDialog::analysisDescription(int level)\n{\n    // TODO: replace this with meaningful descriptions\n    switch (level) {\n    case 0:\n        return tr(\"No analysis\");\n    case 1:\n        return tr(\"Auto-Analysis (aaa)\");\n    case 2:\n        return tr(\"Auto-Analysis Experimental (aaaa)\");\n    case 3:\n        return tr(\"Advanced\");\n    default:\n        return tr(\"Unknown\");\n    }\n}\n\nvoid InitialOptionsDialog::on_analysisSlider_valueChanged(int value)\n{\n    ui->analDescription->setText(tr(\"Level\") + QString(\": %1\").arg(analysisDescription(value)));\n    if (value == 0) {\n        ui->analysisCheckBox->setChecked(false);\n        ui->analysisCheckBox->setText(tr(\"Analysis: Disabled\"));\n    } else {\n        ui->analysisCheckBox->setChecked(true);\n        ui->analysisCheckBox->setText(tr(\"Analysis: Enabled\"));\n        if (value == 3) {\n            ui->analysisoptionsFrame->setVisible(true);\n            ui->advancedAnlysisLine->setVisible(true);\n        } else {\n            ui->analysisoptionsFrame->setVisible(false);\n            ui->advancedAnlysisLine->setVisible(false);\n        }\n    }\n}\n\nvoid InitialOptionsDialog::on_AdvOptButton_clicked()\n{\n    if (ui->AdvOptButton->isChecked()) {\n        ui->hideFrame->setVisible(true);\n        ui->AdvOptButton->setArrowType(Qt::DownArrow);\n    } else {\n        ui->hideFrame->setVisible(false);\n        ui->AdvOptButton->setArrowType(Qt::RightArrow);\n    }\n}\n\nvoid InitialOptionsDialog::on_analysisCheckBox_clicked(bool checked)\n{\n    if (!checked) {\n        analysisLevel = ui->analysisSlider->value();\n    }\n    ui->analysisSlider->setValue(checked ? analysisLevel : 0);\n}\n\nvoid InitialOptionsDialog::on_archComboBox_currentIndexChanged(int)\n{\n    updateCPUComboBox();\n}\n\nvoid InitialOptionsDialog::updatePDBLayout()\n{\n    ui->pdbWidget->setEnabled(ui->pdbCheckBox->isChecked());\n}\n\nvoid InitialOptionsDialog::on_pdbSelectButton_clicked()\n{\n    QFileDialog dialog(this);\n    dialog.setWindowTitle(tr(\"Select PDB file\"));\n    dialog.setNameFilters({ tr(\"PDB file (*.pdb)\"), tr(\"All files (*)\") });\n\n    if (!dialog.exec()) {\n        return;\n    }\n\n    const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first());\n\n    if (!fileName.isEmpty()) {\n        ui->pdbLineEdit->setText(fileName);\n    }\n}\n\nvoid InitialOptionsDialog::updateScriptLayout()\n{\n    ui->scriptWidget->setEnabled(ui->scriptCheckBox->isChecked());\n}\n\nvoid InitialOptionsDialog::on_scriptSelectButton_clicked()\n{\n    QFileDialog dialog(this);\n    dialog.setWindowTitle(tr(\"Select Rizin script file\"));\n    dialog.setNameFilters({ tr(\"Script file (*.rz)\"), tr(\"All files (*)\") });\n\n    if (!dialog.exec()) {\n        return;\n    }\n\n    const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first());\n\n    if (!fileName.isEmpty()) {\n        ui->scriptLineEdit->setText(fileName);\n    }\n}\n\nvoid InitialOptionsDialog::reject()\n{\n    done(0);\n    main->displayNewFileDialog();\n}\n"
  },
  {
    "path": "src/dialogs/InitialOptionsDialog.h",
    "content": "#ifndef OPTIONSDIALOG_H\n#define OPTIONSDIALOG_H\n\n#include <QDialog>\n#include <QCheckBox>\n#include <memory>\n#include \"common/InitialOptions.h\"\n\nnamespace Ui {\nclass InitialOptionsDialog;\n}\n\nclass CutterCore;\nclass MainWindow;\nclass InitialOptionsDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit InitialOptionsDialog(MainWindow *main);\n    ~InitialOptionsDialog();\n\n    void setupAndStartAnalysis();\n\nprivate slots:\n    void on_okButton_clicked();\n    void on_analysisSlider_valueChanged(int value);\n    void on_AdvOptButton_clicked();\n    void on_analysisCheckBox_clicked(bool checked);\n    void on_archComboBox_currentIndexChanged(int index);\n    void on_pdbSelectButton_clicked();\n    void on_scriptSelectButton_clicked();\n\n    void updatePDBLayout();\n    void updateScriptLayout();\n\nprotected:\n    void closeEvent(QCloseEvent *event) override;\n\nprivate:\n    std::unique_ptr<Ui::InitialOptionsDialog> ui;\n\n    MainWindow *main;\n    CutterCore *core;\n\n    QString analysisDescription(int level);\n    QString shellcode;\n    int analysisLevel;\n    QList<RzAsmPluginDescription> asmPlugins;\n\n    void updateCPUComboBox();\n    struct AnalysisCommands\n    {\n        CommandDescription commandDesc;\n        QCheckBox *checkbox;\n        bool checked;\n    };\n    QList<AnalysisCommands> analysisCommands;\n\n    QList<QString> getAnalysisCommands(const InitialOptions &options);\n    QString getSelectedArch() const;\n    QString getSelectedCPU() const;\n    int getSelectedBits() const;\n    InitialOptions::Endianness getSelectedEndianness() const;\n    QString getSelectedOS() const;\n    QList<CommandDescription> getSelectedAdvancedAnalCmds() const;\n\n    /**\n     * @brief setTooltipWithConfigHelp is an helper function that add a tolltip to a widget with\n     * a description of a given Rizin eval config.\n     * @param w - a widget to which to add the tooltip\n     * @param config - name of a configuration variable such as \"asm.bits\".\n     */\n    void setTooltipWithConfigHelp(QWidget *w, const char *config);\n\npublic:\n    void loadOptions(const InitialOptions &options);\n\n    void reject() override;\n};\n\n#endif // OPTIONSDIALOG_H\n"
  },
  {
    "path": "src/dialogs/InitialOptionsDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>InitialOptionsDialog</class>\n <widget class=\"QDialog\" name=\"InitialOptionsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>598</width>\n    <height>524</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Maximum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>400</width>\n    <height>0</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Load Options</string>\n  </property>\n  <property name=\"autoFillBackground\">\n   <bool>false</bool>\n  </property>\n  <property name=\"styleSheet\">\n   <string notr=\"true\">QCheckBox {\n    margin-top: 5px;\n    margin-bottom: 5px;\n}</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_10\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_9\">\n     <property name=\"leftMargin\">\n      <number>8</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>8</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>8</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>8</number>\n     </property>\n     <item alignment=\"Qt::AlignHCenter\">\n      <widget class=\"QSvgWidget\" name=\"logoSvgWidget\" native=\"true\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>88</width>\n         <height>88</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>88</width>\n         <height>88</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <property name=\"topMargin\">\n      <number>5</number>\n     </property>\n     <item>\n      <widget class=\"QLabel\" name=\"programLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Program:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"programLineEdit\">\n       <property name=\"focusPolicy\">\n        <enum>Qt::ClickFocus</enum>\n       </property>\n       <property name=\"text\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"frame\">\n        <bool>false</bool>\n       </property>\n       <property name=\"readOnly\">\n        <bool>true</bool>\n       </property>\n       <property name=\"clearButtonEnabled\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"Line\" name=\"line_3\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QWidget\" name=\"optionsWidget\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n        <property name=\"spacing\">\n         <number>5</number>\n        </property>\n        <property name=\"sizeConstraint\">\n         <enum>QLayout::SetDefaultConstraint</enum>\n        </property>\n        <item>\n         <widget class=\"Line\" name=\"line\">\n          <property name=\"orientation\">\n           <enum>Qt::Vertical</enum>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QCheckBox\" name=\"analysisCheckBox\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"layoutDirection\">\n           <enum>Qt::LeftToRight</enum>\n          </property>\n          <property name=\"styleSheet\">\n           <string notr=\"true\"/>\n          </property>\n          <property name=\"text\">\n           <string>Analysis: Enabled</string>\n          </property>\n          <property name=\"checked\">\n           <bool>true</bool>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QLabel\" name=\"analDescription\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"text\">\n           <string>Level: </string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <layout class=\"QHBoxLayout\" name=\"sliderLabelLayout\">\n        <property name=\"spacing\">\n         <number>0</number>\n        </property>\n        <property name=\"sizeConstraint\">\n         <enum>QLayout::SetDefaultConstraint</enum>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QLabel\" name=\"sliderNone\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>55</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>None</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <spacer name=\"horizontalSpacer\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeType\">\n           <enum>QSizePolicy::Expanding</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>40</width>\n            <height>0</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item>\n         <widget class=\"QLabel\" name=\"sliderAuto\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>55</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>Auto</string>\n          </property>\n          <property name=\"alignment\">\n           <set>Qt::AlignCenter</set>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <spacer name=\"horizontalSpacer_4\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeType\">\n           <enum>QSizePolicy::Fixed</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>12</width>\n            <height>0</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item>\n         <spacer name=\"horizontalSpacer_5\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>40</width>\n            <height>0</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item>\n         <spacer name=\"horizontalSpacer_6\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeType\">\n           <enum>QSizePolicy::Fixed</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>12</width>\n            <height>0</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item>\n         <widget class=\"QLabel\" name=\"sliderAutoExp\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>55</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>Experimental</string>\n          </property>\n          <property name=\"alignment\">\n           <set>Qt::AlignCenter</set>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <spacer name=\"horizontalSpacer_2\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>40</width>\n            <height>0</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item>\n         <widget class=\"QLabel\" name=\"sliderAdvanced\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>55</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"layoutDirection\">\n           <enum>Qt::LeftToRight</enum>\n          </property>\n          <property name=\"text\">\n           <string>Advanced</string>\n          </property>\n          <property name=\"alignment\">\n           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <widget class=\"QSlider\" name=\"analysisSlider\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"minimumSize\">\n         <size>\n          <width>0</width>\n          <height>24</height>\n         </size>\n        </property>\n        <property name=\"minimum\">\n         <number>0</number>\n        </property>\n        <property name=\"maximum\">\n         <number>3</number>\n        </property>\n        <property name=\"pageStep\">\n         <number>1</number>\n        </property>\n        <property name=\"value\">\n         <number>0</number>\n        </property>\n        <property name=\"orientation\">\n         <enum>Qt::Horizontal</enum>\n        </property>\n        <property name=\"invertedAppearance\">\n         <bool>false</bool>\n        </property>\n        <property name=\"tickPosition\">\n         <enum>QSlider::NoTicks</enum>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QScrollArea\" name=\"scrollArea\">\n        <property name=\"widgetResizable\">\n         <bool>true</bool>\n        </property>\n        <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n         <property name=\"geometry\">\n          <rect>\n           <x>0</x>\n           <y>0</y>\n           <width>553</width>\n           <height>410</height>\n          </rect>\n         </property>\n         <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n          <property name=\"spacing\">\n           <number>0</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>0</number>\n          </property>\n          <item>\n           <widget class=\"QFrame\" name=\"frame\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"frameShape\">\n             <enum>QFrame::NoFrame</enum>\n            </property>\n            <property name=\"frameShadow\">\n             <enum>QFrame::Plain</enum>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n             <property name=\"spacing\">\n              <number>2</number>\n             </property>\n             <property name=\"sizeConstraint\">\n              <enum>QLayout::SetDefaultConstraint</enum>\n             </property>\n             <property name=\"leftMargin\">\n              <number>5</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>5</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n               <property name=\"spacing\">\n                <number>0</number>\n               </property>\n               <property name=\"sizeConstraint\">\n                <enum>QLayout::SetMinimumSize</enum>\n               </property>\n               <item>\n                <widget class=\"QFrame\" name=\"analysisoptionsFrame\">\n                 <property name=\"enabled\">\n                  <bool>true</bool>\n                 </property>\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n                  <property name=\"spacing\">\n                   <number>6</number>\n                  </property>\n                  <property name=\"leftMargin\">\n                   <number>5</number>\n                  </property>\n                  <property name=\"topMargin\">\n                   <number>5</number>\n                  </property>\n                  <property name=\"rightMargin\">\n                   <number>5</number>\n                  </property>\n                  <property name=\"bottomMargin\">\n                   <number>5</number>\n                  </property>\n                 </layout>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"Line\" name=\"advancedAnlysisLine\">\n                 <property name=\"visible\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"orientation\">\n                  <enum>Qt::Horizontal</enum>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QCheckBox\" name=\"writeCheckBox\">\n                 <property name=\"text\">\n                  <string>Load in write mode (-w)</string>\n                 </property>\n                 <property name=\"checked\">\n                  <bool>false</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QCheckBox\" name=\"binCheckBox\">\n                 <property name=\"text\">\n                  <string>Do not load bin information (-n)</string>\n                 </property>\n                 <property name=\"checked\">\n                  <bool>false</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QCheckBox\" name=\"vaCheckBox\">\n                 <property name=\"styleSheet\">\n                  <string notr=\"true\"/>\n                 </property>\n                 <property name=\"text\">\n                  <string>Use virtual addressing</string>\n                 </property>\n                 <property name=\"checked\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QCheckBox\" name=\"demangleCheckBox\">\n                 <property name=\"text\">\n                  <string>Import demangled symbols</string>\n                 </property>\n                 <property name=\"checked\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n               <property name=\"topMargin\">\n                <number>0</number>\n               </property>\n               <item>\n                <widget class=\"QToolButton\" name=\"AdvOptButton\">\n                 <property name=\"text\">\n                  <string>...</string>\n                 </property>\n                 <property name=\"iconSize\">\n                  <size>\n                   <width>8</width>\n                   <height>8</height>\n                  </size>\n                 </property>\n                 <property name=\"checkable\">\n                  <bool>true</bool>\n                 </property>\n                 <property name=\"arrowType\">\n                  <enum>Qt::RightArrow</enum>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"label_5\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Advanced options</string>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <widget class=\"QFrame\" name=\"hideFrame\">\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_8\">\n                <item>\n                 <widget class=\"QLabel\" name=\"label_2\">\n                  <property name=\"sizePolicy\">\n                   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                    <horstretch>0</horstretch>\n                    <verstretch>0</verstretch>\n                   </sizepolicy>\n                  </property>\n                  <property name=\"font\">\n                   <font>\n                    <weight>75</weight>\n                    <bold>true</bold>\n                   </font>\n                  </property>\n                  <property name=\"toolTip\">\n                   <string notr=\"true\"/>\n                  </property>\n                  <property name=\"accessibleDescription\">\n                   <string notr=\"true\"/>\n                  </property>\n                  <property name=\"text\">\n                   <string>CPU options</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n                  <item>\n                   <widget class=\"QLabel\" name=\"archLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"text\">\n                     <string>Architecture:</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QComboBox\" name=\"archComboBox\">\n                    <property name=\"currentText\">\n                     <string notr=\"true\">Auto</string>\n                    </property>\n                    <item>\n                     <property name=\"text\">\n                      <string>Auto</string>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QLabel\" name=\"cpuLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"text\">\n                     <string>CPU:</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QComboBox\" name=\"cpuComboBox\">\n                    <property name=\"minimumSize\">\n                     <size>\n                      <width>70</width>\n                      <height>0</height>\n                     </size>\n                    </property>\n                    <property name=\"editable\">\n                     <bool>true</bool>\n                    </property>\n                    <item>\n                     <property name=\"text\">\n                      <string/>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                 </layout>\n                </item>\n                <item>\n                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n                  <property name=\"topMargin\">\n                   <number>0</number>\n                  </property>\n                  <item>\n                   <widget class=\"QLabel\" name=\"bitsLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"text\">\n                     <string>Bits: </string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QComboBox\" name=\"bitsComboBox\">\n                    <item>\n                     <property name=\"text\">\n                      <string>Auto</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>8</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>16</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>32</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>64</string>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QLabel\" name=\"endiannessLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"text\">\n                     <string>Endianness: </string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QComboBox\" name=\"endiannessComboBox\">\n                    <property name=\"editable\">\n                     <bool>false</bool>\n                    </property>\n                    <property name=\"currentText\">\n                     <string notr=\"true\">Auto</string>\n                    </property>\n                    <item>\n                     <property name=\"text\">\n                      <string>Auto</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>Little</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>Big</string>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                 </layout>\n                </item>\n                <item>\n                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n                  <property name=\"topMargin\">\n                   <number>0</number>\n                  </property>\n                  <item>\n                   <widget class=\"QLabel\" name=\"kernelLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"text\">\n                     <string>Kernel: </string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QComboBox\" name=\"kernelComboBox\">\n                    <property name=\"editable\">\n                     <bool>false</bool>\n                    </property>\n                    <property name=\"currentText\">\n                     <string notr=\"true\">Auto</string>\n                    </property>\n                    <item>\n                     <property name=\"text\">\n                      <string>Auto</string>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QLabel\" name=\"formatLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"text\">\n                     <string>Format:</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QComboBox\" name=\"formatComboBox\">\n                    <property name=\"currentText\">\n                     <string notr=\"true\">Auto</string>\n                    </property>\n                    <item>\n                     <property name=\"text\">\n                      <string>Auto</string>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                 </layout>\n                </item>\n                <item>\n                 <widget class=\"Line\" name=\"line_2\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <layout class=\"QFormLayout\" name=\"formLayout_2\">\n                  <property name=\"sizeConstraint\">\n                   <enum>QLayout::SetDefaultConstraint</enum>\n                  </property>\n                  <property name=\"fieldGrowthPolicy\">\n                   <enum>QFormLayout::ExpandingFieldsGrow</enum>\n                  </property>\n                  <property name=\"labelAlignment\">\n                   <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n                  </property>\n                  <property name=\"formAlignment\">\n                   <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n                  </property>\n                  <property name=\"verticalSpacing\">\n                   <number>10</number>\n                  </property>\n                  <property name=\"topMargin\">\n                   <number>0</number>\n                  </property>\n                  <item row=\"0\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"offsetLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"text\">\n                     <string>Load bin offset (-B)</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"0\" column=\"1\">\n                   <widget class=\"QLineEdit\" name=\"entry_loadOffset\">\n                    <property name=\"inputMask\">\n                     <string notr=\"true\"/>\n                    </property>\n                    <property name=\"text\">\n                     <string notr=\"true\"/>\n                    </property>\n                    <property name=\"frame\">\n                     <bool>false</bool>\n                    </property>\n                    <property name=\"alignment\">\n                     <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                    </property>\n                    <property name=\"placeholderText\">\n                     <string>1024</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"1\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"mapLabel\">\n                    <property name=\"sizePolicy\">\n                     <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                      <horstretch>0</horstretch>\n                      <verstretch>0</verstretch>\n                     </sizepolicy>\n                    </property>\n                    <property name=\"minimumSize\">\n                     <size>\n                      <width>0</width>\n                      <height>0</height>\n                     </size>\n                    </property>\n                    <property name=\"text\">\n                     <string>Map offset (-m)</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"1\" column=\"1\">\n                   <widget class=\"QLineEdit\" name=\"entry_mapOffset\">\n                    <property name=\"text\">\n                     <string/>\n                    </property>\n                    <property name=\"frame\">\n                     <bool>false</bool>\n                    </property>\n                    <property name=\"alignment\">\n                     <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                    </property>\n                    <property name=\"placeholderText\">\n                     <string>0x40000</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"2\" column=\"0\">\n                   <widget class=\"QCheckBox\" name=\"pdbCheckBox\">\n                    <property name=\"text\">\n                     <string>Load PDB</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"2\" column=\"1\">\n                   <widget class=\"QWidget\" name=\"pdbWidget\" native=\"true\">\n                    <property name=\"enabled\">\n                     <bool>true</bool>\n                    </property>\n                    <layout class=\"QHBoxLayout\" name=\"pdbLayout\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QLineEdit\" name=\"pdbLineEdit\">\n                       <property name=\"placeholderText\">\n                        <string>PDB File path</string>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QPushButton\" name=\"pdbSelectButton\">\n                       <property name=\"text\">\n                        <string>Select</string>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </widget>\n                  </item>\n                  <item row=\"3\" column=\"0\">\n                   <widget class=\"QCheckBox\" name=\"scriptCheckBox\">\n                    <property name=\"text\">\n                     <string>Load script file</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"3\" column=\"1\">\n                   <widget class=\"QWidget\" name=\"scriptWidget\" native=\"true\">\n                    <property name=\"enabled\">\n                     <bool>true</bool>\n                    </property>\n                    <layout class=\"QHBoxLayout\" name=\"scriptLayout\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QLineEdit\" name=\"scriptLineEdit\">\n                       <property name=\"placeholderText\">\n                        <string>Path to Rizin script file</string>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QPushButton\" name=\"scriptSelectButton\">\n                       <property name=\"text\">\n                        <string>Select</string>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </widget>\n                  </item>\n                 </layout>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </item>\n          <item>\n           <spacer name=\"verticalSpacer\">\n            <property name=\"orientation\">\n             <enum>Qt::Vertical</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>20</width>\n              <height>40</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n         </layout>\n        </widget>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <spacer name=\"horizontalSpacer_3\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cancelButton\">\n       <property name=\"text\">\n        <string>Cancel</string>\n       </property>\n       <property name=\"default\">\n        <bool>false</bool>\n       </property>\n       <property name=\"flat\">\n        <bool>false</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"okButton\">\n       <property name=\"text\">\n        <string>  Ok  </string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>QSvgWidget</class>\n   <extends>QWidget</extends>\n   <header>QSvgWidget</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/LayoutManager.cpp",
    "content": "#include \"LayoutManager.h\"\n#include \"ui_LayoutManager.h\"\n#include <QIntValidator>\n#include <QInputDialog>\n\nusing namespace Cutter;\n\nLayoutManager::LayoutManager(QMap<QString, Cutter::CutterLayout> &layouts, QWidget *parent)\n    : QDialog(parent), ui(new Ui::LayoutManager), layouts(layouts)\n{\n    ui->setupUi(this);\n    connect(ui->renameButton, &QPushButton::clicked, this, &LayoutManager::renameCurrentLayout);\n    connect(ui->deleteButton, &QPushButton::clicked, this, &LayoutManager::deleteLayout);\n    connect(ui->layoutSelector, &QComboBox::currentTextChanged, this,\n            &LayoutManager::updateButtons);\n    refreshNameList();\n}\n\nLayoutManager::~LayoutManager() {}\n\nvoid LayoutManager::refreshNameList(QString selection)\n{\n    ui->layoutSelector->clear();\n    for (auto it = layouts.begin(), end = layouts.end(); it != end; ++it) {\n        if (!Cutter::isBuiltinLayoutName(it.key())) {\n            ui->layoutSelector->addItem(it.key());\n        }\n    }\n    if (!selection.isEmpty()) {\n        ui->layoutSelector->setCurrentText(selection);\n    }\n    updateButtons();\n}\n\nvoid LayoutManager::renameCurrentLayout()\n{\n    QString current = ui->layoutSelector->currentText();\n    if (layouts.contains(current)) {\n        QString newName;\n        while (newName.isEmpty() || isBuiltinLayoutName(newName) || layouts.contains(newName)) {\n            if (!newName.isEmpty()) {\n                QMessageBox::warning(this, tr(\"Rename layout error\"),\n                                     tr(\"'%1' is already used.\").arg(newName));\n            }\n            newName = QInputDialog::getText(this, tr(\"Save layout\"), tr(\"Enter name\"),\n                                            QLineEdit::Normal, current);\n            if (newName.isEmpty()) {\n                return;\n            }\n        }\n        auto layout = layouts.take(current);\n        layouts.insert(newName, layout);\n        refreshNameList(newName);\n    }\n}\n\nvoid LayoutManager::deleteLayout()\n{\n    auto selected = ui->layoutSelector->currentText();\n    auto answer = QMessageBox::question(this, tr(\"Delete\"),\n                                        tr(\"Do you want to delete '%1'\").arg(selected));\n    if (answer == QMessageBox::Yes) {\n        layouts.remove(selected);\n        refreshNameList();\n    }\n}\n\nvoid LayoutManager::updateButtons()\n{\n    bool hasSelection = !ui->layoutSelector->currentText().isEmpty();\n    ui->renameButton->setEnabled(hasSelection);\n    ui->deleteButton->setEnabled(hasSelection);\n}\n"
  },
  {
    "path": "src/dialogs/LayoutManager.h",
    "content": "#ifndef LAYOUT_MANAGER_H\n#define LAYOUT_MANAGER_H\n\n#include <QDialog>\n#include <memory>\n#include \"core/Cutter.h\"\n#include \"common/CutterLayout.h\"\n\nnamespace Ui {\nclass LayoutManager;\n}\n\nclass LayoutManager : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    LayoutManager(QMap<QString, Cutter::CutterLayout> &layouts, QWidget *parent);\n    ~LayoutManager();\n\nprivate:\n    void refreshNameList(QString selection = \"\");\n    void renameCurrentLayout();\n    void deleteLayout();\n    void updateButtons();\n    std::unique_ptr<Ui::LayoutManager> ui;\n    QMap<QString, Cutter::CutterLayout> &layouts;\n};\n\n#endif // LAYOUT_MANAGER_H\n"
  },
  {
    "path": "src/dialogs/LayoutManager.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>LayoutManager</class>\n <widget class=\"QDialog\" name=\"LayoutManager\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>254</width>\n    <height>88</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Layout</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QComboBox\" name=\"layoutSelector\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"renameButton\">\n       <property name=\"text\">\n        <string>Rename</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"deleteButton\">\n       <property name=\"text\">\n        <string>Delete</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/MapFileDialog.cpp",
    "content": "#include \"MapFileDialog.h\"\n#include \"ui_MapFileDialog.h\"\n\n#include \"common/Configuration.h\"\n\n#include <QFileDialog>\n\nMapFileDialog::MapFileDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MapFileDialog)\n{\n    ui->setupUi(this);\n}\n\nMapFileDialog::~MapFileDialog() {}\n\nvoid MapFileDialog::on_selectFileButton_clicked()\n{\n    QString currentDir = Config()->getRecentFolder();\n    QString fileName = QFileDialog::getOpenFileName(this, tr(\"Select file\"), currentDir);\n\n    if (!fileName.isEmpty()) {\n        ui->filenameLineEdit->setText(fileName);\n        Config()->setRecentFolder(QFileInfo(fileName).absolutePath());\n    }\n}\n\nvoid MapFileDialog::on_buttonBox_accepted()\n{\n    const QString &filePath = QDir::toNativeSeparators(ui->filenameLineEdit->text());\n    RVA mapAddress = RVA_INVALID;\n    QString mapAddressStr = ui->mapAddressLineEdit->text();\n    if (!mapAddressStr.isEmpty()) {\n        mapAddress = Core()->math(mapAddressStr);\n    }\n\n    if (!Core()->mapFile(filePath, mapAddress)) {\n        QMessageBox::critical(this, tr(\"Map new file\"), tr(\"Failed to map a new file\"));\n        return;\n    }\n    close();\n}\n\nvoid MapFileDialog::on_buttonBox_rejected()\n{\n    close();\n}\n"
  },
  {
    "path": "src/dialogs/MapFileDialog.h",
    "content": "#ifndef MAPFILEDIALOG_H\n#define MAPFILEDIALOG_H\n\n#include <QDialog>\n#include <memory>\n#include \"core/Cutter.h\"\n\nnamespace Ui {\nclass MapFileDialog;\n}\n\nclass MapFileDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit MapFileDialog(QWidget *parent = nullptr);\n    ~MapFileDialog();\n\nprivate slots:\n    void on_selectFileButton_clicked();\n    void on_buttonBox_accepted();\n    void on_buttonBox_rejected();\n\nprivate:\n    std::unique_ptr<Ui::MapFileDialog> ui;\n};\n\n#endif // MAPFILEDIALOG_H\n"
  },
  {
    "path": "src/dialogs/MapFileDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MapFileDialog</class>\n <widget class=\"QDialog\" name=\"MapFileDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>171</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Map New File</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QLabel\" name=\"filenameLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string>File:</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"1\">\n    <widget class=\"QLineEdit\" name=\"filenameLineEdit\">\n     <property name=\"focusPolicy\">\n      <enum>Qt::ClickFocus</enum>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\"/>\n     </property>\n     <property name=\"frame\">\n      <bool>false</bool>\n     </property>\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n     <property name=\"clearButtonEnabled\">\n      <bool>false</bool>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"2\">\n    <widget class=\"QPushButton\" name=\"selectFileButton\">\n     <property name=\"text\">\n      <string>Select file</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QLabel\" name=\"mapAddressLabel\">\n     <property name=\"text\">\n      <string>Map address:</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"1\">\n    <widget class=\"QLineEdit\" name=\"mapAddressLineEdit\">\n     <property name=\"toolTip\">\n      <string/>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n     </property>\n     <property name=\"placeholderText\">\n      <string>0x40000</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"0\" colspan=\"3\">\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>MapFileDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/MarkDialog.cpp",
    "content": "#include \"MarkDialog.h\"\n#include \"Cutter.h\"\n#include \"CutterCommon.h\"\n#include \"ui_MarkDialog.h\"\n#include <QColorDialog>\n#include <QRegularExpressionValidator>\n\nMarkDialog::MarkDialog(RVA start, RVA end, QWidget *parent, QString name)\n    : QDialog(parent),\n      ui(new Ui::MarkDialog),\n      markName(name),\n      markFrom(start),\n      markTo(end),\n      markColor(Qt::black),\n      markComment(\"\"),\n      edit(false)\n{\n    ui->setupUi(this);\n\n    if (!markName.isEmpty()) {\n        // Editing existing Mark\n        setWindowTitle(\"Edit Mark\");\n        RzCoreLocked core(Core());\n        RzMarkItem *mark = rz_mark_get(core->marks, markName.toStdString().c_str());\n        if (mark) {\n            markFrom = mark->from;\n            markTo = mark->to;\n            markColor = QColor(mark->color);\n            markComment = mark->comment;\n        }\n        edit = true;\n    } else {\n        // Creating new Mark\n        setWindowTitle(\"Add Mark\");\n        markName = QString(\"%1_%2\").arg(RzAddressString(markFrom), RzAddressString(markTo));\n    }\n\n    ui->startAddressEdit->setText(RzAddressString(markFrom));\n    ui->endAddressEdit->setText(RzAddressString(markTo));\n    ui->nameEdit->setText(markName);\n    ui->commentEdit->setText(markComment);\n    ui->colorDisplay->setStyleSheet(colorToStyle(markColor));\n\n    auto hexValidator =\n            new QRegularExpressionValidator(QRegularExpression(\"(?:0[xX])?[0-9a-fA-F]+\"), this);\n    ui->startAddressEdit->setValidator(hexValidator);\n    ui->endAddressEdit->setValidator(hexValidator);\n\n    connect(ui->colorButton, &QPushButton::clicked, this, &MarkDialog::onPickColor);\n    connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &MarkDialog::accept);\n    connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &MarkDialog::reject);\n}\n\nvoid MarkDialog::accept()\n{\n    bool ok = false;\n    markFrom = ui->startAddressEdit->text().toULongLong(&ok, 16);\n    if (!ok) {\n        QMessageBox::warning(this, tr(\"Invalid Input\"),\n                             tr(\"Starting address is not a valid hexadecimal number\"));\n        return;\n    }\n    markTo = ui->endAddressEdit->text().toULongLong(&ok, 16);\n    if (!ok) {\n        QMessageBox::warning(this, tr(\"Invalid Input\"),\n                             tr(\"Ending address is not a valid hexadecimal number\"));\n        return;\n    }\n    if (markFrom > markTo) {\n        QMessageBox::warning(this, tr(\"Invalid Input\"),\n                             tr(\"Starting address cannot be greater than ending address\"));\n        return;\n    }\n\n    QString name = ui->nameEdit->text();\n    if (edit && !name.isEmpty() && name != markName) {\n        Core()->delMark(markName); // Delete the old mark\n    } else if (name.isEmpty()) {\n        QMessageBox::warning(this, tr(\"Invalid Input\"), tr(\"Name cannot be empty\"));\n        return;\n    }\n\n    markName = name;\n    markComment = ui->commentEdit->toPlainText();\n    Core()->addMark(markFrom, markTo, markName, markComment, markColor);\n\n    QDialog::accept();\n}\n\nMarkDialog::~MarkDialog() {}\n\nvoid MarkDialog::onPickColor()\n{\n    QColor c = QColorDialog::getColor(markColor, this, tr(\"Pick Background Color\"));\n    if (c.isValid()) {\n        markColor = c;\n        ui->colorDisplay->setStyleSheet(colorToStyle(markColor));\n    }\n}\n\nQString MarkDialog::colorToStyle(const QColor &color)\n{\n    return QString(\"background-color: rgba(%1, %2, %3, %4);\")\n            .arg(color.red())\n            .arg(color.green())\n            .arg(color.blue())\n            .arg(MARK_ALPHA_F);\n}\n"
  },
  {
    "path": "src/dialogs/MarkDialog.h",
    "content": "#ifndef MARKDIALOG_H\n#define MARKDIALOG_H\n\n#include \"CutterCommon.h\"\n#include <QDialog>\n#include <QColor>\n\nconstexpr qreal MARK_ALPHA_F = 0.5; // 50% alpha to show blending of multiple overalapping marks\n\nnamespace Ui {\nclass MarkDialog;\n}\n\nclass MarkDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit MarkDialog(RVA from, RVA to, QWidget *parent = nullptr, QString name = {});\n    ~MarkDialog();\n\n    void accept() override;\n\nprivate slots:\n    void onPickColor();\n\nprivate:\n    std::unique_ptr<Ui::MarkDialog> ui;\n    QString markName;\n    RVA markFrom;\n    RVA markTo;\n    QColor markColor;\n    QString markComment;\n    bool edit;\n\n    static QString colorToStyle(const QColor &color);\n};\n\n#endif // MARKDIALOG_H\n"
  },
  {
    "path": "src/dialogs/MarkDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MarkDialog</class>\n <widget class=\"QDialog\" name=\"MarkDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>410</width>\n    <height>314</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Mark Editor</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"nameLabel\">\n       <property name=\"text\">\n        <string>Name:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"nameEdit\"/>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"colorLabel\">\n       <property name=\"text\">\n        <string>Background Color:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <layout class=\"QHBoxLayout\" name=\"colorLayout\">\n       <item>\n        <widget class=\"QPushButton\" name=\"colorButton\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>50</width>\n           <height>25</height>\n          </size>\n         </property>\n         <property name=\"text\">\n          <string>Pick</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"colorDisplay\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>40</width>\n           <height>25</height>\n          </size>\n         </property>\n         <property name=\"autoFillBackground\">\n          <bool>true</bool>\n         </property>\n         <property name=\"frameShape\">\n          <enum>QFrame::Shape::Box</enum>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"startAddressLabel\">\n       <property name=\"text\">\n        <string>Start Address:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"startAddressEdit\"/>\n     </item>\n     <item row=\"4\" column=\"0\">\n      <widget class=\"QLabel\" name=\"endAddressLabel\">\n       <property name=\"text\">\n        <string>End Address:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"endAddressEdit\"/>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"commentLabel\">\n       <property name=\"text\">\n        <string>Comment:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QTextEdit\" name=\"commentEdit\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>0</width>\n         <height>80</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/MultitypeFileSaveDialog.cpp",
    "content": "#include \"CutterConfig.h\"\n\n#include \"MultitypeFileSaveDialog.h\"\n\n#include <QMessageBox>\n\nMultitypeFileSaveDialog::MultitypeFileSaveDialog(QWidget *parent, const QString &caption,\n                                                 const QString &directory)\n    : QFileDialog(parent, caption, directory)\n{\n    this->setAcceptMode(AcceptMode::AcceptSave);\n    this->setFileMode(QFileDialog::AnyFile);\n\n    connect(this, &QFileDialog::filterSelected, this, &MultitypeFileSaveDialog::onFilterSelected);\n}\n\nvoid MultitypeFileSaveDialog::setTypes(\n        const QVector<MultitypeFileSaveDialog::TypeDescription> types, bool useDetection)\n{\n    this->hasTypeDetection = useDetection;\n    this->types.clear();\n    this->types.reserve(types.size() + (useDetection ? 1 : 0));\n    if (useDetection) {\n        this->types.push_back(TypeDescription { tr(\"Detect type (*)\"), \"\", QVariant() });\n    }\n    this->types.append(types);\n    QStringList filters;\n    for (auto &type : this->types) {\n        filters.append(type.description);\n    }\n    setNameFilters(filters);\n    onFilterSelected(this->types.first().description);\n}\n\nMultitypeFileSaveDialog::TypeDescription MultitypeFileSaveDialog::selectedType() const\n{\n    auto filterIt = findType(this->selectedNameFilter());\n    if (filterIt == this->types.end()) {\n        return {};\n    }\n    if (hasTypeDetection && filterIt == this->types.begin()) {\n        QFileInfo info(this->selectedFiles().first());\n        QString currentSuffix = info.suffix();\n        filterIt = std::find_if(types.begin(), types.end(),\n                                [&currentSuffix](const TypeDescription &v) {\n                                    return currentSuffix == v.extension;\n                                });\n        if (filterIt != types.end()) {\n            return *filterIt;\n        }\n        return {};\n    } else {\n        return *filterIt;\n    }\n}\n\nvoid MultitypeFileSaveDialog::done(int r)\n{\n    if (r == QDialog::Accepted) {\n        QFileInfo info(selectedFiles().first());\n        auto selectedType = this->selectedType();\n        if (selectedType.extension.isEmpty()) {\n            QMessageBox::warning(this, tr(\"File save error\"),\n                                 tr(\"Unrecognized extension '%1'\").arg(info.suffix()));\n            return;\n        }\n    }\n    QFileDialog::done(r);\n}\n\nvoid MultitypeFileSaveDialog::onFilterSelected(const QString &filter)\n{\n    auto it = findType(filter);\n    if (it == types.end()) {\n        return;\n    }\n    bool detectionSelected = hasTypeDetection && it == types.begin();\n    if (detectionSelected) {\n        setDefaultSuffix(types[1].extension);\n    } else {\n        setDefaultSuffix(it->extension);\n    }\n    if (!this->selectedFiles().empty()) {\n        QString currentSelection = this->selectedFiles().first();\n        QFileInfo info(currentSelection);\n        if (!detectionSelected) {\n            QString currentSuffix = info.suffix();\n            if (currentSuffix != it->extension) {\n                selectFile(info.dir().filePath(info.completeBaseName() + \".\" + it->extension));\n            }\n        }\n    }\n}\n\nQVector<MultitypeFileSaveDialog::TypeDescription>::const_iterator\nMultitypeFileSaveDialog::findType(const QString &description) const\n{\n    return std::find_if(types.begin(), types.end(), [&description](const TypeDescription &v) {\n        return v.description == description;\n    });\n}\n"
  },
  {
    "path": "src/dialogs/MultitypeFileSaveDialog.h",
    "content": "#ifndef MULTITYPEFILESAVEDIALOG_H\n#define MULTITYPEFILESAVEDIALOG_H\n\n#include <QDialog>\n#include <QFileDialog>\n#include <QVariant>\n\nclass MultitypeFileSaveDialog : public QFileDialog\n{\n    Q_OBJECT\n\npublic:\n    struct TypeDescription\n    {\n        QString description;\n        QString extension;\n        QVariant data;\n    };\n\n    explicit MultitypeFileSaveDialog(QWidget *parent = nullptr, const QString &caption = QString(),\n                                     const QString &directory = QString());\n\n    void setTypes(const QVector<TypeDescription> types, bool useDetection = true);\n    TypeDescription selectedType() const;\n\nprotected:\n    void done(int r) override;\n\nprivate:\n    void onFilterSelected(const QString &filter);\n    QVector<TypeDescription>::const_iterator findType(const QString &description) const;\n\n    QVector<TypeDescription> types;\n    bool hasTypeDetection;\n};\n\n#endif // MULTITYPEFILESAVEDIALOG_H\n"
  },
  {
    "path": "src/dialogs/NativeDebugDialog.cpp",
    "content": "#include \"NativeDebugDialog.h\"\n#include \"ui_NativeDebugDialog.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include \"ProfileDirectivesDialog.h\"\n\n#include <QMessageBox>\n#include <QShortcut>\n#include <QFileDialog>\n#include <QTextStream>\n\nNativeDebugDialog::NativeDebugDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::NativeDebugDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n    auto shortcut = Shortcuts()->makeQShortcut(\"Debug.accept\", ui->argEdit);\n    shortcut->setContext(Qt::ShortcutContext::WidgetShortcut);\n    connect(shortcut, &QShortcut::activated, this, &QDialog::accept);\n\n    connect(ui->profileCheckbox, &QCheckBox::toggled, this,\n            &NativeDebugDialog::profileCheckboxToggled);\n    connect(ui->profilePathBtn, &QPushButton::clicked, this,\n            &NativeDebugDialog::profilePathBtnClicked);\n    connect(ui->directiveListBtn, &QPushButton::clicked, this,\n            &NativeDebugDialog::directiveListBtnClicked);\n\n    profileCheckboxToggled(ui->profileCheckbox->isChecked());\n}\n\nNativeDebugDialog::~NativeDebugDialog() {}\n\nQString NativeDebugDialog::getArgs() const\n{\n    return ui->argEdit->toPlainText();\n}\n\nvoid NativeDebugDialog::setArgs(const QString &args)\n{\n    ui->argEdit->setPlainText(args);\n    ui->argEdit->selectAll();\n}\n\nvoid NativeDebugDialog::setProfilePath(const QString &profilePath)\n{\n    ui->profilePathEdit->setText(profilePath);\n    setFileContents(profilePath);\n}\n\nvoid NativeDebugDialog::profileCheckboxToggled(bool checked)\n{\n    if (checked && showWarningWhenChecked) {\n        showWarning(profilePath);\n    }\n\n    ui->argEdit->setEnabled(!checked);\n    ui->argText->setEnabled(!checked);\n\n    ui->profileText->setEnabled(checked);\n    ui->profilePathEdit->setEnabled(checked);\n    ui->profilePathBtn->setEnabled(checked);\n    ui->directiveText->setEnabled(checked);\n    ui->directiveEdit->setEnabled(checked);\n}\n\nQString NativeDebugDialog::getProfilePath() const\n{\n    return ui->profilePathEdit->text();\n}\n\nQString NativeDebugDialog::getDirectives() const\n{\n    return ui->directiveEdit->toPlainText();\n}\n\nDebugConfigMethod NativeDebugDialog::getSelectedMethod() const\n{\n    return !ui->profileCheckbox->isChecked()\n            ? DebugConfigMethod::CommandLine\n            : (ui->directiveEdit->document()->isModified() ? DebugConfigMethod::RzRunDirectives\n                                                           : DebugConfigMethod::RzRunProfile);\n}\n\nvoid NativeDebugDialog::profilePathBtnClicked()\n{\n    QString filePath =\n            QFileDialog::getOpenFileName(this, tr(\"Open RzRun Profile\"), QString(),\n                                         tr(\"RzRun Profiles (*.rz *.rrz);;All Files (*)\"));\n    setFileContents(filePath);\n}\n\nvoid NativeDebugDialog::setFileContents(const QString &filePath)\n{\n    if (filePath.isEmpty()) {\n        return;\n    }\n\n    ui->profilePathEdit->setText(filePath);\n\n    QFile file(filePath);\n    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n        QTextStream in(&file);\n        QString directives = in.readAll();\n\n        // These directives are loaded from the profile\n        // If unmodified: 'dbg.profile' will point directly to the original file path\n        // If modified: A temporary file containing the new directives will be created and used as\n        // the 'dbg.profile' value instead\n        ui->directiveEdit->setPlainText(directives);\n        ui->directiveEdit->document()->setModified(false);\n\n        file.close();\n    } else if (ui->profileCheckbox->isChecked()) {\n        showWarning(filePath);\n    } else {\n        showWarningWhenChecked = true;\n        profilePath = filePath;\n    }\n}\n\nvoid NativeDebugDialog::directiveListBtnClicked()\n{\n    ProfileDirectivesDialog pdd(this);\n    pdd.exec();\n}\n\nvoid NativeDebugDialog::showWarning(const QString &filePath)\n{\n    QMessageBox::warning(this, tr(\"Error\"), tr(\"Could not open file: %1\").arg(filePath));\n}\n"
  },
  {
    "path": "src/dialogs/NativeDebugDialog.h",
    "content": "#ifndef NATIVEDEBUGDIALOG_H\n#define NATIVEDEBUGDIALOG_H\n\n#include <QDialog>\n#include <memory>\n\nnamespace Ui {\nclass NativeDebugDialog;\n}\n\nenum class DebugConfigMethod { CommandLine, RzRunProfile, RzRunDirectives };\n\n/**\n * @brief Dialog for connecting to native debug\n */\nclass NativeDebugDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit NativeDebugDialog(QWidget *parent = nullptr);\n    ~NativeDebugDialog();\n\n    QString getArgs() const;\n    void setArgs(const QString &args);\n\n    /**\n     * @brief Sets the path to an RzRun profile and loads its contents\n     * @param profilePath Path to a .rz or .rrz file\n     */\n    void setProfilePath(const QString &profilePath);\n\n    QString getProfilePath() const;\n    QString getDirectives() const;\n\n    /**\n     * @brief Determines which configuration method should be used based on UI state\n     * @return The selected DebugConfigMethod\n     */\n    DebugConfigMethod getSelectedMethod() const;\n\nprivate slots:\n    void profileCheckboxToggled(bool checked);\n    void profilePathBtnClicked();\n    void directiveListBtnClicked();\n\nprivate:\n    std::unique_ptr<Ui::NativeDebugDialog> ui;\n\n    /**\n     * @brief Reads a file from disk and populates the directive edit\n     * @param filePath Path of the file to read\n     */\n    void setFileContents(const QString &filePath);\n\n    void showWarning(const QString &filePath);\n    QString profilePath;\n    bool showWarningWhenChecked = false;\n};\n\n#endif // NATIVE_DEBUG_DIALOG\n"
  },
  {
    "path": "src/dialogs/NativeDebugDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>NativeDebugDialog</class>\n <widget class=\"QDialog\" name=\"NativeDebugDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::WindowModality::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>600</width>\n    <height>450</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>0</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Native debugging configuration</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"argText\">\n     <property name=\"text\">\n      <string>Command line arguments:</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPlainTextEdit\" name=\"argEdit\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>35</height>\n      </size>\n     </property>\n     <property name=\"maximumSize\">\n      <size>\n       <width>16777215</width>\n       <height>45</height>\n      </size>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QCheckBox\" name=\"profileCheckbox\">\n     <property name=\"text\">\n      <string>Use RzRun Profile</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"loadProfileLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"profileText\">\n       <property name=\"text\">\n        <string>Load Profile</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"profilePathEdit\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n       <property name=\"readOnly\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"profilePathBtn\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n       <property name=\"text\">\n        <string>Select</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"directiveText\">\n     <property name=\"text\">\n      <string>Profile Directives</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPlainTextEdit\" name=\"directiveEdit\">\n     <property name=\"enabled\">\n      <bool>false</bool>\n     </property>\n     <property name=\"inputMethodHints\">\n      <set>Qt::InputMethodHint::ImhMultiLine</set>\n     </property>\n     <property name=\"placeholderText\">\n      <string>key=value</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"bottomLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"directiveListBtn\">\n       <property name=\"text\">\n        <string>Show Help</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"orientation\">\n        <enum>Qt::Orientation::Horizontal</enum>\n       </property>\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>NativeDebugDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>240</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>254</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>NativeDebugDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>234</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/NewFileDialog.cpp",
    "content": "#include \"InitialOptionsDialog.h\"\n#include \"core/MainWindow.h\"\n#include \"dialogs/NewFileDialog.h\"\n#include \"dialogs/AboutDialog.h\"\n#include \"ui_NewFileDialog.h\"\n#include \"common/Helpers.h\"\n#include \"common/HighDpiPixmap.h\"\n\n#include <QFileDialog>\n#include <QtGui>\n#include <QMessageBox>\n#include <QDir>\n#include <QPushButton>\n#include <QLineEdit>\n\nconst int NewFileDialog::MaxRecentFiles;\n\nstatic QColor getColorFor(int pos)\n{\n    static const QList<QColor> colors = {\n        QColor(29, 188, 156), // Turquoise\n        QColor(52, 152, 219), // Blue\n        QColor(155, 89, 182), // Violet\n        QColor(52, 73, 94), // Grey\n        QColor(231, 76, 60), // Red\n        QColor(243, 156, 17) // Orange\n    };\n    return colors[pos % colors.size()];\n}\n\nstatic QIcon getIconFor(const QString &str, int pos)\n{\n    // Add to the icon list\n    int w = 64;\n    int h = 64;\n\n    HighDpiPixmap pixmap(w, h);\n    pixmap.fill(Qt::transparent);\n\n    QPainter pixPaint(&pixmap);\n    pixPaint.setPen(Qt::NoPen);\n    pixPaint.setRenderHint(QPainter::Antialiasing);\n    pixPaint.setBrush(getColorFor(pos));\n    pixPaint.drawEllipse(1, 1, w - 2, h - 2);\n    pixPaint.setPen(Qt::white);\n    QFont font = Config()->getBaseFont();\n    font.setBold(true);\n    font.setPointSize(18);\n    pixPaint.setFont(font);\n    pixPaint.drawText(0, 0, w, h - 2, Qt::AlignCenter, QString(str).toUpper().mid(0, 2));\n    return QIcon(pixmap);\n}\n\nNewFileDialog::NewFileDialog(MainWindow *main)\n    : QDialog(nullptr), // no parent on purpose, using main causes weird positioning\n      ui(new Ui::NewFileDialog),\n      main(main)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n    setAcceptDrops(true);\n    ui->recentsListWidget->addAction(ui->actionRemove_item);\n    ui->recentsListWidget->addAction(ui->actionClear_all);\n    ui->projectsListWidget->addAction(ui->actionRemove_project);\n    ui->projectsListWidget->addAction(ui->actionClearProjects);\n    ui->logoSvgWidget->load(Config()->getLogoFile());\n\n    fillRecentFilesList();\n    fillIOPluginsList();\n    fillProjectsList();\n\n    // Set last clicked tab\n    ui->tabWidget->setCurrentIndex(Config()->getNewFileLastClicked());\n\n    /* Set focus on the TextInput */\n    ui->newFileEdit->setFocus();\n\n    /* Install an event filter for shellcode text edit to enable ctrl+return event */\n    ui->shellcodeText->installEventFilter(this);\n\n    updateLoadProjectButton();\n}\n\nNewFileDialog::~NewFileDialog() {}\n\nvoid NewFileDialog::on_loadFileButton_clicked()\n{\n    loadFile(ui->newFileEdit->text());\n}\n\nvoid NewFileDialog::on_selectFileButton_clicked()\n{\n    QString currentDir = Config()->getRecentFolder();\n    const QString &fileName = QDir::toNativeSeparators(\n            QFileDialog::getOpenFileName(this, tr(\"Select file\"), currentDir));\n\n    if (!fileName.isEmpty()) {\n        ui->newFileEdit->setText(fileName);\n        ui->loadFileButton->setFocus();\n        Config()->setRecentFolder(QFileInfo(fileName).absolutePath());\n    }\n}\n\nvoid NewFileDialog::on_selectProjectFileButton_clicked()\n{\n    const QString &fileName =\n            QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr(\"Open Project\")));\n\n    if (!fileName.isEmpty()) {\n        ui->projectFileEdit->setText(fileName);\n        ui->loadProjectButton->setFocus();\n    }\n}\n\nvoid NewFileDialog::on_loadProjectButton_clicked()\n{\n    loadProject(ui->projectFileEdit->text());\n}\n\nvoid NewFileDialog::on_shellcodeButton_clicked()\n{\n    QString shellcode = ui->shellcodeText->toPlainText();\n    QString extractedCode = \"\";\n    static const QRegularExpression rx(\"([0-9a-f]{2})\", QRegularExpression::CaseInsensitiveOption);\n    QRegularExpressionMatchIterator i = rx.globalMatch(shellcode);\n    while (i.hasNext()) {\n        QRegularExpressionMatch match = i.next();\n        extractedCode.append(match.captured(1));\n    }\n    int size = extractedCode.size() / 2;\n    if (size > 0) {\n        loadShellcode(extractedCode, size);\n    }\n}\n\nvoid NewFileDialog::on_recentsListWidget_itemClicked(QListWidgetItem *item)\n{\n    updateSelectionFromItem(item);\n}\n\nvoid NewFileDialog::on_recentsListWidget_currentItemChanged(QListWidgetItem *current)\n{\n    updateSelectionFromItem(current);\n}\n\nvoid NewFileDialog::on_recentsListWidget_itemDoubleClicked(QListWidgetItem *item)\n{\n    const QStringList sitem = item->data(Qt::UserRole).toStringList();\n    loadFile(sitem.at(1));\n}\n\nvoid NewFileDialog::on_projectFileEdit_textChanged()\n{\n    updateLoadProjectButton();\n}\n\nvoid NewFileDialog::on_projectsListWidget_itemClicked(QListWidgetItem *item)\n{\n    ui->projectFileEdit->setText(item->data(Qt::UserRole).toStringList().at(1));\n}\n\nvoid NewFileDialog::on_projectsListWidget_itemDoubleClicked(QListWidgetItem *item)\n{\n    loadProject(item->data(Qt::UserRole).toStringList().at(1));\n}\n\nvoid NewFileDialog::on_aboutButton_clicked()\n{\n    AboutDialog *a = new AboutDialog(this);\n    a->setAttribute(Qt::WA_DeleteOnClose);\n    a->open();\n}\n\nvoid NewFileDialog::on_actionRemove_item_triggered()\n{\n    // Remove selected item from recents list\n    QListWidgetItem *item = ui->recentsListWidget->currentItem();\n    if (item == nullptr) {\n        return;\n    }\n    QStringList sitem = item->data(Qt::UserRole).toStringList();\n    RecentFileEntry file = { sitem.at(0), sitem.at(1) };\n    QList<RecentFileEntry> files = Config()->getRecentFiles();\n    files.removeAll(file);\n    Config()->setRecentFiles(files);\n    ui->recentsListWidget->takeItem(ui->recentsListWidget->currentRow());\n    ui->newFileEdit->clear();\n}\n\nvoid NewFileDialog::on_actionClear_all_triggered()\n{\n    Config()->setRecentFiles({});\n    ui->recentsListWidget->clear();\n    ui->newFileEdit->clear();\n}\n\nvoid NewFileDialog::on_actionRemove_project_triggered()\n{\n    QListWidgetItem *item = ui->projectsListWidget->currentItem();\n    if (item == nullptr) {\n        return;\n    }\n    QString sitem = item->data(Qt::UserRole).toString();\n    RecentFileEntry project = { \"\", sitem };\n    QList<RecentFileEntry> files = Config()->getRecentProjects();\n    files.removeAll(project);\n    Config()->setRecentProjects(files);\n    ui->projectsListWidget->takeItem(ui->projectsListWidget->currentRow());\n    ui->projectFileEdit->clear();\n}\n\nvoid NewFileDialog::on_actionClearProjects_triggered()\n{\n    Config()->setRecentProjects({});\n    ui->projectsListWidget->clear();\n    ui->projectFileEdit->clear();\n}\n\nvoid NewFileDialog::dragEnterEvent(QDragEnterEvent *event)\n{\n    // Accept drag & drop events only if they provide a URL\n    if (event->mimeData()->hasUrls()) {\n        event->acceptProposedAction();\n    }\n}\n\nvoid NewFileDialog::dropEvent(QDropEvent *event)\n{\n    // Accept drag & drop events only if they provide a URL\n    if (event->mimeData()->urls().count() == 0) {\n        qWarning() << \"No URL in drop event, ignoring it.\";\n        return;\n    }\n\n    event->acceptProposedAction();\n    loadFile(event->mimeData()->urls().first().toLocalFile());\n}\n\n/*\n * @brief Add the existing files from the list to the widget.\n * @return the list of files that actually exist\n */\nstatic QList<RecentFileEntry> fillFilesList(QListWidget *widget,\n                                            const QList<RecentFileEntry> &files)\n{\n    QList<RecentFileEntry> updatedFiles = files;\n\n    QMutableListIterator<RecentFileEntry> it(updatedFiles);\n    int i = 0;\n    while (it.hasNext()) {\n        // Get the file name\n        const RecentFileEntry &file = it.next();\n        const QString &path = QDir::toNativeSeparators(file.path);\n        const QString &ioMode = file.ioMode;\n        const QString homepath = QDir::homePath();\n        const QString basename = path.section(QDir::separator(), -1);\n\n        QString filenameHome = path;\n        filenameHome.replace(homepath, \"~\");\n        filenameHome.replace(basename, \"\");\n        filenameHome.chop(1); // Remove last character that will be a path separator\n        // Get file info\n        QFileInfo info(path);\n        if (!info.exists()) {\n            it.remove();\n        } else {\n            // Format the text\n            const QString text =\n                    QString(\"%1\\n%2\\nSize: %3\")\n                            .arg(basename, filenameHome, qhelpers::formatBytecount(info.size()));\n            QListWidgetItem *item = new QListWidgetItem(getIconFor(basename, i++), text);\n\n            // add the item to the file list\n            QStringList fullpath = { ioMode, path };\n            item->setData(Qt::UserRole, fullpath);\n            widget->addItem(item);\n        }\n    }\n    return updatedFiles;\n}\n\nbool NewFileDialog::fillRecentFilesList()\n{\n    QList<RecentFileEntry> files = Config()->getRecentFiles();\n    files = fillFilesList(ui->recentsListWidget, files);\n    // Removed files were deleted from the stringlist. Save it again.\n    Config()->setRecentFiles(files);\n    return !files.isEmpty();\n}\n\nbool NewFileDialog::fillProjectsList()\n{\n    QList<RecentFileEntry> files = Config()->getRecentProjects();\n    files = fillFilesList(ui->projectsListWidget, files);\n    Config()->setRecentProjects(files);\n    return !files.isEmpty();\n}\n\nvoid NewFileDialog::fillIOPluginsList()\n{\n    ui->ioPlugin->clear();\n    ui->ioPlugin->addItem(\"file://\");\n    ui->ioPlugin->setItemData(0, tr(\"Open a file without additional options/settings.\"),\n                              Qt::ToolTipRole);\n\n    int index = 1;\n    QList<RzIOPluginDescription> ioPlugins = Core()->getRIOPluginDescriptions();\n    for (const RzIOPluginDescription &plugin : ioPlugins) {\n        // Hide debug plugins\n        if (plugin.permissions.contains('d')) {\n            continue;\n        }\n        const auto &uris = plugin.uris;\n        for (const auto &uri : uris) {\n            if (uri == \"file://\") {\n                continue;\n            }\n            ui->ioPlugin->addItem(uri);\n            ui->ioPlugin->setItemData(index, plugin.description, Qt::ToolTipRole);\n            index++;\n        }\n    }\n}\n\nvoid NewFileDialog::updateLoadProjectButton()\n{\n    ui->loadProjectButton->setEnabled(!ui->projectFileEdit->text().trimmed().isEmpty());\n}\n\nvoid NewFileDialog::loadFile(const QString &filename)\n{\n    bool isFileless = ui->checkBox_FilelessOpen->isChecked();\n    QString ioFile;\n    if (!isFileless) {\n        const QString &nativeFn = QDir::toNativeSeparators(filename);\n        if (ui->ioPlugin->currentIndex() == 0 && !Core()->tryFile(nativeFn, false)) {\n            QMessageBox msgBox(this);\n            msgBox.setText(tr(\"Select a new program or a previous one before continuing.\"));\n            msgBox.exec();\n            return;\n        }\n\n        const QString ioMode = ui->ioPlugin->currentText();\n        ioFile = ioMode + nativeFn;\n\n        // Add file to recent file list\n        RecentFileEntry file = { ioMode, nativeFn };\n        QList<RecentFileEntry> files = Config()->getRecentFiles();\n        files.removeAll(file);\n        files.prepend(file);\n        while (files.size() > MaxRecentFiles)\n            files.removeLast();\n        Config()->setRecentFiles(files);\n    }\n\n    // Close dialog and open MainWindow/InitialOptionsDialog\n    InitialOptions options;\n    options.filename = ioFile;\n    main->openNewFile(options, isFileless);\n\n    close();\n}\n\nvoid NewFileDialog::loadProject(const QString &project)\n{\n    MainWindow *main = new MainWindow();\n    if (!main->openProject(project)) {\n        return;\n    }\n    close();\n}\n\nvoid NewFileDialog::loadShellcode(const QString &shellcode, const int size)\n{\n    MainWindow *main = new MainWindow();\n    InitialOptions options;\n    options.filename = QString(\"malloc://%1\").arg(size);\n    options.shellcode = shellcode;\n    main->openNewFile(options);\n    close();\n}\n\nvoid NewFileDialog::on_tabWidget_currentChanged(int index)\n{\n    Config()->setNewFileLastClicked(index);\n}\n\nbool NewFileDialog::eventFilter(QObject * /*obj*/, QEvent *event)\n{\n    QString shellcode = ui->shellcodeText->toPlainText();\n    QString extractedCode = \"\";\n    static const QRegularExpression rx(\"([0-9a-f]{2})\", QRegularExpression::CaseInsensitiveOption);\n    QRegularExpressionMatchIterator i = rx.globalMatch(shellcode);\n    int size = 0;\n\n    if (event->type() == QEvent::KeyPress) {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n\n        // Confirm comment by pressing Ctrl/Cmd+Return\n        if ((keyEvent->modifiers() & Qt::ControlModifier)\n            && ((keyEvent->key() == Qt::Key_Enter) || (keyEvent->key() == Qt::Key_Return))) {\n            while (i.hasNext()) {\n                QRegularExpressionMatch match = i.next();\n                extractedCode.append(match.captured(1));\n            }\n            size = extractedCode.size() / 2;\n            if (size > 0) {\n                loadShellcode(extractedCode, size);\n            }\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid NewFileDialog::updateSelectionFromItem(QListWidgetItem *item)\n{\n    if (!item) {\n        return;\n    }\n    QStringList sitem = item->data(Qt::UserRole).toStringList();\n    ui->ioPlugin->setCurrentIndex(ui->ioPlugin->findText(sitem.at(0)));\n    ui->newFileEdit->setText(sitem.at(1));\n}\n"
  },
  {
    "path": "src/dialogs/NewFileDialog.h",
    "content": "#ifndef NEWFILEDIALOG_H\n#define NEWFILEDIALOG_H\n\n#include <QDialog>\n#include <QListWidgetItem>\n#include <memory>\n\nnamespace Ui {\nclass NewFileDialog;\n}\n\nclass MainWindow;\n\nclass NewFileDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit NewFileDialog(MainWindow *main);\n    ~NewFileDialog();\n\nprivate slots:\n    void on_loadFileButton_clicked();\n    void on_selectFileButton_clicked();\n    void on_selectProjectFileButton_clicked();\n\n    void on_loadProjectButton_clicked();\n    void on_shellcodeButton_clicked();\n\n    void on_aboutButton_clicked();\n\n    void on_recentsListWidget_itemClicked(QListWidgetItem *item);\n    void on_recentsListWidget_itemDoubleClicked(QListWidgetItem *item);\n    void on_recentsListWidget_currentItemChanged(QListWidgetItem *current);\n\n    void on_projectFileEdit_textChanged();\n    void on_projectsListWidget_itemClicked(QListWidgetItem *item);\n    void on_projectsListWidget_itemDoubleClicked(QListWidgetItem *item);\n\n    void on_actionRemove_item_triggered();\n    void on_actionClear_all_triggered();\n    void on_actionRemove_project_triggered();\n    void on_actionClearProjects_triggered();\n\n    void on_tabWidget_currentChanged(int index);\n\nprotected:\n    void dragEnterEvent(QDragEnterEvent *event);\n    void dropEvent(QDropEvent *event);\n\nprivate:\n    std::unique_ptr<Ui::NewFileDialog> ui;\n\n    MainWindow *main;\n\n    /**\n     * @return true if list is not empty\n     */\n    bool fillRecentFilesList();\n\n    /**\n     * @return true if list is not empty\n     */\n    bool fillProjectsList();\n    void fillIOPluginsList();\n\n    void updateLoadProjectButton();\n\n    void loadFile(const QString &filename);\n    void loadProject(const QString &project);\n    void loadShellcode(const QString &shellcode, const int size);\n    /**\n     * @brief Updates IO plugin and file path based on the selected recent item\n     * @param item The list item containing the IO mode and file path in its UserRole data\n     */\n    void updateSelectionFromItem(QListWidgetItem *item);\n\n    bool eventFilter(QObject *obj, QEvent *event);\n\n    static const int MaxRecentFiles = 5;\n};\n\n#endif // NEWFILEDIALOG_H\n"
  },
  {
    "path": "src/dialogs/NewFileDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>NewFileDialog</class>\n <widget class=\"QDialog\" name=\"NewFileDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>451</width>\n    <height>599</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Open File</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n     <property name=\"leftMargin\">\n      <number>2</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>2</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>2</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>2</number>\n     </property>\n     <item alignment=\"Qt::AlignHCenter\">\n      <widget class=\"QSvgWidget\" name=\"logoSvgWidget\" native=\"true\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>88</width>\n         <height>88</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>88</width>\n         <height>88</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"buttonBar\" stretch=\"0,0,0\">\n     <property name=\"spacing\">\n      <number>10</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <spacer name=\"horizontalSpacer_4\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>500</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"aboutButton\">\n       <property name=\"text\">\n        <string>About</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>500</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n     <property name=\"spacing\">\n      <number>10</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <widget class=\"QFrame\" name=\"frame\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>0</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"frameShape\">\n        <enum>QFrame::NoFrame</enum>\n       </property>\n       <property name=\"frameShadow\">\n        <enum>QFrame::Plain</enum>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QTabWidget\" name=\"tabWidget\">\n          <property name=\"currentIndex\">\n           <number>2</number>\n          </property>\n          <widget class=\"QWidget\" name=\"filesTab\">\n           <attribute name=\"title\">\n            <string>Open File</string>\n           </attribute>\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n            <item>\n             <layout class=\"QGridLayout\" name=\"gridLayout\">\n              <property name=\"sizeConstraint\">\n               <enum>QLayout::SetDefaultConstraint</enum>\n              </property>\n              <property name=\"horizontalSpacing\">\n               <number>5</number>\n              </property>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QLabel\" name=\"newFileLabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"text\">\n                 <string>&lt;b&gt;Select new file&lt;b&gt;</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"2\">\n               <widget class=\"QPushButton\" name=\"selectFileButton\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"text\">\n                 <string>Select</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"1\">\n               <widget class=\"QLineEdit\" name=\"newFileEdit\">\n                <property name=\"frame\">\n                 <bool>false</bool>\n                </property>\n                <property name=\"clearButtonEnabled\">\n                 <bool>true</bool>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QComboBox\" name=\"ioPlugin\"/>\n              </item>\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"ioLabel\">\n                <property name=\"text\">\n                 <string>&lt;b&gt;IO&lt;/b&gt;</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n            <item>\n             <widget class=\"Line\" name=\"line\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n                <horstretch>1</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QListWidget\" name=\"recentsListWidget\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n                <horstretch>0</horstretch>\n                <verstretch>1</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"font\">\n               <font>\n                <pointsize>11</pointsize>\n               </font>\n              </property>\n              <property name=\"contextMenuPolicy\">\n               <enum>Qt::ActionsContextMenu</enum>\n              </property>\n              <property name=\"frameShape\">\n               <enum>QFrame::NoFrame</enum>\n              </property>\n              <property name=\"frameShadow\">\n               <enum>QFrame::Plain</enum>\n              </property>\n              <property name=\"lineWidth\">\n               <number>0</number>\n              </property>\n              <property name=\"iconSize\">\n               <size>\n                <width>48</width>\n                <height>48</height>\n               </size>\n              </property>\n              <property name=\"verticalScrollMode\">\n               <enum>QAbstractItemView::ScrollPerPixel</enum>\n              </property>\n              <property name=\"resizeMode\">\n               <enum>QListView::Adjust</enum>\n              </property>\n              <property name=\"spacing\">\n               <number>5</number>\n              </property>\n              <property name=\"viewMode\">\n               <enum>QListView::ListMode</enum>\n              </property>\n              <property name=\"uniformItemSizes\">\n               <bool>false</bool>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"selectionRectVisible\">\n               <bool>true</bool>\n              </property>\n              <property name=\"sortingEnabled\">\n               <bool>false</bool>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n              <item>\n               <spacer name=\"horizontalSpacer_2\">\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>40</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n              <item>\n               <widget class=\"QCheckBox\" name=\"checkBox_FilelessOpen\">\n                <property name=\"text\">\n                 <string>Don't open any file</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QPushButton\" name=\"loadFileButton\">\n                <property name=\"text\">\n                 <string>Open</string>\n                </property>\n                <property name=\"default\">\n                 <bool>true</bool>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n           </layout>\n          </widget>\n          <widget class=\"QWidget\" name=\"shellcodeTab\">\n           <attribute name=\"title\">\n            <string>Open Shellcode</string>\n           </attribute>\n           <layout class=\"QVBoxLayout\" name=\"shellcodeLayout\">\n            <item>\n             <widget class=\"QLabel\" name=\"shellcodeLabel\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"text\">\n               <string>&lt;b&gt;Paste Shellcode&lt;b&gt;</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPlainTextEdit\" name=\"shellcodeText\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n                <horstretch>1</horstretch>\n                <verstretch>1</verstretch>\n               </sizepolicy>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <layout class=\"QHBoxLayout\" name=\"shellcodeLayout_1\">\n              <item>\n               <spacer name=\"shellcodeSpacer\">\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>0</width>\n                  <height>0</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n              <item>\n               <widget class=\"QPushButton\" name=\"shellcodeButton\">\n                <property name=\"text\">\n                 <string>Open</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n           </layout>\n          </widget>\n          <widget class=\"QWidget\" name=\"projectsTab\">\n           <attribute name=\"title\">\n            <string>Projects</string>\n           </attribute>\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n            <item>\n             <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n              <property name=\"sizeConstraint\">\n               <enum>QLayout::SetDefaultConstraint</enum>\n              </property>\n              <property name=\"horizontalSpacing\">\n               <number>5</number>\n              </property>\n              <item row=\"1\" column=\"1\">\n               <widget class=\"QPushButton\" name=\"selectProjectFileButton\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"text\">\n                 <string>Select</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QLineEdit\" name=\"projectFileEdit\">\n                <property name=\"frame\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"projectFileLabel\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"text\">\n                 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Open Project&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n            <item>\n             <widget class=\"Line\" name=\"line_2\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n                <horstretch>1</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QListWidget\" name=\"projectsListWidget\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n                <horstretch>0</horstretch>\n                <verstretch>1</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"font\">\n               <font>\n                <pointsize>11</pointsize>\n               </font>\n              </property>\n              <property name=\"contextMenuPolicy\">\n               <enum>Qt::ActionsContextMenu</enum>\n              </property>\n              <property name=\"frameShape\">\n               <enum>QFrame::NoFrame</enum>\n              </property>\n              <property name=\"frameShadow\">\n               <enum>QFrame::Plain</enum>\n              </property>\n              <property name=\"lineWidth\">\n               <number>0</number>\n              </property>\n              <property name=\"iconSize\">\n               <size>\n                <width>48</width>\n                <height>48</height>\n               </size>\n              </property>\n              <property name=\"verticalScrollMode\">\n               <enum>QAbstractItemView::ScrollPerPixel</enum>\n              </property>\n              <property name=\"resizeMode\">\n               <enum>QListView::Adjust</enum>\n              </property>\n              <property name=\"spacing\">\n               <number>5</number>\n              </property>\n              <property name=\"viewMode\">\n               <enum>QListView::ListMode</enum>\n              </property>\n              <property name=\"uniformItemSizes\">\n               <bool>false</bool>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>false</bool>\n              </property>\n              <property name=\"selectionRectVisible\">\n               <bool>true</bool>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n              <item>\n               <spacer name=\"horizontalSpacer_3\">\n                <property name=\"orientation\">\n                 <enum>Qt::Horizontal</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>40</width>\n                  <height>20</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n              <item>\n               <widget class=\"QPushButton\" name=\"loadProjectButton\">\n                <property name=\"text\">\n                 <string>Open</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n           </layout>\n          </widget>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n  <action name=\"actionRemove_item\">\n   <property name=\"text\">\n    <string>Remove item</string>\n   </property>\n  </action>\n  <action name=\"actionClear_all\">\n   <property name=\"text\">\n    <string>Clear all</string>\n   </property>\n  </action>\n  <action name=\"actionRemove_project\">\n   <property name=\"text\">\n    <string>Delete project</string>\n   </property>\n  </action>\n  <action name=\"actionClearProjects\">\n   <property name=\"text\">\n    <string>Clear all projects</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>QSvgWidget</class>\n   <extends>QWidget</extends>\n   <header>QSvgWidget</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/ProfileDirectivesDialog.cpp",
    "content": "#include \"ProfileDirectivesDialog.h\"\n#include \"ui_ProfileDirectivesDialog.h\"\n\n#include <QPushButton>\n\nProfileDirectivesDialog::ProfileDirectivesDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::ProfileDirectivesDialog)\n{\n    ui->setupUi(this);\n\n    setWindowTitle(tr(\"Profile Directives\"));\n\n    model = new QStandardItemModel(this);\n    model->setHorizontalHeaderLabels({ tr(\"Key\"), tr(\"Description\") });\n\n    addDirective(\"arg[0-511]\", tr(\"Set value for argument N passed to the program\"));\n    addDirective(\"aslr\", tr(\"Enable or disable ASLR\"));\n    addDirective(\"bits\", tr(\"Set 32 or 64 bit (if the architecture supports it)\"));\n    addDirective(\"chdir\", tr(\"Change directory before executing the program\"));\n    addDirective(\"chroot\", tr(\"Run the program in chroot. requires some previous setup\"));\n    addDirective(\"connect\", tr(\"Connect stdin/stdout/stderr to a socket\"));\n    addDirective(\"core\", tr(\"Set no limit the core file size\"));\n    addDirective(\"daemon\",\n                 tr(\"Set to false by default, otherwise it will run the program in background, \"\n                    \"detached from the terminal\"));\n    addDirective(\"envfile\", tr(\"Set a file with lines like `var=value` to be used as env\"));\n    addDirective(\"fork\",\n                 tr(\"Used with the listen option, allow to spawn a different process for each \"\n                    \"connection. Ignored when debugging.\"));\n    addDirective(\"input\", tr(\"Set string to be passed to the program via stdin\"));\n    addDirective(\"libpath\",\n                 tr(\"Override path where the dynamic loader will look for shared libraries\"));\n    addDirective(\"listen\", tr(\"Bound stdin/stdout/stderr to a listening socket\"));\n    addDirective(\"maxfd\", tr(\"Set the maximum number of file descriptors\"));\n    addDirective(\"maxproc\", tr(\"Set the maximum number of processes\"));\n    addDirective(\"maxstack\", tr(\"Set the maximum size for the stack\"));\n    addDirective(\"nice\", tr(\"Set the niceness level of the process\"));\n    addDirective(\"pid\", tr(\"Set to true to print the PID of the process to stderr\"));\n    addDirective(\"pidfile\", tr(\"Print the PID of the process to the specified file\"));\n    addDirective(\"preload\", tr(\"Preload a library (not supported on Windows, only linux,osx,bsd)\"));\n    addDirective(\"program\", tr(\"Path to program to be executed\"));\n    addDirective(\"pty\", tr(\"Use a pty for connection over socket (with connect/listen)\"));\n    addDirective(\"runlib\", tr(\"Path to the library to be executed\"));\n    addDirective(\"runlib.fcn\", tr(\"Function name to call from runlib library\"));\n    addDirective(\"rzpreload\",\n                 tr(\"Preload with librz, kill -USR1 to get an rizin shell or -USRZ to spawn a \"\n                    \"webserver in a thread\"));\n    addDirective(\"setegid\", tr(\"Set effective process group id\"));\n    addDirective(\"setenv\", tr(\"Set value for given environment variable (setenv=FOO=BAR)\"));\n    addDirective(\"seteuid\", tr(\"Set effective process uid\"));\n    addDirective(\"setgid\", tr(\"Set process group id\"));\n    addDirective(\"setuid\", tr(\"Set process uid\"));\n    addDirective(\"sleep\", tr(\"Sleep for the given amount of seconds\"));\n    addDirective(\"sterr\", tr(\"Select file to replace stderr file descriptor\"));\n    addDirective(\"stdin\", tr(\"Select file to read data from stdin\"));\n    addDirective(\n            \"stdio\",\n            tr(\"Select io stream to redirect data from/to. Redirect input/output to the process \"\n               \"created by the command prefixed by '!' (stdio=!cmd)\"));\n    addDirective(\"stdout\", tr(\"Select file to replace stdout file descriptor\"));\n    addDirective(\"system\", tr(\"Execute the given command\"));\n    addDirective(\"timeout\", tr(\"Set a timeout\"));\n    addDirective(\"timeoutsig\",\n                 tr(\"Signal to use when killing the child because the timeout happens\"));\n    addDirective(\"unsetenv\", tr(\"Unset one environment variable\"));\n\n    proxyModel = new QSortFilterProxyModel(this);\n    proxyModel->setSourceModel(model);\n    proxyModel->setFilterKeyColumn(0);\n    proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);\n\n    ui->treeView->setModel(proxyModel);\n    ui->treeView->setSortingEnabled(true);\n    ui->treeView->resizeColumnToContents(1);\n    ui->treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);\n\n    connect(ui->filterEdit, &QLineEdit::textChanged, proxyModel,\n            &QSortFilterProxyModel::setFilterFixedString);\n    connect(ui->closeBtn, &QPushButton::clicked, this, &QDialog::close);\n}\n\nProfileDirectivesDialog::~ProfileDirectivesDialog() {}\n\nvoid ProfileDirectivesDialog::addDirective(const QString &key, const QString &description)\n{\n    QList<QStandardItem *> items;\n    items << new QStandardItem(key);\n    items << new QStandardItem(description);\n    model->appendRow(items);\n}\n"
  },
  {
    "path": "src/dialogs/ProfileDirectivesDialog.h",
    "content": "#ifndef PROFILEDIRECTIVESDIALOG_H\n#define PROFILEDIRECTIVESDIALOG_H\n\n#include <QDialog>\n#include <QStandardItemModel>\n#include <QSortFilterProxyModel>\n#include <memory>\n\nnamespace Ui {\nclass ProfileDirectivesDialog;\n}\n\n/**\n * @brief Dialog displaying a searchable list of available RzRun profile directives\n */\nclass ProfileDirectivesDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit ProfileDirectivesDialog(QWidget *parent = nullptr);\n    ~ProfileDirectivesDialog();\n\nprivate:\n    std::unique_ptr<Ui::ProfileDirectivesDialog> ui;\n    QStandardItemModel *model;\n    QSortFilterProxyModel *proxyModel;\n\n    /**\n     * @brief Adds a directive entry to the list model\n     * @param key The directive name (e.g., \"aslr\")\n     * @param description Explanation of what the directive does\n     */\n    void addDirective(const QString &key, const QString &description);\n};\n\n#endif\n"
  },
  {
    "path": "src/dialogs/ProfileDirectivesDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ProfileDirectivesDialog</class>\n <widget class=\"QDialog\" name=\"ProfileDirectivesDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>950</width>\n    <height>650</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTreeView\" name=\"treeView\"/>\n   </item>\n   <item>\n    <widget class=\"QLineEdit\" name=\"filterEdit\">\n     <property name=\"placeholderText\">\n      <string>Filter</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"bottomLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Orientation::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"closeBtn\">\n       <property name=\"text\">\n        <string>Close</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/RegisterProfileDialog.cpp",
    "content": "#include \"RegisterProfileDialog.h\"\n#include \"ui_RegisterProfileDialog.h\"\n#include \"Cutter.h\"\n#include \"Configuration.h\"\n#include \"EditRegProfileDialog.h\"\n\n#include <QFileDialog>\n#include <QPushButton>\n#include <QMessageBox>\n#include <QFile>\n#include <QTextStream>\n#include <QMenu>\n\nRegisterProfileDialog::RegisterProfileDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::RegisterProfileDialog)\n{\n    ui->setupUi(this);\n\n    ui->recentProfilesList->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    connect(ui->loadProfileBtn, &QPushButton::clicked, this,\n            &RegisterProfileDialog::loadProfileBtnClicked);\n    connect(ui->editProfileBtn, &QPushButton::clicked, this,\n            &RegisterProfileDialog::editProfileBtnClicked);\n    connect(ui->recentProfilesList, &QListWidget::itemClicked, this,\n            &RegisterProfileDialog::itemClicked);\n    connect(ui->recentProfilesList, &QListWidget::customContextMenuRequested, this,\n            &RegisterProfileDialog::showContextMenu);\n    connect(ui->loadGDBBtn, &QPushButton::clicked, this, &RegisterProfileDialog::loadGDBBtnClicked);\n    connect(ui->exportProfileBtn, &QPushButton::clicked, this,\n            &RegisterProfileDialog::exportProfileBtnClicked);\n}\n\nRegisterProfileDialog::~RegisterProfileDialog() = default;\n\nvoid RegisterProfileDialog::showContextMenu(const QPoint &pos)\n{\n    QListWidgetItem *item = ui->recentProfilesList->itemAt(pos);\n    QMenu menu(this);\n    if (item) {\n        menu.addAction(tr(\"Remove Item\"), this, &RegisterProfileDialog::removeItem);\n        menu.addSeparator();\n    }\n    menu.addAction(tr(\"Clear All\"), this, &RegisterProfileDialog::clearAll);\n    menu.exec(ui->recentProfilesList->mapToGlobal(pos));\n}\n\nvoid RegisterProfileDialog::removeItem()\n{\n    QListWidgetItem *item = ui->recentProfilesList->currentItem();\n    if (!item) {\n        return;\n    }\n\n    QString serializedData = item->data(Qt::UserRole).toString();\n    Config()->removeRecentRegProfile(serializedData);\n    delete ui->recentProfilesList->takeItem(ui->recentProfilesList->currentRow());\n}\n\nvoid RegisterProfileDialog::clearAll()\n{\n    ui->recentProfilesList->clear();\n    Config()->setRecentRegProfiles(QStringList());\n}\n\nvoid RegisterProfileDialog::fillProfilePaths(const QStringList &profilePaths)\n{\n    ui->recentProfilesList->clear();\n\n    for (const QString &p : profilePaths) {\n        bool isGdb = p.startsWith(\"gdb::\");\n        QString pathData = p.mid(p.indexOf(\"::\") + 2);\n\n        // Visible format:\n        // [Rizin/GDB] /path/to/file\n        // easier for user to remember if the opened file was a GDB profile or Rizin\n        QString displayText = QString(\"[%1] %2\").arg(isGdb ? \"GDB\" : \"Rizin\", pathData);\n\n        QListWidgetItem *item = new QListWidgetItem(displayText);\n        item->setData(Qt::UserRole, p);\n        ui->recentProfilesList->addItem(item);\n    }\n}\n\nvoid RegisterProfileDialog::loadProfileBtnClicked()\n{\n    QString filePath = QFileDialog::getOpenFileName(this, tr(\"Open Register Profile\"), QString(),\n                                                    tr(\"All Files (*)\"));\n    if (!filePath.isEmpty()) {\n        setFileContents(filePath);\n        loadedProfile = RegisterProfile::Rizin;\n    }\n}\n\nvoid RegisterProfileDialog::loadGDBBtnClicked()\n{\n    QString filePath = QFileDialog::getOpenFileName(this, tr(\"Open GDB Profile\"), QString(),\n                                                    tr(\"All Files (*)\"));\n    if (filePath.isEmpty()) {\n        return;\n    }\n\n    QString profile = Core()->convertGDBProfile(filePath);\n    if (profile.isEmpty()) {\n        return;\n    }\n\n    setProfileData(profile);\n    setProfilePath(filePath);\n    loadedProfile = RegisterProfile::GDB;\n}\n\nvoid RegisterProfileDialog::setFileContents(const QString &filePath)\n{\n    if (filePath.isEmpty()) {\n        return;\n    }\n\n    QFile file(filePath);\n    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {\n        QTextStream in(&file);\n        QString data = in.readAll();\n\n        setProfileData(data);\n        setProfilePath(filePath);\n        file.close();\n    } else {\n        showWarning(filePath);\n    }\n}\n\nvoid RegisterProfileDialog::showWarning(const QString &filePath)\n{\n    QMessageBox::warning(this, tr(\"Error\"), tr(\"Could not open file: %1\").arg(filePath));\n}\n\nvoid RegisterProfileDialog::editProfileBtnClicked()\n{\n    EditRegProfileDialog dialog(this);\n    dialog.setProfileData(profileData);\n    if (dialog.exec() == QDialog::Accepted) {\n        setProfileData(dialog.getProfleData());\n    }\n}\n\nvoid RegisterProfileDialog::setProfileData(const QString &data)\n{\n    profileData = data;\n}\n\nvoid RegisterProfileDialog::updateProfile(const QString &path, const QString &data)\n{\n    setProfilePath(path);\n    setProfileData(data);\n}\n\nQString RegisterProfileDialog::getProfileData() const\n{\n    return profileData;\n}\n\nRegisterProfile RegisterProfileDialog::getLoadedProfile() const\n{\n    return loadedProfile;\n}\n\nQString RegisterProfileDialog::getSerializedProfilePath() const\n{\n    // Saving format:\n    // rizin::/path/to/profile\n    // gdb::/path/to/profile\n    QString path = ui->profilePathEdit->text();\n    if (path.isEmpty()) {\n        return QString();\n    }\n    return (loadedProfile == RegisterProfile::GDB ? \"gdb::\" : \"rizin::\") + path;\n}\n\nvoid RegisterProfileDialog::itemClicked(QListWidgetItem *item)\n{\n    if (!item) {\n        return;\n    }\n\n    QString fullSerialized = item->data(Qt::UserRole).toString();\n    QString path = fullSerialized.mid(fullSerialized.indexOf(\"::\") + 2);\n\n    loadedProfile =\n            fullSerialized.startsWith(\"gdb::\") ? RegisterProfile::GDB : RegisterProfile::Rizin;\n\n    setFileContents(path);\n}\n\nvoid RegisterProfileDialog::exportProfileBtnClicked()\n{\n    if (profileData.isEmpty()) {\n        QMessageBox::information(this, tr(\"Export Profile\"), tr(\"The profile is empty.\"));\n        return;\n    }\n\n    QString filePath = QFileDialog::getSaveFileName(this, tr(\"Export Profile As\"), QString(),\n                                                    tr(\"All Files (*)\"));\n    if (filePath.isEmpty()) {\n        return;\n    }\n\n    QFile file(filePath);\n    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {\n        QTextStream out(&file);\n        out << profileData;\n        file.close();\n\n        setProfilePath(filePath);\n        loadedProfile = RegisterProfile::Rizin;\n    } else {\n        showWarning(filePath);\n    }\n}\n\nvoid RegisterProfileDialog::setProfilePath(const QString &path)\n{\n    ui->profilePathEdit->setText(path);\n}\n\nQString RegisterProfileDialog::getProfilePath() const\n{\n    return ui->profilePathEdit->text();\n}\n"
  },
  {
    "path": "src/dialogs/RegisterProfileDialog.h",
    "content": "#ifndef REGISTERPROFILEDIALOG_H\n#define REGISTERPROFILEDIALOG_H\n\n#include <QDialog>\n#include <QListWidgetItem>\n#include <memory>\n\nnamespace Ui {\nclass RegisterProfileDialog;\n}\n\nenum class RegisterProfile {\n    Default,\n    Rizin,\n    GDB,\n};\n\n/**\n * @brief Main dialog for selecting, loading, and exporting register profiles\n */\nclass RegisterProfileDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit RegisterProfileDialog(QWidget *parent = nullptr);\n    ~RegisterProfileDialog();\n\n    /**\n     * @brief Updates the path display in the UI\n     * @param path The file path string\n     */\n    void setProfilePath(const QString &path);\n\n    /**\n     * @brief Internal storage for the profile text.\n     * @param data The raw profile content.\n     */\n    void setProfileData(const QString &data);\n\n    /**\n     * @brief Retrieves the current profile content.\n     * @return Raw profile string.\n     */\n    QString getProfileData() const;\n\n    /**\n     * @brief Retrieves the current file path from the UI.\n     * @return Path string.\n     */\n    QString getProfilePath() const;\n\n    /**\n     * @brief Formats the path for configuration storage (e.g., \"type::path\").\n     * @return Serialized string.\n     */\n    QString getSerializedProfilePath() const;\n\n    /**\n     * @brief Populates the recent profiles list.\n     * @param profilePaths List of serialized profile strings.\n     */\n    void fillProfilePaths(const QStringList &profilePaths);\n\n    /**\n     * @brief Identifies which profile type is currently active.\n     * @return Active RegisterProfile enum.\n     */\n    RegisterProfile getLoadedProfile() const;\n\nprivate slots:\n    void loadProfileBtnClicked();\n    void loadGDBBtnClicked();\n    void editProfileBtnClicked();\n    void exportProfileBtnClicked();\n    void setFileContents(const QString &filePath);\n    void itemClicked(QListWidgetItem *item);\n\n    /**\n     * @brief Creates and shows the right-click context menu for the recent list.\n     * @param pos The position where the user clicked.\n     */\n    void showContextMenu(const QPoint &pos);\n\n    /**\n     * @brief Removes the selected item from the list and persistent settings.\n     */\n    void removeItem();\n\n    /**\n     * @brief Clears all items from the list and persistent settings.\n     */\n    void clearAll();\n\nprivate:\n    std::unique_ptr<Ui::RegisterProfileDialog> ui;\n    QString profileData;\n    RegisterProfile loadedProfile = RegisterProfile::Default;\n\n    void showWarning(const QString &filePath);\n    void updateProfile(const QString &path, const QString &data);\n};\n\n#endif\n"
  },
  {
    "path": "src/dialogs/RegisterProfileDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RegisterProfileDialog</class>\n <widget class=\"QDialog\" name=\"RegisterProfileDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>420</width>\n    <height>320</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Register Profile</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\" stretch=\"0,0,0,0,0,0\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"curProfileLayout\">\n     <item>\n      <widget class=\"QLineEdit\" name=\"profilePathEdit\">\n       <property name=\"readOnly\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"editProfileBtn\">\n       <property name=\"text\">\n        <string>Edit Profile</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n     <item>\n      <widget class=\"QPushButton\" name=\"loadGDBBtn\">\n       <property name=\"text\">\n        <string>Import GDB Profile</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"loadProfileBtn\">\n       <property name=\"text\">\n        <string>Load Profile</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"recentProfilesLabel\">\n     <property name=\"text\">\n      <string>Recent Profiles</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n     <item>\n      <widget class=\"QListWidget\" name=\"recentProfilesList\"/>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n     <item>\n      <widget class=\"QPushButton\" name=\"exportProfileBtn\">\n       <property name=\"text\">\n        <string>Export Profile</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"orientation\">\n        <enum>Qt::Orientation::Horizontal</enum>\n       </property>\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RegisterProfileDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RegisterProfileDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/RemoteDebugDialog.cpp",
    "content": "#include \"RemoteDebugDialog.h\"\n#include \"ui_RemoteDebugDialog.h\"\n\n#include <QHostAddress>\n#include <QFileInfo>\n#include <QMessageBox>\n#include <QSettings>\n\nenum DbgBackendType { GDB = 0, WINDBG = 1 };\n\nstruct DbgBackend\n{\n    DbgBackendType type;\n    QString name;\n    QString prefix;\n};\n\nstatic const QList<DbgBackend> dbgBackends = { { GDB, \"GDB\", \"gdb://\" },\n                                               { WINDBG, \"WinKd - Pipe\", \"winkd://\" } };\n\nRemoteDebugDialog::RemoteDebugDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::RemoteDebugDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    // Fill in debugger Combo\n    ui->debuggerCombo->clear();\n    for (auto &backend : dbgBackends) {\n        ui->debuggerCombo->addItem(backend.name);\n    }\n\n    // Fill ip list\n    fillRecentIpList();\n    ui->ipEdit->setFocus();\n\n    // Connect statements for right click action and itemClicked action\n    ui->recentsIpListWidget->addAction(ui->actionRemoveItem);\n    ui->recentsIpListWidget->addAction(ui->actionRemoveAll);\n    connect(ui->actionRemoveAll, &QAction::triggered, this, &RemoteDebugDialog::clearAll);\n    connect(ui->actionRemoveItem, &QAction::triggered, this, &RemoteDebugDialog::removeItem);\n    connect(ui->recentsIpListWidget, &QListWidget::itemClicked, this,\n            &RemoteDebugDialog::itemClicked);\n}\n\nRemoteDebugDialog::~RemoteDebugDialog() {}\n\nbool RemoteDebugDialog::validate()\n{\n    int debugger = getDebugger();\n    if (debugger == GDB) {\n        return validatePort() && validateIp();\n    } else if (debugger == WINDBG) {\n        return validatePath();\n    }\n    QMessageBox msgBox(this);\n    msgBox.setText(tr(\"Invalid debugger\"));\n    msgBox.exec();\n    return false;\n}\n\nbool RemoteDebugDialog::validateIp()\n{\n    QMessageBox msgBox(this);\n\n    QString ip = getIpOrPath();\n    if (QHostAddress(ip).isNull()) {\n        msgBox.setText(tr(\"Invalid IP address\"));\n        msgBox.exec();\n        return false;\n    }\n    return true;\n}\n\nbool RemoteDebugDialog::validatePath()\n{\n    QMessageBox msgBox(this);\n\n    QString path = getIpOrPath();\n    if (!QFileInfo(path).exists()) {\n        msgBox.setText(tr(\"Path does not exist\"));\n        msgBox.exec();\n        return false;\n    }\n    return true;\n}\n\nbool RemoteDebugDialog::validatePort()\n{\n    QMessageBox msgBox(this);\n\n    int port = getPort();\n    if (port < 1 || port > 65535) {\n        msgBox.setText(tr(\"Invalid port\"));\n        msgBox.exec();\n        return false;\n    }\n    return true;\n}\n\nvoid RemoteDebugDialog::on_buttonBox_accepted() {}\n\nvoid RemoteDebugDialog::on_buttonBox_rejected()\n{\n    close();\n}\n\nvoid RemoteDebugDialog::removeItem()\n{\n    QListWidgetItem *item = ui->recentsIpListWidget->currentItem();\n\n    if (item == nullptr)\n        return;\n\n    QVariant data = item->data(Qt::UserRole);\n    QString sitem = data.toString();\n\n    // Remove the item from recentIpList\n    QSettings settings;\n    QStringList ips = settings.value(\"recentIpList\").toStringList();\n    ips.removeAll(sitem);\n    settings.setValue(\"recentIpList\", ips);\n\n    // Also remove the line from list\n    ui->recentsIpListWidget->takeItem(ui->recentsIpListWidget->currentRow());\n    checkIfEmpty();\n}\n\nvoid RemoteDebugDialog::clearAll()\n{\n    QSettings settings;\n    ui->recentsIpListWidget->clear();\n\n    QStringList ips = settings.value(\"recentIpList\").toStringList();\n    ips.clear();\n    settings.setValue(\"recentIpList\", ips);\n\n    checkIfEmpty();\n}\n\nvoid RemoteDebugDialog::fillFormData(QString formdata)\n{\n    QString ipText = \"\";\n    QString portText = \"\";\n    const DbgBackend *backend = nullptr;\n    for (auto &back : dbgBackends) {\n        if (formdata.startsWith(back.prefix)) {\n            backend = &back;\n        }\n    }\n    if (!backend) {\n        return;\n    }\n\n    if (backend->type == GDB) {\n        // Format is | prefix | IP | : | PORT |\n        int lastColon = formdata.lastIndexOf(QString(\":\"));\n        portText = formdata.mid(lastColon + 1, formdata.length());\n        ipText = formdata.mid(backend->prefix.length(), lastColon - backend->prefix.length());\n    } else if (backend->type == WINDBG) {\n        // Format is | prefix | PATH |\n        ipText = formdata.mid(backend->prefix.length());\n    }\n    ui->debuggerCombo->setCurrentText(backend->name);\n    ui->ipEdit->setText(ipText);\n    ui->portEdit->setText(portText);\n}\n\nQString RemoteDebugDialog::getUri() const\n{\n    int debugger = getDebugger();\n    if (debugger == WINDBG) {\n        return QString(\"%1%2\").arg(dbgBackends[WINDBG].prefix, getIpOrPath());\n    } else if (debugger == GDB) {\n        return QString(\"%1%2:%3\").arg(dbgBackends[GDB].prefix, getIpOrPath(),\n                                      QString::number(getPort()));\n    }\n    return \"- uri error\";\n}\n\nbool RemoteDebugDialog::fillRecentIpList()\n{\n    QSettings settings;\n\n    // Fetch recentIpList\n    QStringList ips = settings.value(\"recentIpList\").toStringList();\n    QMutableListIterator<QString> it(ips);\n    while (it.hasNext()) {\n        const QString ip = it.next();\n        const QString text = QString(\"%1\").arg(ip);\n        QListWidgetItem *item = new QListWidgetItem(text);\n        item->setData(Qt::UserRole, ip);\n        // Fill recentsIpListWidget\n        ui->recentsIpListWidget->addItem(item);\n    }\n\n    if (!ips.isEmpty()) {\n        fillFormData(ips[0]);\n    }\n\n    checkIfEmpty();\n\n    return !ips.isEmpty();\n}\n\nvoid RemoteDebugDialog::checkIfEmpty()\n{\n    QSettings settings;\n    QStringList ips = settings.value(\"recentIpList\").toStringList();\n\n    if (ips.isEmpty()) {\n        ui->recentsIpListWidget->setVisible(false);\n        ui->line->setVisible(false);\n    } else {\n        // TODO: Find a way to make the list widget not to high\n    }\n}\n\nvoid RemoteDebugDialog::itemClicked(QListWidgetItem *item)\n{\n    QVariant data = item->data(Qt::UserRole);\n    QString ipport = data.toString();\n    fillFormData(ipport);\n}\n\nQString RemoteDebugDialog::getIpOrPath() const\n{\n    return ui->ipEdit->text();\n}\n\nint RemoteDebugDialog::getPort() const\n{\n    return ui->portEdit->text().toInt();\n}\n\nint RemoteDebugDialog::getDebugger() const\n{\n    return ui->debuggerCombo->currentIndex();\n}\n"
  },
  {
    "path": "src/dialogs/RemoteDebugDialog.h",
    "content": "#ifndef REMOTEDEBUGDIALOG_H\n#define REMOTEDEBUGDIALOG_H\n\n#include <QDialog>\n#include <QListWidgetItem>\n#include <memory>\n\nnamespace Ui {\nclass RemoteDebugDialog;\n}\n\n/**\n * @brief Dialog for connecting to remote debuggers\n */\nclass RemoteDebugDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit RemoteDebugDialog(QWidget *parent = nullptr);\n    ~RemoteDebugDialog();\n\n    /**\n     * @brief Generate a URI for given UI input\n     */\n    QString getUri() const;\n    bool validate();\n\nprivate slots:\n    void on_buttonBox_accepted();\n    void on_buttonBox_rejected();\n\n    /**\n     * @brief Clears the list of recent connections.\n     * Triggers when you right click and click on \"Clear All\" in remote debug dialog.\n     */\n    void clearAll();\n\n    /**\n     * @brief Clears the selected item in the list of recent connections.\n     * Triggers when you right click and click on \"Remove Item\" in remote debug dialog.\n     */\n    void removeItem();\n\n    /**\n     * @brief Fills the form with the selected item's data.\n     */\n    void itemClicked(QListWidgetItem *item);\n\nprivate:\n    std::unique_ptr<Ui::RemoteDebugDialog> ui;\n    int getPort() const;\n    int getDebugger() const;\n    QString getIpOrPath() const;\n\n    bool validatePath();\n    bool validateIp();\n    bool validatePort();\n\n    /**\n     * @brief Fills recent remote connections.\n     */\n    bool fillRecentIpList();\n\n    /**\n     * @brief Fills the remote debug dialog form from a given URI\n     * Eg: gdb://127.0.0.1:8754 or windbg:///tmp/abcd\n     */\n    void fillFormData(QString formdata);\n\n    /**\n     * @brief Checks if the recent connection list is empty or and hide/unhide the table view\n     */\n    void checkIfEmpty();\n};\n\n#endif // REMOTE_DEBUG_DIALOG\n"
  },
  {
    "path": "src/dialogs/RemoteDebugDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RemoteDebugDialog</class>\n <widget class=\"QDialog\" name=\"RemoteDebugDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>373</width>\n    <height>204</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Remote debugging configuration</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetMinimumSize</enum>\n   </property>\n   <item>\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"debugText\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Debugger:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"ipEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>382</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"inputMask\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"text\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"frame\">\n        <bool>false</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string>127.0.0.1 or /tmp/win.sock</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"debuggerCombo\"/>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"portText\">\n       <property name=\"text\">\n        <string>Port:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"ipText\">\n       <property name=\"text\">\n        <string>IP or Path:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"portEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>382</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"inputMask\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"text\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"frame\">\n        <bool>false</bool>\n       </property>\n       <property name=\"placeholderText\">\n        <string>Enter Port</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <item>\n      <widget class=\"Line\" name=\"line\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QListWidget\" name=\"recentsIpListWidget\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"MinimumExpanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>0</width>\n         <height>60</height>\n        </size>\n       </property>\n       <property name=\"contextMenuPolicy\">\n        <enum>Qt::ActionsContextMenu</enum>\n       </property>\n       <property name=\"selectionRectVisible\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n  <action name=\"actionRemoveItem\">\n   <property name=\"text\">\n    <string>Remove item</string>\n   </property>\n  </action>\n  <action name=\"actionRemoveAll\">\n   <property name=\"text\">\n    <string>Remove all</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Remove all</string>\n   </property>\n  </action>\n </widget>\n <tabstops>\n  <tabstop>debuggerCombo</tabstop>\n  <tabstop>ipEdit</tabstop>\n  <tabstop>portEdit</tabstop>\n  <tabstop>recentsIpListWidget</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RemoteDebugDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>234</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RemoteDebugDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>240</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>254</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/RizinPluginsDialog.cpp",
    "content": "#include \"RizinPluginsDialog.h\"\n#include \"ui_RizinPluginsDialog.h\"\n\n#include \"core/Cutter.h\"\n#include \"common/Helpers.h\"\n#include \"plugins/PluginManager.h\"\n\nRizinPluginsDialog::RizinPluginsDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::RizinPluginsDialog)\n{\n    ui->setupUi(this);\n\n    for (const auto &plugin : Core()->getBinPluginDescriptions()) {\n        QTreeWidgetItem *item = new QTreeWidgetItem();\n        item->setText(0, plugin.name);\n        item->setText(1, plugin.description);\n        item->setText(2, plugin.license);\n        item->setText(3, plugin.type);\n        ui->RzBinTreeWidget->addTopLevelItem(item);\n    }\n    ui->RzBinTreeWidget->sortByColumn(0, Qt::AscendingOrder);\n    qhelpers::adjustColumns(ui->RzBinTreeWidget, 0);\n\n    for (const auto &plugin : Core()->getRIOPluginDescriptions()) {\n        QTreeWidgetItem *item = new QTreeWidgetItem();\n        item->setText(0, plugin.name);\n        item->setText(1, plugin.description);\n        item->setText(2, plugin.license);\n        item->setText(3, plugin.permissions);\n        ui->RzIOTreeWidget->addTopLevelItem(item);\n    }\n    ui->RzIOTreeWidget->sortByColumn(0, Qt::AscendingOrder);\n    qhelpers::adjustColumns(ui->RzIOTreeWidget, 0);\n\n    for (const auto &plugin : Core()->getRCorePluginDescriptions()) {\n        QTreeWidgetItem *item = new QTreeWidgetItem();\n        item->setText(0, plugin.name);\n        item->setText(1, plugin.description);\n        item->setText(2, plugin.license);\n        ui->RzCoreTreeWidget->addTopLevelItem(item);\n    }\n    ui->RzCoreTreeWidget->sortByColumn(0, Qt::AscendingOrder);\n    qhelpers::adjustColumns(ui->RzCoreTreeWidget, 0);\n\n    for (const auto &plugin : Core()->getRAsmPluginDescriptions()) {\n        QTreeWidgetItem *item = new QTreeWidgetItem();\n        item->setText(0, plugin.name);\n        item->setText(1, plugin.architecture);\n        item->setText(2, plugin.cpus);\n        item->setText(3, plugin.version);\n        item->setText(4, plugin.description);\n        item->setText(5, plugin.license);\n        item->setText(6, plugin.author);\n        ui->RzAsmTreeWidget->addTopLevelItem(item);\n    }\n    ui->RzAsmTreeWidget->sortByColumn(0, Qt::AscendingOrder);\n    qhelpers::adjustColumns(ui->RzAsmTreeWidget, 0);\n}\n\nRizinPluginsDialog::~RizinPluginsDialog()\n{\n    delete ui;\n}\n"
  },
  {
    "path": "src/dialogs/RizinPluginsDialog.h",
    "content": "#ifndef PLUGINSDIALOG_H\n#define PLUGINSDIALOG_H\n\n#include <QDialog>\n#include <QAbstractTableModel>\n\n#include \"core/Cutter.h\"\n\nnamespace Ui {\nclass RizinPluginsDialog;\n}\n\nclass RizinPluginsDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit RizinPluginsDialog(QWidget *parent = nullptr);\n    ~RizinPluginsDialog();\n\nprivate:\n    Ui::RizinPluginsDialog *ui;\n};\n\n#endif // PLUGINSDIALOG_H\n"
  },
  {
    "path": "src/dialogs/RizinPluginsDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RizinPluginsDialog</class>\n <widget class=\"QDialog\" name=\"RizinPluginsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>618</width>\n    <height>642</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Rizin plugin information</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_1\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>4</number>\n     </property>\n     <widget class=\"QWidget\" name=\"RzBinTab\">\n      <attribute name=\"title\">\n       <string>RzBin</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"QLabel\" name=\"RzBinLabel\">\n         <property name=\"text\">\n          <string>RzBin plugins</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QTreeWidget\" name=\"RzBinTreeWidget\">\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n         <column>\n          <property name=\"text\">\n           <string>Name</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Description</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>License</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Type</string>\n          </property>\n         </column>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"RzIOTab\">\n      <attribute name=\"title\">\n       <string>RzIO</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n       <item>\n        <widget class=\"QLabel\" name=\"RzIOLabel\">\n         <property name=\"text\">\n          <string>RzIO plugins</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QTreeWidget\" name=\"RzIOTreeWidget\">\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n         <column>\n          <property name=\"text\">\n           <string>Name</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Description</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>License</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Permissions</string>\n          </property>\n         </column>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"RzCoreTab\">\n      <attribute name=\"title\">\n       <string>RzCore</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n       <item>\n        <widget class=\"QLabel\" name=\"RzCoreLabel\">\n         <property name=\"text\">\n          <string>RzCore plugins</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QTreeWidget\" name=\"RzCoreTreeWidget\">\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n         <column>\n          <property name=\"text\">\n           <string>Name</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Description</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>License</string>\n          </property>\n         </column>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"RzAsmTab\">\n      <attribute name=\"title\">\n       <string>RzAsm</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n       <item>\n        <widget class=\"QLabel\" name=\"RzAsmLabel\">\n         <property name=\"text\">\n          <string>RzAsm plugins</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QTreeWidget\" name=\"RzAsmTreeWidget\">\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n         <column>\n          <property name=\"text\">\n           <string>Name</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Architecture</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>CPU's</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Version</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Description</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>License</string>\n          </property>\n         </column>\n         <column>\n          <property name=\"text\">\n           <string>Author</string>\n          </property>\n         </column>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RizinPluginsDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/RizinTaskDialog.cpp",
    "content": "#include \"RizinTaskDialog.h\"\n#include \"common/RizinTask.h\"\n\n#include <QCloseEvent>\n\n#include \"ui_RizinTaskDialog.h\"\n\nRizinTaskDialog::RizinTaskDialog(RizinTask::Ptr task, QWidget *parent)\n    : QDialog(parent), ui(new Ui::RizinTaskDialog), task(task)\n{\n    ui->setupUi(this);\n\n    connect(task.data(), &RizinTask::finished, this, [this]() { close(); });\n\n    connect(&timer, &QTimer::timeout, this, &RizinTaskDialog::updateProgressTimer);\n    timer.setInterval(1000);\n    timer.setSingleShot(false);\n    timer.start();\n\n    elapsedTimer.start();\n    updateProgressTimer();\n}\n\nRizinTaskDialog::~RizinTaskDialog() {}\n\nvoid RizinTaskDialog::updateProgressTimer()\n{\n    int secondsElapsed = elapsedTimer.elapsed() / 1000;\n    int minutesElapsed = secondsElapsed / 60;\n    int hoursElapsed = minutesElapsed / 60;\n\n    QString label;\n    if (hoursElapsed) {\n        label += tr(\"%n hours\", nullptr, hoursElapsed);\n        label += \" \";\n    }\n    if (minutesElapsed) {\n        label += tr(\"%n minutes\", nullptr, minutesElapsed % 60);\n        label += \" \";\n    }\n    label += tr(\"%n seconds\", nullptr, secondsElapsed % 60);\n    ui->timeLabel->setText(tr(\"Running for %1\", \"time\").arg(label));\n}\n\nvoid RizinTaskDialog::setDesc(const QString &label)\n{\n    ui->descLabel->setText(label);\n}\n\nvoid RizinTaskDialog::closeEvent(QCloseEvent *event)\n{\n    if (breakOnClose) {\n        task->breakTask();\n        setDesc(tr(\"Attempting to stop the task...\"));\n        event->ignore();\n    } else {\n        QWidget::closeEvent(event);\n    }\n}\n\nvoid RizinTaskDialog::reject()\n{\n    task->breakTask();\n    setDesc(tr(\"Attempting to stop the task...\"));\n}\n"
  },
  {
    "path": "src/dialogs/RizinTaskDialog.h",
    "content": "#ifndef RZTASKDIALOG_H\n#define RZTASKDIALOG_H\n\n#include <memory>\n\n#include <QDialog>\n#include <QTimer>\n#include <QElapsedTimer>\n\n#include \"common/RizinTask.h\"\n#include \"core/CutterCommon.h\"\n\nclass RizinTask;\nnamespace Ui {\nclass RizinTaskDialog;\n}\n\nclass CUTTER_EXPORT RizinTaskDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    using Ptr = QSharedPointer<RizinTask>;\n    RizinTaskDialog(Ptr task, QWidget *parent = nullptr);\n    ~RizinTaskDialog();\n\n    void setBreakOnClose(bool v) { breakOnClose = v; }\n    bool getBreakOnClose() { return breakOnClose; }\n    void setDesc(const QString &label);\n\npublic slots:\n    void reject() override;\n\nprivate slots:\n    void updateProgressTimer();\n\nprotected:\n    void closeEvent(QCloseEvent *event) override;\n\nprivate:\n    std::unique_ptr<Ui::RizinTaskDialog> ui;\n    QSharedPointer<RizinTask> task;\n\n    QTimer timer;\n    QElapsedTimer elapsedTimer;\n\n    bool breakOnClose = false;\n};\n\n#endif // RZTASKDIALOG_H\n"
  },
  {
    "path": "src/dialogs/RizinTaskDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RizinTaskDialog</class>\n <widget class=\"QDialog\" name=\"RizinTaskDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>87</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Rizin Task</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"descLabel\">\n     <property name=\"text\">\n      <string>Rizin task in progress..</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"timeLabel\">\n     <property name=\"text\">\n      <string>Time</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QProgressBar\" name=\"progressBar\">\n     <property name=\"maximum\">\n      <number>0</number>\n     </property>\n     <property name=\"textVisible\">\n      <bool>false</bool>\n     </property>\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RizinTaskDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RizinTaskDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/SetToDataDialog.cpp",
    "content": "#include \"SetToDataDialog.h\"\n#include \"ui_SetToDataDialog.h\"\n#include <QIntValidator>\n\nSetToDataDialog::SetToDataDialog(RVA startAddr, QWidget *parent)\n    : QDialog(parent), ui(new Ui::SetToDataDialog), startAddress(startAddr)\n{\n    ui->setupUi(this);\n    auto validator = new QIntValidator(this);\n    validator->setBottom(1);\n    ui->sizeEdit->setValidator(validator);\n    ui->repeatEdit->setValidator(validator);\n    ui->startAddrLabel->setText(RzAddressString(startAddr));\n    updateEndAddress();\n}\n\nSetToDataDialog::~SetToDataDialog()\n{\n    delete ui;\n}\n\nint SetToDataDialog::getItemSize()\n{\n    return ui->sizeEdit->text().toInt();\n}\n\nint SetToDataDialog::getItemCount()\n{\n    return ui->repeatEdit->text().toInt();\n}\n\nvoid SetToDataDialog::updateEndAddress()\n{\n    RVA endAddr = startAddress + (getItemSize() * getItemCount());\n    ui->endAddrLabel->setText(RzAddressString(endAddr));\n}\n\nvoid SetToDataDialog::on_sizeEdit_textChanged(const QString &arg1)\n{\n    Q_UNUSED(arg1);\n    updateEndAddress();\n}\n\nvoid SetToDataDialog::on_repeatEdit_textChanged(const QString &arg1)\n{\n    Q_UNUSED(arg1);\n    updateEndAddress();\n}\n"
  },
  {
    "path": "src/dialogs/SetToDataDialog.h",
    "content": "#ifndef SETTODATADIALOG_H\n#define SETTODATADIALOG_H\n\n#include <QDialog>\n#include \"core/Cutter.h\"\n\nnamespace Ui {\nclass SetToDataDialog;\n}\n\nclass SetToDataDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit SetToDataDialog(RVA startAddr, QWidget *parent = nullptr);\n    ~SetToDataDialog();\n\n    int getItemSize();\n    int getItemCount();\n\nprivate slots:\n    void on_sizeEdit_textChanged(const QString &arg1);\n    void on_repeatEdit_textChanged(const QString &arg1);\n\nprivate:\n    void updateEndAddress();\n\n    Ui::SetToDataDialog *ui;\n    RVA startAddress;\n};\n\n#endif // SETTODATADIALOG_H\n"
  },
  {
    "path": "src/dialogs/SetToDataDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SetToDataDialog</class>\n <widget class=\"QDialog\" name=\"SetToDataDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>273</width>\n    <height>197</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Set to Data</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"0\" column=\"1\">\n    <widget class=\"QLabel\" name=\"startAddrLabel\">\n     <property name=\"text\">\n      <string>???</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Start address</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"1\">\n    <widget class=\"QLabel\" name=\"endAddrLabel\">\n     <property name=\"text\">\n      <string>???</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label_3\">\n     <property name=\"text\">\n      <string>End address</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label_5\">\n     <property name=\"text\">\n      <string>Item size</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label_6\">\n     <property name=\"text\">\n      <string>Number of items</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"1\">\n    <widget class=\"QLineEdit\" name=\"repeatEdit\">\n     <property name=\"text\">\n      <string>1</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"1\">\n    <widget class=\"QLineEdit\" name=\"sizeEdit\">\n     <property name=\"text\">\n      <string>1</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"4\" column=\"0\" colspan=\"2\">\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>sizeEdit</tabstop>\n  <tabstop>repeatEdit</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>SetToDataDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>SetToDataDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/TypesInteractionDialog.cpp",
    "content": "#include \"dialogs/TypesInteractionDialog.h\"\n#include \"ui_TypesInteractionDialog.h\"\n\n#include \"core/Cutter.h\"\n#include \"common/Configuration.h\"\n#include \"common/SyntaxHighlighter.h\"\n#include \"widgets/TypesWidget.h\"\n\n#include <QFileDialog>\n#include <QTemporaryFile>\n\nTypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly)\n    : QDialog(parent), ui(new Ui::TypesInteractionDialog)\n{\n    ui->setupUi(this);\n    QFont font = Config()->getBaseFont();\n    ui->plainTextEdit->setFont(font);\n    ui->plainTextEdit->setPlainText(\"\");\n#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)\n    ui->plainTextEdit->setTabStopDistance(4 * QFontMetrics(font).horizontalAdvance(' '));\n#endif\n    syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document());\n    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);\n    ui->plainTextEdit->setReadOnly(readOnly);\n}\n\nTypesInteractionDialog::~TypesInteractionDialog() {}\n\nvoid TypesInteractionDialog::setTypeName(QString name)\n{\n    this->typeName = name;\n}\n\nvoid TypesInteractionDialog::on_selectFileButton_clicked()\n{\n    QString filename =\n            QFileDialog::getOpenFileName(this, tr(\"Select file\"), Config()->getRecentFolder(),\n                                         \"Header files (*.h *.hpp);;All files (*)\");\n    if (filename.isEmpty()) {\n        return;\n    }\n    Config()->setRecentFolder(QFileInfo(filename).absolutePath());\n    QFile file(filename);\n    if (!file.open(QIODevice::ReadOnly)) {\n        QMessageBox::critical(this, tr(\"Error\"), file.errorString());\n        on_selectFileButton_clicked();\n        return;\n    }\n    ui->filenameLineEdit->setText(filename);\n    ui->plainTextEdit->setPlainText(file.readAll());\n}\n\nvoid TypesInteractionDialog::on_plainTextEdit_textChanged()\n{\n    if (ui->plainTextEdit->toPlainText().isEmpty()) {\n        ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);\n    } else {\n        ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);\n    }\n}\n\nvoid TypesInteractionDialog::done(int r)\n{\n    if (r == QDialog::Accepted) {\n        RzCoreLocked core(Core());\n        bool success;\n        if (!typeName.isEmpty()) {\n            success = rz_type_db_edit_base_type(\n                    core->analysis->typedb, this->typeName.toUtf8().constData(),\n                    ui->plainTextEdit->toPlainText().toUtf8().constData());\n        } else {\n            char *error_msg = NULL;\n            success = rz_type_parse_string_stateless(\n                              core->analysis->typedb->parser,\n                              ui->plainTextEdit->toPlainText().toUtf8().constData(), &error_msg)\n                    == 0;\n            if (error_msg) {\n                RZ_LOG_ERROR(\"%s\\n\", error_msg);\n                rz_mem_free(error_msg);\n            }\n        }\n        if (success) {\n            emit newTypesLoaded();\n            QDialog::done(r);\n            return;\n        }\n\n        QMessageBox popup(this);\n        popup.setWindowTitle(tr(\"Error\"));\n        popup.setText(tr(\"There was some error while loading new types\"));\n        popup.setStandardButtons(QMessageBox::Ok);\n        popup.exec();\n    } else {\n        QDialog::done(r);\n    }\n}\n\nvoid TypesInteractionDialog::fillTextArea(QString content)\n{\n    ui->layoutWidget->hide();\n    ui->plainTextEdit->setPlainText(content);\n}\n"
  },
  {
    "path": "src/dialogs/TypesInteractionDialog.h",
    "content": "#ifndef TYPESINTERACTIONDIALOG_H\n#define TYPESINTERACTIONDIALOG_H\n\n#include <QDialog>\n#include <memory>\n\nnamespace Ui {\nclass TypesInteractionDialog;\n}\n\nclass QSyntaxHighlighter;\n\nclass TypesInteractionDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit TypesInteractionDialog(QWidget *parent = nullptr, bool readOnly = false);\n    ~TypesInteractionDialog();\n    /**\n     * @brief Fill the Dialog's TextEdit object with the content\n     * passed to the function. The content will most likely be a C\n     * representation of a Type.\n     * @param content - The content which should be in the TextEdit object.\n     * most likely will be a C representation of a Type.\n     * @param readonly - Will be set as \"true\" for viewing mode\n     */\n    void fillTextArea(QString content);\n\n    /**\n     * @brief Set the name of the type that is going to be changed\n     * @param name - name of the type\n     */\n    void setTypeName(QString name);\n\nprivate slots:\n    /**\n     * @brief Executed when the user clicks the selectFileButton\n     * Opens a File Dialog from where the user can select a file from where\n     * the types will be loaded.\n     */\n    void on_selectFileButton_clicked();\n\n    /**\n     * @brief Executed whenever the text inside the textbox changes\n     * When the text box is empty, the OK button is disabled.\n     */\n    void on_plainTextEdit_textChanged();\n\n    /**\n     * @brief done Closes the dialog and sets its result code to r\n     * @param r The value which will be returned by exec()\n     */\n    void done(int r) override;\n\nprivate:\n    std::unique_ptr<Ui::TypesInteractionDialog> ui;\n    QSyntaxHighlighter *syntaxHighLighter;\n    QString typeName;\n\nsignals:\n    /**\n     * @brief Emitted when new types are loaded\n     */\n    void newTypesLoaded();\n};\n\n#endif // TYPESINTERACTIONDIALOG_H\n"
  },
  {
    "path": "src/dialogs/TypesInteractionDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>TypesInteractionDialog</class>\n <widget class=\"QDialog\" name=\"TypesInteractionDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QWidget\" name=\"layoutWidget\" native=\"true\">\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n      <item>\n       <widget class=\"QLabel\" name=\"filenameLabel\">\n        <property name=\"text\">\n         <string>Load From File:</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLineEdit\" name=\"filenameLineEdit\">\n        <property name=\"readOnly\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QPushButton\" name=\"selectFileButton\">\n        <property name=\"text\">\n         <string>Select File</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPlainTextEdit\" name=\"plainTextEdit\">\n     <property name=\"plainText\">\n      <string/>\n     </property>\n     <property name=\"placeholderText\">\n      <string>Enter Types Manually</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>TypesInteractionDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>TypesInteractionDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/TypesVariablesDialog.cpp",
    "content": "#include \"TypesVariablesDialog.h\"\n#include \"ui_TypesVariablesDialog.h\"\n\nQString toString(VariableScope scope)\n{\n    switch (scope) {\n    case VariableScope::ALL:\n        return TypesVariablesDialog::tr(\"ALL\");\n    case VariableScope::GLOBAL:\n        return TypesVariablesDialog::tr(\"GLOBAL\");\n    case VariableScope::LOCAL:\n        return TypesVariablesDialog::tr(\"LOCAL\");\n    default:\n        return QString();\n    }\n}\n\nTypesVariablesModel::TypesVariablesModel(QObject *parent) : QAbstractTableModel(parent) {}\n\nint TypesVariablesModel::rowCount(const QModelIndex &) const\n{\n    return variables.count();\n}\n\nint TypesVariablesModel::columnCount(const QModelIndex &) const\n{\n    return Column::COUNT;\n}\n\nQVariant TypesVariablesModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid() || index.row() >= variables.size()) {\n        return QVariant();\n    }\n\n    const VariableEntry &v = variables.at(index.row());\n\n    if (role == Qt::DisplayRole) {\n        switch (index.column()) {\n        case Column::NAME:\n            return v.name;\n        case Column::ADDRESS:\n            return v.address;\n        case Column::SCOPE:\n            return toString(v.scope);\n        case Column::FUNCTION:\n            return v.function;\n        default:\n            return QVariant();\n        }\n    }\n\n    if (role == TypeVariableRole) {\n        return QVariant::fromValue(v);\n    }\n    return QVariant();\n}\n\nQVariant TypesVariablesModel::headerData(int section, Qt::Orientation, int role) const\n{\n    if (role == Qt::DisplayRole) {\n        switch (section) {\n        case Column::NAME:\n            return tr(\"Name\");\n        case Column::ADDRESS:\n            return tr(\"Address\");\n        case Column::SCOPE:\n            return tr(\"Scope\");\n        case Column::FUNCTION:\n            return tr(\"Function\");\n        default:\n            return QVariant();\n        }\n    }\n    return QVariant();\n}\n\nvoid TypesVariablesModel::updateVariables(const QList<VariableEntry> &newVars)\n{\n    beginResetModel();\n    variables = newVars;\n    endResetModel();\n}\n\nTypesVariablesProxyModel::TypesVariablesProxyModel(QObject *parent) : QSortFilterProxyModel(parent)\n{\n}\n\nvoid TypesVariablesProxyModel::setScope(VariableScope scope)\n{\n    selectedScope = scope;\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    invalidateFilter();\n#else\n    invalidateRowsFilter();\n#endif\n}\n\nbool TypesVariablesProxyModel::filterAcceptsRow(int source_row,\n                                                const QModelIndex &source_parent) const\n{\n    if (selectedScope == VariableScope::ALL) {\n        return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);\n    }\n\n    QVariant var = sourceModel()->data(sourceModel()->index(source_row, 0),\n                                       TypesVariablesModel::TypeVariableRole);\n    if (!var.isValid()) {\n        return false;\n    }\n\n    VariableEntry v = var.value<VariableEntry>();\n    return (v.scope == selectedScope)\n            && QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);\n}\n\nbool TypesVariablesProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    if (left.column() == TypesVariablesModel::Column::ADDRESS) {\n        QVariant varLeft = sourceModel()->data(left, TypesVariablesModel::TypeVariableRole);\n        QVariant varRight = sourceModel()->data(right, TypesVariablesModel::TypeVariableRole);\n\n        if (varLeft.isValid() && varRight.isValid()) {\n            VariableEntry vLeft = varLeft.value<VariableEntry>();\n            VariableEntry vRight = varRight.value<VariableEntry>();\n\n            return vLeft.offset < vRight.offset;\n        }\n    }\n\n    return QSortFilterProxyModel::lessThan(left, right);\n}\n\nTypesVariablesDialog::TypesVariablesDialog(QWidget *parent, const QString &typeName)\n    : QDialog(parent), ui(new Ui::TypesVariablesDialog), tree(new CutterTreeWidget(this))\n{\n    ui->setupUi(this);\n    setWindowTitle(tr(\"Variables: %1\").arg(typeName));\n\n    tree->addStatusBar(ui->verticalLayout);\n\n    sourceModel = new TypesVariablesModel(this);\n    proxyModel = new TypesVariablesProxyModel(this);\n    proxyModel->setSourceModel(sourceModel);\n\n    proxyModel->setFilterKeyColumn(-1);\n    proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);\n\n    ui->treeView->setModel(proxyModel);\n    ui->treeView->setSortingEnabled(true);\n    ui->treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);\n    ui->treeView->header()->setSectionResizeMode(QHeaderView::Interactive);\n    ui->treeView->header()->setStretchLastSection(true);\n\n    ui->quickFilterView->setLabelText(tr(\"Scope\"));\n    QComboBox *scopeCombo = ui->quickFilterView->comboBox();\n    scopeCombo->addItem(tr(\"All\"), ALL);\n    scopeCombo->addItem(tr(\"Globals Only\"), GLOBAL);\n    scopeCombo->addItem(tr(\"Locals Only\"), LOCAL);\n\n    auto updateCount = [this]() { tree->showItemsNumber(proxyModel->rowCount()); };\n\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, proxyModel,\n            &TypesVariablesProxyModel::setFilterFixedString);\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, this, updateCount);\n\n    connect(scopeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,\n            [this, scopeCombo, updateCount](int index) {\n                int scope = scopeCombo->itemData(index).toInt();\n                proxyModel->setScope(static_cast<VariableScope>(scope));\n                updateCount();\n            });\n\n    connect(ui->treeView, &QTreeView::doubleClicked, this,\n            &TypesVariablesDialog::onItemDoubleClicked);\n\n    refreshModel(typeName);\n    updateCount();\n}\n\nvoid TypesVariablesDialog::refreshModel(const QString &typeName)\n{\n    QList<VariableEntry> newVariables;\n\n    if (!typeName.isEmpty()) {\n        const auto &globals = Core()->getAllGlobals();\n        for (const auto &g : globals) {\n            if (g.type == typeName) {\n                newVariables.append(VariableEntry { g.name, QString(\"0x%1\").arg(g.addr, 0, 16),\n                                                    VariableScope::GLOBAL, \"\", g.addr });\n            }\n        }\n\n        const auto &fcns = Core()->getAllFunctions();\n        for (const auto &f : fcns) {\n            const auto &vars = Core()->getVariables(f.offset);\n            for (const auto &v : vars) {\n                if (v.type == typeName) {\n                    newVariables.append(VariableEntry { v.name, v.value, VariableScope::LOCAL,\n                                                        f.name, f.offset });\n                }\n            }\n        }\n    }\n\n    sourceModel->updateVariables(newVariables);\n    qhelpers::adjustColumns(ui->treeView, TypesVariablesModel::Column::COUNT, 0);\n}\n\nvoid TypesVariablesDialog::onItemDoubleClicked(const QModelIndex &index)\n{\n    if (!index.isValid()) {\n        return;\n    }\n    VariableEntry v = index.data(TypesVariablesModel::TypeVariableRole).value<VariableEntry>();\n    Core()->seekAndShow(v.offset);\n    close();\n}\n\nTypesVariablesDialog::~TypesVariablesDialog() {}\n"
  },
  {
    "path": "src/dialogs/TypesVariablesDialog.h",
    "content": "#ifndef TYPESVARIABLESDIALOG_H\n#define TYPESVARIABLESDIALOG_H\n\n#include <QDialog>\n#include <QSortFilterProxyModel>\n#include <QAbstractTableModel>\n#include \"CutterTreeWidget.h\"\n\n#include \"core/Cutter.h\"\n\nnamespace Ui {\nclass TypesVariablesDialog;\n}\n\nenum VariableScope { ALL = 0, GLOBAL, LOCAL };\n/**\n * @brief Get a string representation of the given Scope.\n * @param scope The scope to convert.\n * @return A translated QString (e.g., \"GLOBAL\", \"LOCAL\").\n */\nQString toString(VariableScope scope);\n\nstruct VariableEntry\n{\n    QString name;\n    QString address;\n    VariableScope scope;\n    QString function; ///< Name of the function containing the variable (if local)\n    RVA offset; ///< Offset for navigation (Function start for locals, absolute address for globals)\n};\n\nQ_DECLARE_METATYPE(VariableEntry)\n\nclass TypesVariablesModel : public QAbstractTableModel\n{\n    Q_OBJECT\n\npublic:\n    enum Column { NAME = 0, ADDRESS, SCOPE, FUNCTION, COUNT };\n    static const int TypeVariableRole = Qt::UserRole;\n\n    explicit TypesVariablesModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    void updateVariables(const QList<VariableEntry> &newVars);\n    QList<VariableEntry> variables;\n};\n\nclass TypesVariablesProxyModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    explicit TypesVariablesProxyModel(QObject *parent = nullptr);\n    void setScope(VariableScope scope);\n\nprotected:\n    bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n\nprivate:\n    VariableScope selectedScope = VariableScope::ALL;\n};\n\nclass TypesVariablesDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit TypesVariablesDialog(QWidget *parent = nullptr, const QString &typeName = \"\");\n    ~TypesVariablesDialog();\n\nprivate slots:\n    /**\n     * @brief Navigate to the variable's location in the disassembly when an item is double-clicked.\n     * @param index The index of the item clicked\n     */\n    void onItemDoubleClicked(const QModelIndex &index);\n\nprivate:\n    /**\n     * @brief Populate the model with variables of the specified type by querying both globals and\n     * local variables in all functions.\n     * @param typeName The name of the type to filter by\n     */\n    void refreshModel(const QString &typeName);\n\n    std::unique_ptr<Ui::TypesVariablesDialog> ui;\n    TypesVariablesModel *sourceModel;\n    TypesVariablesProxyModel *proxyModel;\n    CutterTreeWidget *tree;\n};\n\n#endif // TYPESVARIABLESDIALOG_H\n"
  },
  {
    "path": "src/dialogs/TypesVariablesDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>TypesVariablesDialog</class>\n <widget class=\"QDialog\" name=\"TypesVariablesDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>700</width>\n    <height>450</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>TypesVariablesDialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\" stretch=\"9,1\">\n   <item>\n    <widget class=\"CutterTreeView\" name=\"treeView\"/>\n   </item>\n   <item>\n    <widget class=\"ComboQuickFilterView\" name=\"quickFilterView\" native=\"true\"/>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>CutterTreeView</class>\n   <extends>QTreeView</extends>\n   <header>widgets/CutterTreeView.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ComboQuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/ComboQuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/VersionInfoDialog.cpp",
    "content": "#include \"VersionInfoDialog.h\"\n#include \"ui_VersionInfoDialog.h\"\n\n#include \"common/Helpers.h\"\n\n#include <QJsonArray>\n#include <QStringList>\n#include <QJsonObject>\n#include <QJsonDocument>\n#include <QTreeWidget>\n#include <QContextMenuEvent>\n#include <QClipboard>\n\nVersionInfoDialog::VersionInfoDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::VersionInfoDialog), core(Core())\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    // Get version information\n    fillVersionInfo();\n\n    // Setup context menu and actions\n    copyActionLeftTreewidget = new QAction(tr(\"Copy\"), this);\n    copyActionLeftTreewidget->setIcon(QIcon(\":/img/icons/copy.svg\"));\n    copyActionLeftTreewidget->setShortcut(QKeySequence::StandardKey::Copy);\n    copyActionLeftTreewidget->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    copyActionRightTreewidget = new QAction(tr(\"Copy\"), this);\n    copyActionRightTreewidget->setIcon(QIcon(\":/img/icons/copy.svg\"));\n    copyActionRightTreewidget->setShortcut(QKeySequence::StandardKey::Copy);\n    copyActionRightTreewidget->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    selAllActionLeftTreewidget = new QAction(tr(\"Select All\"), this);\n    selAllActionLeftTreewidget->setShortcut(QKeySequence::StandardKey::SelectAll);\n    selAllActionLeftTreewidget->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    selAllActionRightTreewidget = new QAction(tr(\"Select All\"), this);\n    selAllActionRightTreewidget->setShortcut(QKeySequence::StandardKey::SelectAll);\n    selAllActionRightTreewidget->setShortcutContext(\n            Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    // Connect Copy actions\n    connect(copyActionLeftTreewidget, &QAction::triggered, this,\n            [this]() { CopyTreeWidgetSelection(ui->leftTreeWidget); });\n\n    connect(copyActionRightTreewidget, &QAction::triggered, this,\n            [this]() { CopyTreeWidgetSelection(ui->rightTreeWidget); });\n\n    // Connect select sll actions\n    connect(selAllActionLeftTreewidget, &QAction::triggered, this,\n            [this]() { ui->leftTreeWidget->selectAll(); });\n\n    connect(selAllActionRightTreewidget, &QAction::triggered, this,\n            [this]() { ui->rightTreeWidget->selectAll(); });\n\n    // Connect selection handles\n    connect(ui->leftTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this,\n            [this]() { ui->rightTreeWidget->clearSelection(); });\n    connect(ui->rightTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this,\n            [this]() { ui->leftTreeWidget->clearSelection(); });\n    connect(this, &VersionInfoDialog::finished, this, &VersionInfoDialog::clearSelectionOnClose);\n\n    // Add actions to context menu\n    ui->leftTreeWidget->addAction(copyActionLeftTreewidget);\n    ui->leftTreeWidget->addAction(selAllActionLeftTreewidget);\n\n    ui->rightTreeWidget->addAction(copyActionRightTreewidget);\n    ui->rightTreeWidget->addAction(selAllActionRightTreewidget);\n}\n\nVersionInfoDialog::~VersionInfoDialog() {}\n\nvoid VersionInfoDialog::clearSelectionOnClose()\n{\n    ui->leftTreeWidget->clearSelection();\n    ui->rightTreeWidget->clearSelection();\n\n    // remove default \"current\" item selection after dialog close\n    QModelIndex defaultIndex;\n    ui->leftTreeWidget->setCurrentIndex(defaultIndex);\n    ui->rightTreeWidget->setCurrentIndex(defaultIndex);\n}\n\nvoid VersionInfoDialog::CopyTreeWidgetSelection(QTreeWidget *t)\n{\n    QString vinfo, row;\n\n    QTreeWidgetItemIterator it(t);\n\n    while (*it) {\n        if ((*it)->isSelected()) {\n            row = (*it)->text(KeyColumn) + \" \" + (*it)->text(ValueColumn) + \"\\n\";\n            vinfo.append(row);\n        }\n        it++;\n    }\n\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(vinfo.trimmed());\n}\n\nvoid VersionInfoDialog::contextMenuEvent(QContextMenuEvent *event)\n{\n    contextMenu->exec(event->globalPos());\n    event->accept();\n}\n\nvoid VersionInfoDialog::on_buttonBox_rejected()\n{\n    close();\n}\n\nvoid VersionInfoDialog::on_copyVersionInfoButton_clicked()\n{\n    QString vinfo = \"# \" + ui->leftLabel->text() + \"\\n\";\n\n    // Iterate & Copy leftTreeWidget items\n    QTreeWidgetItemIterator itl(ui->leftTreeWidget);\n\n    int keyColumnIndex = 0, valueColumnIndex = 1;\n\n    while (*itl) {\n        QString row = (*itl)->text(keyColumnIndex) + \" : \" + (*itl)->text(valueColumnIndex) + \"\\n\";\n        vinfo.append(row);\n        ++itl;\n    }\n\n    vinfo.append(\"\\n# \" + ui->rightLabel->text() + \"\\n\");\n\n    // Iterate & Copy rightTreeWidget items\n    QTreeWidgetItemIterator itr(ui->rightTreeWidget);\n\n    while (*itr) {\n        QString row = (*itr)->text(keyColumnIndex) + \" : \" + (*itr)->text(valueColumnIndex) + \"\\n\";\n        vinfo.append(row);\n        ++itr;\n    }\n\n    // Copy to Clipboard\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(vinfo);\n\n    QMessageBox::information(this, tr(\"Copy to Clipboard\"),\n                             tr(\"Version information was successfully copied!\"));\n}\n\nvoid VersionInfoDialog::fillVersionInfo()\n{\n    RzCoreLocked core(Core());\n    RzBinObject *bobj = rz_bin_cur_object(core->bin);\n    if (!bobj) {\n        return;\n    }\n    const RzBinInfo *info = rz_bin_object_get_info(bobj);\n    if (!info || !info->rclass) {\n        return;\n    }\n    // Case ELF\n    if (strncmp(\"elf\", info->rclass, 3) == 0) {\n        // Set labels\n        ui->leftLabel->setText(tr(\"Version symbols\"));\n        ui->rightLabel->setText(tr(\"Version need\"));\n\n        Sdb *sdb = sdb_ns_path(core->sdb, \"bin/cur/info/versioninfo/versym\", 0);\n        if (!sdb) {\n            return;\n        }\n\n        // Left tree\n        QTreeWidgetItem *addrItemL = new QTreeWidgetItem();\n        addrItemL->setText(0, tr(\"Address:\"));\n        addrItemL->setText(1, RzAddressString(sdb_num_get(sdb, \"addr\")));\n        ui->leftTreeWidget->addTopLevelItem(addrItemL);\n\n        QTreeWidgetItem *offItemL = new QTreeWidgetItem();\n        offItemL->setText(0, tr(\"Offset:\"));\n        offItemL->setText(1, RzAddressString(sdb_num_get(sdb, \"offset\")));\n        ui->leftTreeWidget->addTopLevelItem(offItemL);\n\n        QTreeWidgetItem *entriesItemL = new QTreeWidgetItem();\n        entriesItemL->setText(0, tr(\"Entries:\"));\n        const ut64 num_entries = sdb_num_get(sdb, \"num_entries\");\n        for (size_t i = 0; i < num_entries; ++i) {\n            auto key = QString(\"entry%0\").arg(i);\n            const char *const value = sdb_const_get(sdb, key.toStdString().c_str());\n            if (!value) {\n                continue;\n            }\n            auto item = new QTreeWidgetItem();\n            item->setText(KeyColumn, RzAddressString(i));\n            item->setText(ValueColumn, value);\n            entriesItemL->addChild(item);\n        }\n        ui->leftTreeWidget->addTopLevelItem(entriesItemL);\n\n        // Adjust columns to content\n        qhelpers::adjustColumns(ui->leftTreeWidget, 0);\n        sdb = sdb_ns_path(core->sdb, \"bin/cur/info/versioninfo/verneed\", 0);\n\n        // Right tree\n        QTreeWidgetItem *addrItemR = new QTreeWidgetItem();\n        addrItemR->setText(0, tr(\"Address:\"));\n        addrItemR->setText(1, RzAddressString(sdb_num_get(sdb, \"addr\")));\n        ui->rightTreeWidget->addTopLevelItem(addrItemR);\n\n        QTreeWidgetItem *offItemR = new QTreeWidgetItem();\n        offItemR->setText(0, tr(\"Offset:\"));\n        offItemR->setText(1, RzAddressString(sdb_num_get(sdb, \"offset\")));\n        ui->rightTreeWidget->addTopLevelItem(offItemR);\n\n        QTreeWidgetItem *entriesItemR = new QTreeWidgetItem();\n        entriesItemR->setText(0, tr(\"Entries:\"));\n        for (size_t num_version = 0;; num_version++) {\n            auto path_version =\n                    QString(\"bin/cur/info/versioninfo/verneed/version%0\").arg(num_version);\n            sdb = sdb_ns_path(core->sdb, path_version.toStdString().c_str(), 0);\n            if (!sdb) {\n                break;\n            }\n            const char *filename = sdb_const_get(sdb, \"file_name\");\n            auto *parentItem = new QTreeWidgetItem();\n            parentItem->setText(0, RzAddressString(sdb_num_get(sdb, \"idx\")));\n            parentItem->setText(1,\n                                tr(\"Version: %0\\t\"\n                                   \"File: %1\")\n                                        .arg(QString::number(sdb_num_get(sdb, \"vn_version\")),\n                                             QString(filename)));\n\n            int num_vernaux = 0;\n            while (true) {\n                auto path_vernaux =\n                        QString(\"%0/vernaux%1\").arg(path_version, QString::number(num_vernaux++));\n                sdb = sdb_ns_path(core->sdb, path_vernaux.toStdString().c_str(), 0);\n                if (!sdb) {\n                    break;\n                }\n\n                auto *childItem = new QTreeWidgetItem();\n                childItem->setText(0, RzAddressString(sdb_num_get(sdb, \"idx\")));\n                QString childString =\n                        tr(\"Name: %0\\t\"\n                           \"Flags: %1\\t\"\n                           \"Version: %2\\t\")\n                                .arg(sdb_const_get(sdb, \"name\"), sdb_const_get(sdb, \"flags\"),\n                                     QString::number(sdb_num_get(sdb, \"version\")));\n                childItem->setText(1, childString);\n                parentItem->addChild(childItem);\n            }\n            entriesItemR->addChild(parentItem);\n        }\n\n        ui->rightTreeWidget->addTopLevelItem(entriesItemR);\n        // Adjust columns to content\n        qhelpers::adjustColumns(ui->rightTreeWidget, 0);\n    }\n    // Case PE\n    else if (strncmp(\"pe\", info->rclass, 2) == 0) {\n        // Set labels\n        ui->leftLabel->setText(tr(\"VS Fixed file info\"));\n        ui->rightLabel->setText(tr(\"String table\"));\n        Sdb *sdb = NULL;\n\n        // Left tree\n        auto path_version = QString(\"bin/cur/info/vs_version_info/VS_VERSIONINFO%0\").arg(0);\n        auto path_fixedfileinfo = QString(\"%0/fixed_file_info\").arg(path_version);\n        sdb = sdb_ns_path(core->sdb, path_fixedfileinfo.toStdString().c_str(), 0);\n        if (!sdb) {\n            return;\n        }\n        ut32 file_version_ms = sdb_num_get(sdb, \"FileVersionMS\");\n        ut32 file_version_ls = sdb_num_get(sdb, \"FileVersionLS\");\n        auto file_version = QString(\"%0.%1.%2.%3\")\n                                    .arg(file_version_ms >> 16)\n                                    .arg(file_version_ms & 0xFFFF)\n                                    .arg(file_version_ls >> 16)\n                                    .arg(file_version_ls & 0xFFFF);\n        ut32 product_version_ms = sdb_num_get(sdb, \"ProductVersionMS\");\n        ut32 product_version_ls = sdb_num_get(sdb, \"ProductVersionLS\");\n        auto product_version = QString(\"%0.%1.%2.%3\")\n                                       .arg(product_version_ms >> 16)\n                                       .arg(product_version_ms & 0xFFFF)\n                                       .arg(product_version_ls >> 16)\n                                       .arg(product_version_ls & 0xFFFF);\n\n        auto item = new QTreeWidgetItem();\n        item->setText(0, \"Signature\");\n        item->setText(1, RzHexString(sdb_num_get(sdb, \"Signature\")));\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(0, \"StrucVersion\");\n        item->setText(1, RzHexString(sdb_num_get(sdb, \"StrucVersion\")));\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(KeyColumn, \"FileVersion\");\n        item->setText(ValueColumn, file_version);\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(KeyColumn, \"ProductVersion\");\n        item->setText(ValueColumn, product_version);\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(0, \"FileFlagsMask\");\n        item->setText(1, RzHexString(sdb_num_get(sdb, \"FileFlagsMask\")));\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(0, \"FileFlags\");\n        item->setText(1, RzHexString(sdb_num_get(sdb, \"FileFlags\")));\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(0, \"FileOS\");\n        item->setText(1, RzHexString(sdb_num_get(sdb, \"FileOS\")));\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(0, \"FileType\");\n        item->setText(1, RzHexString(sdb_num_get(sdb, \"FileType\")));\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        item = new QTreeWidgetItem();\n        item->setText(0, \"FileSubType\");\n        item->setText(1, RzHexString(sdb_num_get(sdb, \"FileSubType\")));\n        ui->leftTreeWidget->addTopLevelItem(item);\n\n        // Adjust columns to content\n        qhelpers::adjustColumns(ui->leftTreeWidget, 0);\n\n        // Right tree\n        for (int num_stringtable = 0;; num_stringtable++) {\n            auto path_stringtable = QString(\"%0/string_file_info/stringtable%1\")\n                                            .arg(path_version)\n                                            .arg(num_stringtable);\n            sdb = sdb_ns_path(core->sdb, path_stringtable.toStdString().c_str(), 0);\n            if (!sdb) {\n                break;\n            }\n            for (int num_string = 0; sdb; num_string++) {\n                auto path_string = QString(\"%0/string%1\").arg(path_stringtable).arg(num_string);\n                sdb = sdb_ns_path(core->sdb, path_string.toStdString().c_str(), 0);\n                if (!sdb) {\n                    continue;\n                }\n                int lenkey = 0;\n                int lenval = 0;\n                ut8 *key_utf16 = sdb_decode(sdb_const_get(sdb, \"key\"), &lenkey);\n                ut8 *val_utf16 = sdb_decode(sdb_const_get(sdb, \"value\"), &lenval);\n                item = new QTreeWidgetItem();\n                item->setText(KeyColumn,\n                              QString::fromUtf16(reinterpret_cast<const ushort *>(key_utf16)));\n                item->setText(ValueColumn,\n                              QString::fromUtf16(reinterpret_cast<const ushort *>(val_utf16)));\n                ui->rightTreeWidget->addTopLevelItem(item);\n                free(key_utf16);\n                free(val_utf16);\n            }\n        }\n        qhelpers::adjustColumns(ui->rightTreeWidget, 0);\n    }\n}\n"
  },
  {
    "path": "src/dialogs/VersionInfoDialog.h",
    "content": "#ifndef VERSIONINFODIALOG_H\n#define VERSIONINFODIALOG_H\n\n#include <QDialog>\n#include <memory>\n\n#include \"core/Cutter.h\"\n\nnamespace Ui {\nclass VersionInfoDialog;\n}\n\nclass VersionInfoDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit VersionInfoDialog(QWidget *parent = nullptr);\n    ~VersionInfoDialog();\n\n    enum Column { KeyColumn = 0, ValueColumn = 1 };\n\nprivate slots:\n    void CopyTreeWidgetSelection(QTreeWidget *t);\n    void clearSelectionOnClose();\n\nprotected:\n    QMenu *contextMenu = nullptr;\n    QAction *copyActionLeftTreewidget = nullptr;\n    QAction *copyActionRightTreewidget = nullptr;\n    QAction *selAllActionLeftTreewidget = nullptr;\n    QAction *selAllActionRightTreewidget = nullptr;\n\n    void contextMenuEvent(QContextMenuEvent *event) override;\n    void on_buttonBox_rejected();\n\n    /**\n     * @fn AboutDialog::on_copyVersionInfoButton_clicked()\n     *\n     * @brief Copies the table values to Clipboard.\n     */\n    void on_copyVersionInfoButton_clicked();\n\nprivate:\n    std::unique_ptr<Ui::VersionInfoDialog> ui;\n    CutterCore *core;\n\n    void fillVersionInfo();\n};\n\n#endif // VERSIONINFODIALOG_H\n"
  },
  {
    "path": "src/dialogs/VersionInfoDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>VersionInfoDialog</class>\n <widget class=\"QDialog\" name=\"VersionInfoDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"enabled\">\n   <bool>true</bool>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1127</width>\n    <height>390</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Version Info</string>\n  </property>\n  <property name=\"toolTip\">\n   <string/>\n  </property>\n  <property name=\"statusTip\">\n   <string/>\n  </property>\n  <property name=\"whatsThis\">\n   <string/>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QLabel\" name=\"leftLabel\">\n       <property name=\"font\">\n        <font>\n         <pointsize>17</pointsize>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>TextLabel</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"rightLabel\">\n       <property name=\"font\">\n        <font>\n         <pointsize>17</pointsize>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>TextLabel</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QTreeWidget\" name=\"leftTreeWidget\">\n      <property name=\"contextMenuPolicy\">\n        <enum>Qt::ActionsContextMenu</enum>\n      </property>\n      <property name=\"selectionMode\">\n        <enum>QAbstractItemView::ExtendedSelection</enum>\n       </property>\n       <property name=\"frameShape\">\n        <enum>QFrame::StyledPanel</enum>\n       </property>\n       <property name=\"frameShadow\">\n        <enum>QFrame::Sunken</enum>\n       </property>\n       <property name=\"sizeAdjustPolicy\">\n        <enum>QAbstractScrollArea::AdjustIgnored</enum>\n       </property>\n       <property name=\"autoExpandDelay\">\n        <number>-1</number>\n       </property>\n       <property name=\"indentation\">\n        <number>20</number>\n       </property>\n       <attribute name=\"headerDefaultSectionSize\">\n        <number>200</number>\n       </attribute>\n       <column>\n        <property name=\"text\">\n         <string>Key</string>\n        </property>\n       </column>\n       <column>\n        <property name=\"text\">\n         <string>Value</string>\n        </property>\n       </column>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QTreeWidget\" name=\"rightTreeWidget\">\n      <property name=\"contextMenuPolicy\">\n        <enum>Qt::ActionsContextMenu</enum>\n      </property>\n       <property name=\"selectionMode\">\n        <enum>QAbstractItemView::ExtendedSelection</enum>\n       </property>\n       <property name=\"frameShape\">\n        <enum>QFrame::StyledPanel</enum>\n       </property>\n       <property name=\"frameShadow\">\n        <enum>QFrame::Sunken</enum>\n       </property>\n       <property name=\"sizeAdjustPolicy\">\n        <enum>QAbstractScrollArea::AdjustIgnored</enum>\n       </property>\n       <property name=\"autoExpandDelay\">\n        <number>-1</number>\n       </property>\n       <property name=\"indentation\">\n        <number>20</number>\n       </property>\n       <attribute name=\"headerDefaultSectionSize\">\n        <number>200</number>\n       </attribute>\n       <column>\n        <property name=\"text\">\n         <string>Key</string>\n        </property>\n       </column>\n       <column>\n        <property name=\"text\">\n         <string>Value</string>\n        </property>\n       </column>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   \n   <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n         <item>\n         <widget class=\"QPushButton\" name=\"copyVersionInfoButton\">\n            <property name=\"icon\">\n            <iconset resource=\"../resources.qrc\">\n            <normaloff>:/img/icons/copy.svg</normaloff>:/img/icons/copy.svg</iconset>\n            </property>\n            <property name=\"text\">\n            <string>Copy</string>\n            </property>\n         </widget>\n         </item>\n         <item>\n         <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n         <property name=\"standardButtons\">\n            <set>QDialogButtonBox::Close</set>\n         </property>\n         </widget>\n         </item>\n      </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/WelcomeDialog.cpp",
    "content": "#include \"core/MainWindow.h\"\n#include \"CutterConfig.h\"\n\n#include \"common/Helpers.h\"\n#include \"WelcomeDialog.h\"\n#include \"AboutDialog.h\"\n\n#include \"ui_WelcomeDialog.h\"\n\n/**\n * @brief Constructs a WelcomeDialog object\n * @param parent\n */\nWelcomeDialog::WelcomeDialog(QWidget *parent) : QDialog(parent), ui(new Ui::WelcomeDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n    ui->logoSvgWidget->load(Config()->getLogoFile());\n    ui->versionLabel->setText(\"<font color='#a4a9b2'>\" + tr(\"Version \") + CUTTER_VERSION_FULL\n                              + \"</font>\");\n    ui->themeComboBox->setCurrentIndex(Config()->getInterfaceTheme());\n\n    QSignalBlocker s(ui->updatesCheckBox);\n    ui->updatesCheckBox->setChecked(Config()->getAutoUpdateEnabled());\n\n    auto langs = Config()->getAvailableTranslations();\n    for (auto &lang : langs) {\n        ui->languageComboBox->addItem(lang.name, lang.locale);\n    }\n\n    auto matchingLang =\n            std::find_if(langs.begin(), langs.end(), [](const Configuration::LangInfo &v) {\n                return v.locale == Config()->getCurrLocale();\n            });\n    if (matchingLang == langs.end()) {\n        matchingLang =\n                std::find_if(langs.begin(), langs.end(), [](const Configuration::LangInfo &v) {\n                    return v.locale.language() == QLocale::English;\n                });\n    }\n    if (matchingLang != langs.end()) {\n        ui->languageComboBox->setCurrentIndex(matchingLang - langs.begin());\n    } else {\n        ui->languageComboBox->setCurrentText(\"English\");\n    }\n\n    connect(ui->languageComboBox,\n            static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,\n            &WelcomeDialog::onLanguageCurrentIndexChanged);\n\n    Config()->adjustColorThemeDarkness();\n}\n\n/**\n * @brief Destroys the WelcomeDialog\n */\nWelcomeDialog::~WelcomeDialog()\n{\n    delete ui;\n}\n\n/**\n * @brief change Cutter's QT Theme as selected by the user\n * @param index - a Slot being called after theme's value changes its index\n */\nvoid WelcomeDialog::on_themeComboBox_currentIndexChanged(int index)\n{\n    Config()->setInterfaceTheme(index);\n\n    // make sure that Cutter's logo changes its color according to the selected theme\n    ui->logoSvgWidget->load(Config()->getLogoFile());\n}\n\n/**\n * @brief change Cutter's interface language as selected by the user\n * @param index - a Slot being called after language combo box value changes its index\n */\nvoid WelcomeDialog::onLanguageCurrentIndexChanged(int)\n{\n    QVariant language = ui->languageComboBox->currentData();\n    if (language.canConvert<QLocale>()) {\n        Config()->setLocale(language.toLocale());\n    }\n\n    QMessageBox mb(this);\n    mb.setWindowTitle(tr(\"Language settings\"));\n    mb.setText(tr(\"Language will be changed after next application start.\"));\n    mb.setIcon(QMessageBox::Information);\n    mb.setStandardButtons(QMessageBox::Ok);\n    mb.exec();\n}\n\n/**\n * @brief show Cutter's About dialog\n */\nvoid WelcomeDialog::on_checkUpdateButton_clicked()\n{\n    AboutDialog *a = new AboutDialog(this);\n    a->setAttribute(Qt::WA_DeleteOnClose);\n    a->open();\n}\n\n/**\n * @brief accept user preferences, close the window and continue Cutter's execution\n */\nvoid WelcomeDialog::on_continueButton_clicked()\n{\n    accept();\n}\n\nvoid WelcomeDialog::on_updatesCheckBox_stateChanged(int)\n{\n    Config()->setAutoUpdateEnabled(!Config()->getAutoUpdateEnabled());\n}\n"
  },
  {
    "path": "src/dialogs/WelcomeDialog.h",
    "content": "#ifndef WELCOMEDIALOG_H\n#define WELCOMEDIALOG_H\n\n#include <QDialog>\n\nnamespace Ui {\n\n/**\n * @class WelcomeDialog\n * @brief The WelcomeDialog class will show the user the Welcome windows\n *  upon first execution of Cutter.\n *\n * Upon first execution of Cutter, the WelcomeDialog would be showed to the user.\n * The Welcome dialog would be showed after a reset of Cutter's preferences by the user.\n */\n\nclass WelcomeDialog;\n}\n\nclass WelcomeDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit WelcomeDialog(QWidget *parent = 0);\n    ~WelcomeDialog();\n\nprivate slots:\n    void on_themeComboBox_currentIndexChanged(int index);\n    void onLanguageCurrentIndexChanged(int index);\n    void on_checkUpdateButton_clicked();\n    void on_continueButton_clicked();\n    void on_updatesCheckBox_stateChanged(int state);\n\nprivate:\n    Ui::WelcomeDialog *ui;\n};\n\n#endif // WELCOMEDIALOG_H\n"
  },
  {
    "path": "src/dialogs/WelcomeDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>WelcomeDialog</class>\n <widget class=\"QDialog\" name=\"WelcomeDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>806</width>\n    <height>620</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Welcome to Cutter</string>\n  </property>\n  <property name=\"modal\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\" stretch=\"1,2,3\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetDefaultConstraint</enum>\n   </property>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\" stretch=\"1,0,1,0,0\">\n     <property name=\"spacing\">\n      <number>0</number>\n     </property>\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <property name=\"leftMargin\">\n      <number>2</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>2</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>2</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>2</number>\n     </property>\n     <item alignment=\"Qt::AlignHCenter|Qt::AlignVCenter\">\n      <widget class=\"QSvgWidget\" name=\"logoSvgWidget\" native=\"true\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>88</width>\n         <height>88</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>88</width>\n         <height>88</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"cutterLabel\">\n       <property name=\"enabled\">\n        <bool>true</bool>\n       </property>\n       <property name=\"font\">\n        <font>\n         <family>Monospace</family>\n         <pointsize>20</pointsize>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>Cutter</string>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignCenter</set>\n       </property>\n      </widget>\n     </item>\n     <item alignment=\"Qt::AlignHCenter\">\n      <widget class=\"QLabel\" name=\"versionLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"font\">\n        <font>\n         <pointsize>11</pointsize>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>Version </string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"verticalSpacer_4\">\n       <property name=\"orientation\">\n        <enum>Qt::Vertical</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Fixed</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n       <item>\n        <spacer name=\"horizontalSpacer_4\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n         <property name=\"spacing\">\n          <number>9</number>\n         </property>\n         <property name=\"sizeConstraint\">\n          <enum>QLayout::SetMaximumSize</enum>\n         </property>\n         <item>\n          <widget class=\"QPushButton\" name=\"checkUpdateButton\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>0</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>16777215</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"text\">\n            <string>About</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"themeComboBox\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>0</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>16777215</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"currentIndex\">\n            <number>0</number>\n           </property>\n           <property name=\"iconSize\">\n            <size>\n             <width>16</width>\n             <height>16</height>\n            </size>\n           </property>\n           <item>\n            <property name=\"text\">\n             <string>Native Theme</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Dark Theme</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Midnight Theme</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Light Theme</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"languageComboBox\"/>\n         </item>\n         <item>\n          <widget class=\"QCheckBox\" name=\"updatesCheckBox\">\n           <property name=\"text\">\n            <string>Check for updates on start</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer_3\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Fixed</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <layout class=\"QGridLayout\" name=\"textGridLayout\" columnstretch=\"1,6,6,1\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <property name=\"rightMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>10</number>\n     </property>\n     <item row=\"0\" column=\"1\" alignment=\"Qt::AlignBottom\">\n      <widget class=\"QLabel\" name=\"communityTitleLabel\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>250</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"font\">\n        <font>\n         <pointsize>12</pointsize>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"lineWidth\">\n        <number>2</number>\n       </property>\n       <property name=\"text\">\n        <string>Community</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\" alignment=\"Qt::AlignTop\">\n      <widget class=\"QLabel\" name=\"communityRichTextLAbel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>250</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"lineWidth\">\n        <number>1</number>\n       </property>\n       <property name=\"text\">\n        <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;\n&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;\np, li { white-space: pre-wrap; }\n&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;\n&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt;&quot;&gt;Join thousands of reverse engineers in our community:&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt; font-weight:600;&quot;&gt;Twitter:&lt;/span&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt;&quot;&gt;\t&lt;/span&gt;&lt;a href=&quot;https://twitter.com/cutter_re&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt; text-decoration: underline; color:#2980b9;&quot;&gt;@cutter_re&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt; font-weight:600;&quot;&gt;Telegram:\t&lt;/span&gt;&lt;a href=&quot;https://t.me/cutter_re&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt; text-decoration: underline; color:#2980b9;&quot;&gt;@cutter_re &lt;br /&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt; font-weight:600;&quot;&gt;IRC:\t&lt;/span&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt;&quot;&gt;#cutter on &lt;/span&gt;&lt;a href=&quot;https://web.libera.chat/&quot;&gt;&lt;span style=&quot; font-family:'Noto Sans'; font-size:10pt; text-decoration: underline; color:#2980b9;&quot;&gt;https://web.libera.chat/&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n       <property name=\"textFormat\">\n        <enum>Qt::RichText</enum>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>true</bool>\n       </property>\n       <property name=\"openExternalLinks\">\n        <bool>true</bool>\n       </property>\n       <property name=\"textInteractionFlags\">\n        <set>Qt::TextBrowserInteraction</set>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"2\" alignment=\"Qt::AlignTop\">\n      <widget class=\"QLabel\" name=\"contributingTextLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>250</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string>&lt;html&gt;&lt;head/&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Want to help us make Cutter even better?&lt;br/&gt;Visit our &lt;/span&gt;&lt;a href=&quot;https://github.com/rizinorg/cutter&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#2980b9;&quot;&gt;Github page&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; and report bugs or contribute code.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n       </property>\n       <property name=\"wordWrap\">\n        <bool>true</bool>\n       </property>\n       <property name=\"indent\">\n        <number>20</number>\n       </property>\n       <property name=\"openExternalLinks\">\n        <bool>true</bool>\n       </property>\n       <property name=\"textInteractionFlags\">\n        <set>Qt::TextBrowserInteraction</set>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>10</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item row=\"0\" column=\"2\" alignment=\"Qt::AlignBottom\">\n      <widget class=\"QLabel\" name=\"contributingLabel\">\n       <property name=\"font\">\n        <font>\n         <pointsize>12</pointsize>\n         <weight>75</weight>\n         <bold>true</bold>\n        </font>\n       </property>\n       <property name=\"text\">\n        <string>Contributing</string>\n       </property>\n       <property name=\"indent\">\n        <number>20</number>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"3\">\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>10</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item row=\"2\" column=\"3\">\n      <widget class=\"QPushButton\" name=\"continueButton\">\n       <property name=\"text\">\n        <string>Continue</string>\n       </property>\n       <property name=\"flat\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>QSvgWidget</class>\n   <extends>QWidget</extends>\n   <header>QSvgWidget</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/WriteCommandsDialogs.cpp",
    "content": "#include \"WriteCommandsDialogs.h\"\n#include \"ui_Base64EnDecodedWriteDialog.h\"\n#include \"ui_IncrementDecrementDialog.h\"\n#include \"ui_DuplicateFromOffsetDialog.h\"\n#include \"Cutter.h\"\n\n#include <cmath>\n#include <QFontDatabase>\n\nBase64EnDecodedWriteDialog::Base64EnDecodedWriteDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::Base64EnDecodedWriteDialog)\n{\n    ui->setupUi(this);\n    ui->decodeRB->click();\n}\n\nBase64EnDecodedWriteDialog::Mode Base64EnDecodedWriteDialog::getMode() const\n{\n    return ui->decodeRB->isChecked() ? Decode : Encode;\n}\n\nQByteArray Base64EnDecodedWriteDialog::getData() const\n{\n    return ui->base64LineEdit->text().toUtf8();\n}\n\nIncrementDecrementDialog::IncrementDecrementDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::IncrementDecrementDialog)\n{\n    ui->setupUi(this);\n    ui->incrementRB->click();\n\n    ui->nBytesCB->addItems(QStringList() << tr(\"Byte\") << tr(\"Word\") << tr(\"Dword\") << tr(\"Qword\"));\n\n    ui->valueLE->setValidator(\n            new QRegularExpressionValidator(QRegularExpression(\"[0-9a-fA-Fx]{1,18}\"), ui->valueLE));\n}\n\nIncrementDecrementDialog::Mode IncrementDecrementDialog::getMode() const\n{\n    return ui->incrementRB->isChecked() ? Increase : Decrease;\n}\n\nuint8_t IncrementDecrementDialog::getNBytes() const\n{\n    // Shift left to keep index powered by two\n    // This is used to create the w1, w2, w4 and w8 commands based on the selected index.\n    return static_cast<uint8_t>(1 << ui->nBytesCB->currentIndex());\n}\n\nuint64_t IncrementDecrementDialog::getValue() const\n{\n    return Core()->math(ui->valueLE->text());\n}\n\nDuplicateFromOffsetDialog::DuplicateFromOffsetDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::DuplicateFromOffsetDialog)\n{\n    ui->setupUi(this);\n    ui->bytesLabel->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));\n    ui->offsetLE->setValidator(new QRegularExpressionValidator(\n            QRegularExpression(\"[0-9a-fA-Fx]{1,18}\"), ui->offsetLE));\n    connect(ui->offsetLE, &QLineEdit::textChanged, this, &DuplicateFromOffsetDialog::refresh);\n    connect(ui->nBytesSB, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &DuplicateFromOffsetDialog::refresh);\n}\n\nRVA DuplicateFromOffsetDialog::getOffset() const\n{\n    return Core()->math(ui->offsetLE->text());\n}\n\nsize_t DuplicateFromOffsetDialog::getNBytes() const\n{\n    return static_cast<size_t>(ui->nBytesSB->value());\n}\n\nvoid DuplicateFromOffsetDialog::refresh()\n{\n    QSignalBlocker sb(Core());\n    RzCoreLocked core(Core());\n    auto buf = Core()->ioRead(getOffset(), (int)getNBytes());\n\n    // Add space every two characters for word wrap in hex sequence\n    QRegularExpression re { \"(.{2})\" };\n    auto bytes = QString(buf).replace(re, \"\\\\1 \").trimmed();\n    ui->bytesLabel->setText(bytes);\n}\n"
  },
  {
    "path": "src/dialogs/WriteCommandsDialogs.h",
    "content": "#ifndef WRITECOMMANDSDIALOGS_H\n#define WRITECOMMANDSDIALOGS_H\n\n#include <QDialog>\n#include \"CutterCommon.h\"\n\nnamespace Ui {\nclass Base64EnDecodedWriteDialog;\nclass IncrementDecrementDialog;\nclass DuplicateFromOffsetDialog;\n}\n\nclass Base64EnDecodedWriteDialog : public QDialog\n{\n    Q_OBJECT\npublic:\n    explicit Base64EnDecodedWriteDialog(QWidget *parent = nullptr);\n    enum Mode { Encode, Decode };\n    Mode getMode() const;\n    QByteArray getData() const;\n\nprivate:\n    Ui::Base64EnDecodedWriteDialog *ui;\n};\n\nclass IncrementDecrementDialog : public QDialog\n{\n    Q_OBJECT\npublic:\n    explicit IncrementDecrementDialog(QWidget *parent = nullptr);\n    enum Mode { Increase, Decrease };\n    Mode getMode() const;\n    uint8_t getNBytes() const;\n    uint64_t getValue() const;\n\nprivate:\n    Ui::IncrementDecrementDialog *ui;\n};\n\nclass DuplicateFromOffsetDialog : public QDialog\n{\n    Q_OBJECT\npublic:\n    explicit DuplicateFromOffsetDialog(QWidget *parent = nullptr);\n    RVA getOffset() const;\n    size_t getNBytes() const;\n\nprivate:\n    Ui::DuplicateFromOffsetDialog *ui;\n\nprivate slots:\n    void refresh();\n};\n\n#endif // WRITECOMMANDSDIALOGS_H\n"
  },
  {
    "path": "src/dialogs/XrefsDialog.cpp",
    "content": "#include \"XrefsDialog.h\"\n#include \"ui_XrefsDialog.h\"\n\n#include \"common/TempConfig.h\"\n#include \"common/Helpers.h\"\n\n#include \"core/MainWindow.h\"\n\n#include <QJsonArray>\n\nXrefsDialog::XrefsDialog(MainWindow *parent, bool hideXrefFrom)\n    : QDialog(parent), addr(0), toModel(this), fromModel(this), ui(new Ui::XrefsDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    ui->toTreeWidget->setMainWindow(parent);\n    ui->fromTreeWidget->setMainWindow(parent);\n\n    ui->toTreeWidget->setModel(&toModel);\n    ui->fromTreeWidget->setModel(&fromModel);\n\n    ui->toTreeWidget->getItemContextMenu()->toggleBreakpointAction(true);\n    ui->fromTreeWidget->getItemContextMenu()->toggleBreakpointAction(true);\n\n    // Modify the splitter's location to show more Disassembly instead of empty space. Not possible\n    // via Designer\n    ui->splitter->setSizes(QList<int>() << 300 << 400);\n\n    // Increase asm text edit margin\n    QTextDocument *asm_docu = ui->previewTextEdit->document();\n    asm_docu->setDocumentMargin(10);\n\n    setupPreviewColors();\n    setupPreviewFont();\n\n    // Highlight current line\n    connect(ui->previewTextEdit, &QPlainTextEdit::cursorPositionChanged, this,\n            &XrefsDialog::highlightCurrentLine);\n    connect(Config(), &Configuration::fontsUpdated, this, &XrefsDialog::setupPreviewFont);\n    connect(Config(), &Configuration::colorsUpdated, this, &XrefsDialog::setupPreviewColors);\n\n    connect(ui->toTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this,\n            &XrefsDialog::onToTreeWidgetItemSelectionChanged);\n    connect(ui->fromTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this,\n            &XrefsDialog::onFromTreeWidgetItemSelectionChanged);\n\n    // Don't create recursive xref dialogs\n    auto toContextMenu = ui->toTreeWidget->getItemContextMenu();\n    connect(toContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);\n    auto fromContextMenu = ui->fromTreeWidget->getItemContextMenu();\n    connect(fromContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);\n\n    connect(ui->toTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);\n    connect(ui->fromTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);\n\n    connect(Core(), &CutterCore::commentsChanged, this, [this]() {\n        qhelpers::emitColumnChanged(&toModel, XrefModel::COMMENT);\n        qhelpers::emitColumnChanged(&fromModel, XrefModel::COMMENT);\n    });\n\n    if (hideXrefFrom) {\n        hideXrefFromSection();\n    }\n}\n\nXrefsDialog::~XrefsDialog() {}\n\nQString XrefsDialog::normalizeAddr(const QString &addr) const\n{\n    QString ret = addr;\n    if (addr.length() < 10) {\n        ret = ret.mid(3).rightJustified(8, QLatin1Char('0'));\n        ret.prepend(QStringLiteral(\"0x\"));\n    }\n    return ret;\n}\n\nvoid XrefsDialog::setupPreviewFont()\n{\n    ui->previewTextEdit->setFont(Config()->getBaseFont());\n}\n\nvoid XrefsDialog::setupPreviewColors()\n{\n    ui->previewTextEdit->setStyleSheet(\n            QString(\"QPlainTextEdit { background-color: %1; color: %2; }\")\n                    .arg(ConfigColor(\"gui.background\").name())\n                    .arg(ConfigColor(\"btext\").name()));\n}\n\nvoid XrefsDialog::highlightCurrentLine()\n{\n    QList<QTextEdit::ExtraSelection> extraSelections;\n\n    if (ui->previewTextEdit->isReadOnly()) {\n        QTextEdit::ExtraSelection selection = QTextEdit::ExtraSelection();\n\n        selection.format.setBackground(ConfigColor(\"lineHighlight\"));\n        selection.format.setProperty(QTextFormat::FullWidthSelection, true);\n        selection.cursor = ui->previewTextEdit->textCursor();\n        selection.cursor.clearSelection();\n        extraSelections.append(selection);\n\n        ui->previewTextEdit->setExtraSelections(extraSelections);\n    }\n}\n\nvoid XrefsDialog::onFromTreeWidgetItemSelectionChanged()\n{\n    auto index = ui->fromTreeWidget->currentIndex();\n    if (!ui->fromTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {\n        return;\n    }\n    ui->toTreeWidget->clearSelection();\n    updatePreview(fromModel.address(index));\n}\n\nvoid XrefsDialog::onToTreeWidgetItemSelectionChanged()\n{\n    auto index = ui->toTreeWidget->currentIndex();\n    if (!ui->toTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {\n        return;\n    }\n    ui->fromTreeWidget->clearSelection();\n    updatePreview(toModel.address(index));\n}\n\nvoid XrefsDialog::updatePreview(RVA addr)\n{\n    TempConfig tempConfig;\n    tempConfig.set(\"scr.html\", true);\n    tempConfig.set(\"scr.color\", COLOR_MODE_16M);\n    tempConfig.set(\"asm.lines\", false);\n    tempConfig.set(\"asm.bytes\", false);\n\n    QString disas = Core()->getFunctionExecOut(\n            [](RzCore *core) {\n                ut64 offset = core->offset;\n                if (!rz_core_prevop_addr(core, core->offset, 20, &offset)) {\n                    offset = rz_core_prevop_addr_force(core, core->offset, 20);\n                }\n                rz_core_seek(core, offset, true);\n                rz_core_print_disasm(core, core->offset, core->block, (int)core->blocksize, 40,\n                                     NULL, NULL);\n                return true;\n            },\n            addr);\n    ui->previewTextEdit->document()->setHtml(disas);\n\n    // Does it make any sense?\n    ui->previewTextEdit->find(normalizeAddr(RzAddressString(addr)), QTextDocument::FindBackward);\n    ui->previewTextEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);\n}\n\nvoid XrefsDialog::updateLabels(QString name)\n{\n    ui->label_xTo->setText(tr(\"X-Refs to %1 (%2 results):\").arg(name).arg(toModel.rowCount()));\n    ui->label_xFrom->setText(\n            tr(\"X-Refs from %1 (%2 results):\").arg(name).arg(fromModel.rowCount()));\n}\n\nvoid XrefsDialog::updateLabelsForVariables(QString name)\n{\n    ui->label_xTo->setText(tr(\"Writes to %1\").arg(name));\n    ui->label_xFrom->setText(tr(\"Reads from %1\").arg(name));\n}\n\nvoid XrefsDialog::hideXrefFromSection()\n{\n    ui->label_xFrom->hide();\n    ui->fromTreeWidget->hide();\n}\n\nvoid XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function)\n{\n    setWindowTitle(tr(\"X-Refs for %1\").arg(name));\n\n    toModel.readForOffset(addr, true, whole_function);\n    fromModel.readForOffset(addr, false, whole_function);\n\n    updateLabels(name);\n\n    // Adjust columns to content\n    qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);\n    qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);\n\n    // Automatically select the first line\n    if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {\n        qhelpers::selectFirstItem(ui->fromTreeWidget);\n    }\n}\n\nvoid XrefsDialog::fillRefsForVariable(QString nameOfVariable, RVA offset)\n{\n    setWindowTitle(tr(\"X-Refs for %1\").arg(nameOfVariable));\n    updateLabelsForVariables(nameOfVariable);\n\n    // Initialize Model\n    toModel.readForVariable(nameOfVariable, true, offset);\n    fromModel.readForVariable(nameOfVariable, false, offset);\n    // Hide irrelevant column 1: which shows type\n    ui->fromTreeWidget->hideColumn(XrefModel::Columns::TYPE);\n    ui->toTreeWidget->hideColumn(XrefModel::Columns::TYPE);\n    // Adjust columns to content\n    qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);\n    qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);\n\n    // Automatically select the first line\n    if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {\n        qhelpers::selectFirstItem(ui->fromTreeWidget);\n    }\n}\n\nQString XrefModel::xrefTypeString(const QString &type)\n{\n    if (type == \"CODE\") {\n        return QStringLiteral(\"Code\");\n    } else if (type == \"CALL\") {\n        return QStringLiteral(\"Call\");\n    } else if (type == \"DATA\") {\n        return QStringLiteral(\"Data\");\n    } else if (type == \"STRING\") {\n        return QStringLiteral(\"String\");\n    }\n    return type;\n}\n\nXrefModel::XrefModel(QObject *parent) : AddressableItemModel(parent) {}\n\nvoid XrefModel::readForOffset(RVA offset, bool to, bool whole_function)\n{\n    beginResetModel();\n    this->to = to;\n    xrefs = Core()->getXRefs(offset, to, whole_function);\n    endResetModel();\n}\n\nvoid XrefModel::readForVariable(QString nameOfVariable, bool write, RVA offset)\n{\n    beginResetModel();\n    this->to = write;\n    xrefs = Core()->getXRefsForVariable(nameOfVariable, write, offset);\n    endResetModel();\n}\n\nint XrefModel::rowCount(const QModelIndex &parent) const\n{\n    Q_UNUSED(parent)\n    return xrefs.size();\n}\n\nint XrefModel::columnCount(const QModelIndex &parent) const\n{\n    Q_UNUSED(parent)\n    return Columns::COUNT;\n}\n\nQVariant XrefModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid() || index.row() >= xrefs.count()) {\n        return QVariant();\n    }\n\n    const XrefDescription &xref = xrefs.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case OFFSET:\n            return to ? xref.from_str : xref.to_str;\n        case TYPE:\n            return xrefTypeString(xref.type);\n        case CODE:\n            if (to || xref.type != \"DATA\") {\n                return Core()->disassembleSingleInstruction(xref.from);\n            } else {\n                return QString();\n            }\n        case COMMENT:\n            return to ? Core()->getCommentAt(xref.from) : Core()->getCommentAt(xref.to);\n        }\n        return QVariant();\n    case FlagDescriptionRole:\n        return QVariant::fromValue(xref);\n    default:\n        break;\n    }\n    return QVariant();\n}\n\nQVariant XrefModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n    Q_UNUSED(orientation)\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case OFFSET:\n            return tr(\"Address\");\n        case TYPE:\n            return tr(\"Type\");\n        case CODE:\n            return tr(\"Code\");\n        case COMMENT:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA XrefModel::address(const QModelIndex &index) const\n{\n    const auto &xref = xrefs.at(index.row());\n    return to ? xref.from : xref.to;\n}\n"
  },
  {
    "path": "src/dialogs/XrefsDialog.h",
    "content": "#ifndef XREFSDIALOG_H\n#define XREFSDIALOG_H\n\n#include <QDialog>\n#include <QTreeWidgetItem>\n#include <memory>\n#include \"common/Highlighter.h\"\n#include \"core/Cutter.h\"\n#include \"common/AddressableItemModel.h\"\n\nclass XrefModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\nprivate:\n    QList<XrefDescription> xrefs;\n    bool to;\n\npublic:\n    enum Columns { OFFSET = 0, TYPE, CODE, COMMENT, COUNT };\n    static const int FlagDescriptionRole = Qt::UserRole;\n\n    XrefModel(QObject *parent = nullptr);\n    void readForOffset(RVA offset, bool to, bool whole_function);\n    void readForVariable(QString nameOfVariable, bool write, RVA offset);\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n\n    static QString xrefTypeString(const QString &type);\n};\n\nclass MainWindow;\n\nnamespace Ui {\nclass XrefsDialog;\n}\n\nclass XrefsDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit XrefsDialog(MainWindow *parent, bool hideXrefFrom = false);\n    ~XrefsDialog();\n\n    void fillRefsForAddress(RVA addr, QString name, bool whole_function);\n    /**\n     * @brief Initializes toModel and fromModel with the details about the references to the\n     * specified local variable 'nameOfVariable'.\n     * @param nameOfVarible Name of the local variable for which the references are being\n     * initialized.\n     * @param offset An offset in the function in which the specified local variable exist.\n     */\n    void fillRefsForVariable(QString nameOfVariable, RVA offset);\n\nprivate slots:\n    QString normalizeAddr(const QString &addr) const;\n\n    void setupPreviewFont();\n    void setupPreviewColors();\n\n    void highlightCurrentLine();\n\n    void onFromTreeWidgetItemSelectionChanged();\n    void onToTreeWidgetItemSelectionChanged();\n\nprivate:\n    RVA addr;\n    QString func_name;\n    XrefModel toModel;\n    XrefModel fromModel;\n\n    std::unique_ptr<Ui::XrefsDialog> ui;\n\n    void updateLabels(QString name);\n    void updateLabelsForVariables(QString name);\n    void updatePreview(RVA addr);\n    void hideXrefFromSection();\n};\n\n#endif // XREFSDIALOG_H\n"
  },
  {
    "path": "src/dialogs/XrefsDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>XrefsDialog</class>\n <widget class=\"QDialog\" name=\"XrefsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1131</width>\n    <height>567</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>XRefs for </string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"spacing\">\n    <number>5</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>5</number>\n   </property>\n   <item>\n    <widget class=\"QSplitter\" name=\"splitter\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"handleWidth\">\n      <number>5</number>\n     </property>\n     <widget class=\"QWidget\" name=\"verticalLayoutWidget\">\n      <layout class=\"QVBoxLayout\" name=\"xrefLayout\">\n       <property name=\"spacing\">\n        <number>5</number>\n       </property>\n       <item>\n        <widget class=\"QLabel\" name=\"label_xTo\">\n         <property name=\"text\">\n          <string>X-Refs to: </string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"AddressableItemList&lt;&gt;\" name=\"toTreeWidget\">\n         <property name=\"frameShape\">\n          <enum>QFrame::Box</enum>\n         </property>\n         <property name=\"frameShadow\">\n          <enum>QFrame::Plain</enum>\n         </property>\n         <property name=\"indentation\">\n          <number>5</number>\n         </property>\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"label_xFrom\">\n         <property name=\"text\">\n          <string>X-Refs from: </string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"AddressableItemList&lt;&gt;\" name=\"fromTreeWidget\">\n         <property name=\"frameShape\">\n          <enum>QFrame::Box</enum>\n         </property>\n         <property name=\"frameShadow\">\n          <enum>QFrame::Plain</enum>\n         </property>\n         <property name=\"indentation\">\n          <number>5</number>\n         </property>\n         <property name=\"sortingEnabled\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"verticalLayoutWidget_2\">\n      <layout class=\"QVBoxLayout\" name=\"previewLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"label\">\n         <property name=\"text\">\n          <string>Code preview</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPlainTextEdit\" name=\"previewTextEdit\">\n         <property name=\"frameShape\">\n          <enum>QFrame::NoFrame</enum>\n         </property>\n         <property name=\"frameShadow\">\n          <enum>QFrame::Plain</enum>\n         </property>\n         <property name=\"lineWidth\">\n          <number>0</number>\n         </property>\n         <property name=\"lineWrapMode\">\n          <enum>QPlainTextEdit::NoWrap</enum>\n         </property>\n         <property name=\"readOnly\">\n          <bool>true</bool>\n         </property>\n         <property name=\"plainText\">\n          <string notr=\"true\"/>\n         </property>\n         <property name=\"textInteractionFlags\">\n          <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Close</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>AddressableItemList&lt;&gt;</class>\n   <extends>QTreeView</extends>\n   <header>widgets/AddressableItemList.h</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>XrefsDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>XrefsDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/AnalysisOptionsWidget.cpp",
    "content": "#include \"AnalysisOptionsWidget.h\"\n#include \"ui_AnalysisOptionsWidget.h\"\n\n#include \"PreferencesDialog.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n\n#include \"core/MainWindow.h\"\n\nstatic const QHash<QString, const char *> analysisBoundaries {\n    { \"io.maps.x\", QT_TRANSLATE_NOOP(\"AnalysisOptionsWidget\", \"All executable maps\") },\n    { \"io.maps\", QT_TRANSLATE_NOOP(\"AnalysisOptionsWidget\", \"All maps\") },\n    { \"io.map\", QT_TRANSLATE_NOOP(\"AnalysisOptionsWidget\", \"Current map\") },\n    { \"raw\", QT_TRANSLATE_NOOP(\"AnalysisOptionsWidget\", \"Raw\") },\n    { \"bin.section\", QT_TRANSLATE_NOOP(\"AnalysisOptionsWidget\", \"Current mapped section\") },\n    { \"bin.sections\", QT_TRANSLATE_NOOP(\"AnalysisOptionsWidget\", \"All mapped sections\") },\n};\n\nAnalysisOptionsWidget::AnalysisOptionsWidget(PreferencesDialog *dialog)\n    : QDialog(dialog), ui(new Ui::AnalysisOptionsWidget)\n{\n    ui->setupUi(this);\n\n    checkboxes = { { ui->autonameCheckbox, \"analysis.autoname\" },\n                   { ui->hasnextCheckbox, \"analysis.hasnext\" },\n                   { ui->jmpRefCheckbox, \"analysis.jmp.ref\" },\n                   { ui->jmpTblCheckbox, \"analysis.jmp.tbl\" },\n                   { ui->pushRetCheckBox, \"analysis.pushret\" },\n                   { ui->typesVerboseCheckBox, \"analysis.types.verbose\" },\n                   { ui->verboseCheckBox, \"analysis.verbose\" } };\n\n    // Create list of options for the analysis.in selector\n    createAnalysisInOptionsList();\n\n    // Connect each checkbox from \"checkboxes\" to the generic signal \"checkboxEnabler\"\n    for (ConfigCheckbox &confCheckbox : checkboxes) {\n        QString val = confCheckbox.config;\n        QCheckBox &cb = *confCheckbox.checkBox;\n        connect(confCheckbox.checkBox, &QCheckBox::stateChanged, this,\n                [val, &cb]() { checkboxEnabler(&cb, val); });\n    }\n\n    ui->analyzePushButton->setToolTip(tr(\"Analyze the program using Rizin's \\\"aaa\\\" command\"));\n    auto *mainWindow = new MainWindow(this);\n    connect(ui->analyzePushButton, &QPushButton::clicked, mainWindow,\n            &MainWindow::on_actionAnalyze_triggered);\n    connect<void (QComboBox::*)(int)>(ui->analysisInComboBox, &QComboBox::currentIndexChanged, this,\n                                      &AnalysisOptionsWidget::updateAnalysisIn);\n    connect<void (QSpinBox::*)(int)>(ui->ptrDepthSpinBox, &QSpinBox::valueChanged, this,\n                                     &AnalysisOptionsWidget::updateAnalysisPtrDepth);\n    connect(ui->preludeLineEdit, &QLineEdit::textChanged, this,\n            &AnalysisOptionsWidget::updateAnalysisPrelude);\n    updateAnalysisOptionsFromVars();\n}\n\nAnalysisOptionsWidget::~AnalysisOptionsWidget() {}\n\nvoid AnalysisOptionsWidget::checkboxEnabler(QCheckBox *checkBox, const QString &config)\n{\n    Config()->setConfig(config, checkBox->isChecked());\n}\n\nvoid AnalysisOptionsWidget::updateAnalysisOptionsFromVars()\n{\n    for (ConfigCheckbox &confCheckbox : checkboxes) {\n        qhelpers::setCheckedWithoutSignals(confCheckbox.checkBox,\n                                           Core()->getConfigb(confCheckbox.config));\n    }\n\n    // Update the rest of analysis options that are not checkboxes\n    ui->analysisInComboBox->setCurrentIndex(\n            ui->analysisInComboBox->findData(Core()->getConfig(\"analysis.in\")));\n    ui->ptrDepthSpinBox->setValue(Core()->getConfigi(\"analysis.ptrdepth\"));\n    ui->preludeLineEdit->setText(Core()->getConfig(\"analysis.prelude\"));\n}\n\nvoid AnalysisOptionsWidget::updateAnalysisIn(int index)\n{\n    Config()->setConfig(\"analysis.in\", ui->analysisInComboBox->itemData(index).toString());\n}\n\nvoid AnalysisOptionsWidget::updateAnalysisPtrDepth(int value)\n{\n    Config()->setConfig(\"analysis.ptrdepth\", value);\n}\n\nvoid AnalysisOptionsWidget::updateAnalysisPrelude(const QString &prelude)\n{\n    Config()->setConfig(\"analysis.prelude\", prelude);\n}\n\nvoid AnalysisOptionsWidget::createAnalysisInOptionsList()\n{\n    auto mapIter = analysisBoundaries.cbegin();\n    ui->analysisInComboBox->blockSignals(true);\n    ui->analysisInComboBox->clear();\n    for (; mapIter != analysisBoundaries.cend(); ++mapIter) {\n        ui->analysisInComboBox->addItem(tr(mapIter.value()), mapIter.key());\n    }\n    ui->analysisInComboBox->blockSignals(false);\n}\n"
  },
  {
    "path": "src/dialogs/preferences/AnalysisOptionsWidget.h",
    "content": "#ifndef ANALOPTIONSWIDGET_H\n#define ANALOPTIONSWIDGET_H\n\n#include <QDialog>\n#include <memory>\n\n#include \"core/Cutter.h\"\n\nclass PreferencesDialog;\n\nnamespace Ui {\nclass AnalysisOptionsWidget;\n}\n\nclass AnalysisOptionsWidget : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit AnalysisOptionsWidget(PreferencesDialog *dialog);\n    ~AnalysisOptionsWidget();\n\nprivate:\n    std::unique_ptr<Ui::AnalysisOptionsWidget> ui;\n    struct ConfigCheckbox\n    {\n        QCheckBox *checkBox;\n        QString config;\n    };\n    QList<ConfigCheckbox> checkboxes;\n\n    /**\n     * @brief This function creates the list with the different options shown in the selector for\n     * analysis.in\n     */\n    void createAnalysisInOptionsList();\n\nprivate slots:\n    /**\n     * @brief A slot to display the options in the dialog according to the current analysis.*\n     * configuration\n     */\n    void updateAnalysisOptionsFromVars();\n\n    /**\n     * @brief A generic slot to handle the simple cases where a checkbox is toggled\n     * while it's only responsible for a single independent boolean configuration eval.\n     * @param checkBox - The checkbox which is responsible for the signal\n     * @param config - the configuration string to be toggled\n     */\n    static void checkboxEnabler(QCheckBox *checkbox, const QString &config);\n\n    /**\n     * @brief A slot to update the value of analysis.in when a different option is selected\n     * @param index - The index of the selected option for analysis.in\n     */\n    void updateAnalysisIn(int index);\n\n    /**\n     * @brief A slot to update the value of analysis.ptrdepth when a new value is selected\n     * @param value - The new value for analysis.ptrdepth\n     */\n    static void updateAnalysisPtrDepth(int value);\n\n    /**\n     * @brief slot to update the value of analysis.prelude when a new value is introduced in the\n     * corresponding textbox\n     * @param prelude - The new value for analysis.prelude\n     */\n    static void updateAnalysisPrelude(const QString &prelude);\n};\n\n#endif // ANALOPTIONSWIDGET_H\n"
  },
  {
    "path": "src/dialogs/preferences/AnalysisOptionsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AnalysisOptionsWidget</class>\n <widget class=\"QWidget\" name=\"AnalysisOptionsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>633</width>\n    <height>689</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Analysis</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\" stretch=\"0,0,0\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n     <item>\n      <widget class=\"QScrollArea\" name=\"scrollArea\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"widgetResizable\">\n        <bool>true</bool>\n       </property>\n       <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n        <property name=\"geometry\">\n         <rect>\n          <x>0</x>\n          <y>0</y>\n          <width>611</width>\n          <height>610</height>\n         </rect>\n        </property>\n        <widget class=\"QCheckBox\" name=\"verboseCheckBox\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>240</y>\n           <width>601</width>\n           <height>23</height>\n          </rect>\n         </property>\n         <property name=\"text\">\n          <string>Show verbose information when performing analysis (analysis.verbose)</string>\n         </property>\n        </widget>\n        <widget class=\"QCheckBox\" name=\"pushRetCheckBox\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>180</y>\n           <width>601</width>\n           <height>23</height>\n          </rect>\n         </property>\n         <property name=\"text\">\n          <string>Analyze push+ret as jmp (analysis.pushret)</string>\n         </property>\n        </widget>\n        <widget class=\"QCheckBox\" name=\"typesVerboseCheckBox\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>210</y>\n           <width>601</width>\n           <height>23</height>\n          </rect>\n         </property>\n         <property name=\"text\">\n          <string>Verbose output from type analysis (analysis.types.verbose)</string>\n         </property>\n        </widget>\n        <widget class=\"QCheckBox\" name=\"autonameCheckbox\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>60</y>\n           <width>601</width>\n           <height>23</height>\n          </rect>\n         </property>\n         <property name=\"text\">\n          <string>Speculatively set a name for the functions (analysis.autoname)</string>\n         </property>\n        </widget>\n        <widget class=\"QCheckBox\" name=\"hasnextCheckbox\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>90</y>\n           <width>601</width>\n           <height>23</height>\n          </rect>\n         </property>\n         <property name=\"text\">\n          <string>Search for new functions following already defined functions (analysis.hasnext)</string>\n         </property>\n        </widget>\n        <widget class=\"QCheckBox\" name=\"jmpRefCheckbox\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>120</y>\n           <width>601</width>\n           <height>23</height>\n          </rect>\n         </property>\n         <property name=\"text\">\n          <string>Create references for unconditional jumps (analysis.jmp.ref)</string>\n         </property>\n        </widget>\n        <widget class=\"QCheckBox\" name=\"jmpTblCheckbox\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>150</y>\n           <width>601</width>\n           <height>23</height>\n          </rect>\n         </property>\n         <property name=\"text\">\n          <string>Analyze jump tables in switch statements (analysis.jmp.tbl)</string>\n         </property>\n        </widget>\n        <widget class=\"QWidget\" name=\"verticalLayoutWidget\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>20</y>\n           <width>581</width>\n           <height>27</height>\n          </rect>\n         </property>\n         <layout class=\"QFormLayout\" name=\"formLayout\">\n          <property name=\"fieldGrowthPolicy\">\n           <enum>QFormLayout::ExpandingFieldsGrow</enum>\n          </property>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"analInLabel\">\n            <property name=\"enabled\">\n             <bool>true</bool>\n            </property>\n            <property name=\"minimumSize\">\n             <size>\n              <width>0</width>\n              <height>0</height>\n             </size>\n            </property>\n            <property name=\"text\">\n             <string>Search boundaries for analysis (analysis.in): </string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QComboBox\" name=\"analysisInComboBox\"/>\n          </item>\n         </layout>\n        </widget>\n        <widget class=\"QWidget\" name=\"verticalLayoutWidget_2\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>270</y>\n           <width>581</width>\n           <height>28</height>\n          </rect>\n         </property>\n         <layout class=\"QFormLayout\" name=\"formLayout_2\">\n          <property name=\"fieldGrowthPolicy\">\n           <enum>QFormLayout::ExpandingFieldsGrow</enum>\n          </property>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"ptrDepthLabel\">\n            <property name=\"enabled\">\n             <bool>true</bool>\n            </property>\n            <property name=\"minimumSize\">\n             <size>\n              <width>0</width>\n              <height>0</height>\n             </size>\n            </property>\n            <property name=\"text\">\n             <string>Pointer depth (analysis.ptrdepth):</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"ptrDepthSpinBox\">\n            <property name=\"minimum\">\n             <number>0</number>\n            </property>\n            <property name=\"value\">\n             <number>0</number>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n        <widget class=\"QWidget\" name=\"verticalLayoutWidget_3\">\n         <property name=\"geometry\">\n          <rect>\n           <x>10</x>\n           <y>300</y>\n           <width>581</width>\n           <height>28</height>\n          </rect>\n         </property>\n         <layout class=\"QFormLayout\" name=\"formLayout_3\">\n          <property name=\"fieldGrowthPolicy\">\n           <enum>QFormLayout::ExpandingFieldsGrow</enum>\n          </property>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"preludeLabel\">\n            <property name=\"enabled\">\n             <bool>true</bool>\n            </property>\n            <property name=\"minimumSize\">\n             <size>\n              <width>0</width>\n              <height>0</height>\n             </size>\n            </property>\n            <property name=\"text\">\n             <string>Functions Prelude (analysis.prelude):</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"preludeLineEdit\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"placeholderText\">\n             <string>e.g.: 0x554889e5</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </widget>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item alignment=\"Qt::AlignRight\">\n    <widget class=\"QPushButton\" name=\"analyzePushButton\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string>Analyze program</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Fixed</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/AppearanceOptionsWidget.cpp",
    "content": "#include <QDir>\n#include <QFile>\n#include <QLabel>\n#include <QPainter>\n#include <QFontDialog>\n#include <QFileDialog>\n#include <QTranslator>\n#include <QInputDialog>\n#include <QSignalBlocker>\n#include <QStandardPaths>\n#include <QtSvg/QSvgRenderer>\n\n#include <QComboBox>\n#include <QtWidgets/QSpinBox>\n#include \"PreferencesDialog.h\"\n#include \"AppearanceOptionsWidget.h\"\n#include \"ui_AppearanceOptionsWidget.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n\n#include \"common/ColorThemeWorker.h\"\n#include \"dialogs/preferences/ColorThemeEditDialog.h\"\n#include \"widgets/ColorPicker.h\"\n\nAppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog)\n    : QDialog(dialog), ui(new Ui::AppearanceOptionsWidget)\n{\n    ui->setupUi(this);\n    updateFromConfig();\n\n    auto langs = Config()->getAvailableTranslations();\n    for (auto &lang : langs) {\n        ui->languageComboBox->addItem(lang.name, lang.locale);\n    }\n\n    auto matchingLang =\n            std::find_if(langs.begin(), langs.end(), [](const Configuration::LangInfo &v) {\n                return v.locale == Config()->getCurrLocale();\n            });\n    if (matchingLang == langs.end()) {\n        matchingLang =\n                std::find_if(langs.begin(), langs.end(), [](const Configuration::LangInfo &v) {\n                    return v.locale.language() == QLocale::English;\n                });\n    }\n    if (matchingLang != langs.end()) {\n        ui->languageComboBox->setCurrentIndex(matchingLang - langs.begin());\n    } else {\n        ui->languageComboBox->setCurrentText(\"English\");\n    }\n\n    auto setIcons = [this]() {\n        QColor textColor = palette().text().color();\n        ui->editButton->setIcon(getIconFromSvg(\":/img/icons/pencil_thin.svg\", textColor));\n        ui->deleteButton->setIcon(getIconFromSvg(\":/img/icons/trash_bin.svg\", textColor));\n        ui->copyButton->setIcon(getIconFromSvg(\":/img/icons/copy.svg\", textColor));\n        ui->importButton->setIcon(getIconFromSvg(\":/img/icons/download_black.svg\", textColor));\n        ui->exportButton->setIcon(getIconFromSvg(\":/img/icons/upload_black.svg\", textColor));\n        ui->renameButton->setIcon(getIconFromSvg(\":/img/icons/rename.svg\", textColor));\n    };\n    setIcons();\n    connect(Config(), &Configuration::interfaceThemeChanged, this, setIcons);\n\n    connect(ui->languageComboBox,\n            static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,\n            &AppearanceOptionsWidget::onLanguageComboBoxCurrentIndexChanged);\n\n    connect(Config(), &Configuration::fontsUpdated, this,\n            &AppearanceOptionsWidget::updateFontFromConfig);\n\n    connect(ui->colorComboBox, &QComboBox::currentTextChanged, this,\n            &AppearanceOptionsWidget::updateModificationButtons);\n\n    connect(ui->fontZoomBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &AppearanceOptionsWidget::onFontZoomBoxValueChanged);\n\n    ui->useDecompilerHighlighter->setChecked(Config()->isDecompilerAnnotationHighlighterEnabled());\n    connect(ui->useDecompilerHighlighter, &QCheckBox::toggled, this,\n            [](bool checked) { Config()->enableDecompilerAnnotationHighlighter(checked); });\n}\n\nAppearanceOptionsWidget::~AppearanceOptionsWidget() {}\n\nvoid AppearanceOptionsWidget::updateFontFromConfig()\n{\n    QFont currentFont = Config()->getBaseFont();\n    ui->fontSelectionLabel->setText(currentFont.toString());\n}\n\nvoid AppearanceOptionsWidget::updateThemeFromConfig(bool interfaceThemeChanged)\n{\n    // Disconnect currentIndexChanged because clearing the comboxBox and refiling it causes its\n    // index to change.\n    QSignalBlocker signalBlockerThemeBox(ui->themeComboBox);\n\n    ui->themeComboBox->clear();\n    for (auto &it : Configuration::cutterInterfaceThemesList()) {\n        ui->themeComboBox->addItem(it.name);\n    }\n    int currInterfaceThemeIndex = Config()->getInterfaceTheme();\n    if (currInterfaceThemeIndex >= Configuration::cutterInterfaceThemesList().size()) {\n        currInterfaceThemeIndex = 0;\n        Config()->setInterfaceTheme(currInterfaceThemeIndex);\n    }\n    ui->themeComboBox->setCurrentIndex(currInterfaceThemeIndex);\n    ui->colorComboBox->updateFromConfig(interfaceThemeChanged);\n    updateModificationButtons(ui->colorComboBox->currentText());\n}\n\nvoid AppearanceOptionsWidget::onFontZoomBoxValueChanged(int zoom)\n{\n    qreal zoomFactor = zoom / 100.0;\n    Config()->setZoomFactor(zoomFactor);\n}\n\nvoid AppearanceOptionsWidget::on_fontSelectionButton_clicked()\n{\n    QFont currentFont = Config()->getBaseFont();\n    bool ok;\n    QFont newFont = QFontDialog::getFont(&ok, currentFont, this, QString(),\n                                         QFontDialog::DontUseNativeDialog);\n    if (ok) {\n        Config()->setFont(newFont);\n    }\n}\n\nvoid AppearanceOptionsWidget::on_themeComboBox_currentIndexChanged(int index)\n{\n    Config()->setInterfaceTheme(index);\n    updateThemeFromConfig();\n}\n\nvoid AppearanceOptionsWidget::on_editButton_clicked()\n{\n    ColorThemeEditDialog dial;\n    dial.setWindowTitle(tr(\"Theme Editor - <%1>\").arg(ui->colorComboBox->currentText()));\n    dial.exec();\n    ui->colorComboBox->updateFromConfig(false);\n}\n\nvoid AppearanceOptionsWidget::on_copyButton_clicked()\n{\n    QString currColorTheme = ui->colorComboBox->currentText();\n\n    QString newThemeName;\n    do {\n        newThemeName = QInputDialog::getText(this, tr(\"Enter theme name\"), tr(\"Name:\"),\n                                             QLineEdit::Normal, currColorTheme + tr(\" - copy\"))\n                               .trimmed();\n        if (newThemeName.isEmpty()) {\n            return;\n        }\n        if (ThemeWorker().isThemeExist(newThemeName)) {\n            QMessageBox::information(this, tr(\"Theme Copy\"),\n                                     tr(\"Theme named %1 already exists.\").arg(newThemeName));\n        } else {\n            break;\n        }\n    } while (true);\n\n    ThemeWorker().copy(currColorTheme, newThemeName);\n    Config()->setColorTheme(newThemeName);\n    updateThemeFromConfig(false);\n}\n\nvoid AppearanceOptionsWidget::on_deleteButton_clicked()\n{\n    QString currTheme = ui->colorComboBox->currentText();\n    if (!ThemeWorker().isCustomTheme(currTheme)) {\n        QMessageBox::critical(this, tr(\"Error\"), ThemeWorker().deleteTheme(currTheme));\n        return;\n    }\n    int ret = QMessageBox::question(\n            this, tr(\"Delete\"), tr(\"Are you sure you want to delete <b>%1</b>?\").arg(currTheme));\n    if (ret == QMessageBox::Yes) {\n        QString err = ThemeWorker().deleteTheme(currTheme);\n        updateThemeFromConfig(false);\n        if (!err.isEmpty()) {\n            QMessageBox::critical(this, tr(\"Error\"), err);\n        }\n    }\n}\n\nvoid AppearanceOptionsWidget::on_importButton_clicked()\n{\n    QString fileName = QFileDialog::getOpenFileName(\n            this, \"\", QStandardPaths::writableLocation(QStandardPaths::HomeLocation));\n    if (fileName.isEmpty()) {\n        return;\n    }\n\n    QString err = ThemeWorker().importTheme(fileName);\n    QString themeName = QFileInfo(fileName).fileName();\n    if (err.isEmpty()) {\n        QMessageBox::information(\n                this, tr(\"Success\"),\n                tr(\"Color theme <b>%1</b> was successfully imported.\").arg(themeName));\n        Config()->setColorTheme(themeName);\n        updateThemeFromConfig(false);\n    } else {\n        QMessageBox::critical(this, tr(\"Error\"), err);\n    }\n}\n\nvoid AppearanceOptionsWidget::on_exportButton_clicked()\n{\n    QString theme = ui->colorComboBox->currentText();\n    QString file = QFileDialog::getSaveFileName(\n            this, \"\",\n            QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QDir::separator()\n                    + theme);\n    if (file.isEmpty()) {\n        return;\n    }\n\n    // User already gave his consent for this in QFileDialog::getSaveFileName()\n    if (QFileInfo(file).exists()) {\n        QFile(file).remove();\n    }\n    QString err = ThemeWorker().save(ThemeWorker().getTheme(theme), file);\n    if (err.isEmpty()) {\n        QMessageBox::information(this, tr(\"Success\"),\n                                 tr(\"Color theme <b>%1</b> was successfully exported.\").arg(theme));\n    } else {\n        QMessageBox::critical(this, tr(\"Error\"), err);\n    }\n}\n\nvoid AppearanceOptionsWidget::on_renameButton_clicked()\n{\n    QString currColorTheme = Config()->getColorTheme();\n    QString newName = QInputDialog::getText(this, tr(\"Enter new theme name\"), tr(\"Name:\"),\n                                            QLineEdit::Normal, currColorTheme);\n    if (newName.isEmpty() || newName == currColorTheme) {\n        return;\n    }\n\n    QString err = ThemeWorker().renameTheme(currColorTheme, newName);\n    if (!err.isEmpty()) {\n        QMessageBox::critical(this, tr(\"Error\"), err);\n    } else {\n        Config()->setColorTheme(newName);\n        updateThemeFromConfig(false);\n    }\n}\n\nvoid AppearanceOptionsWidget::onLanguageComboBoxCurrentIndexChanged(int)\n{\n    QVariant language = ui->languageComboBox->currentData();\n    if (language.canConvert<QLocale>()) {\n        Config()->setLocale(language.toLocale());\n        QMessageBox::information(this, tr(\"Language settings\"),\n                                 tr(\"Language will be changed after next application start.\"));\n    } else {\n        qWarning() << tr(\"Cannot set language, not found in available ones\");\n    }\n}\n\nvoid AppearanceOptionsWidget::updateModificationButtons(const QString &theme)\n{\n    bool editable = ThemeWorker().isCustomTheme(theme);\n    ui->editButton->setEnabled(editable);\n    ui->deleteButton->setEnabled(editable);\n    ui->renameButton->setEnabled(editable);\n}\n\nvoid AppearanceOptionsWidget::updateFromConfig()\n{\n    updateFontFromConfig();\n    updateThemeFromConfig(false);\n    ui->fontZoomBox->setValue(qRound(Config()->getZoomFactor() * 100));\n}\n\nQIcon AppearanceOptionsWidget::getIconFromSvg(const QString &fileName, const QColor &after,\n                                              const QColor &before)\n{\n    QFile file(fileName);\n    if (!file.open(QIODevice::ReadOnly)) {\n        return QIcon();\n    }\n    QString data = file.readAll();\n    data.replace(QRegularExpression(QString(\"#%1\").arg(before.isValid() ? before.name().remove(0, 1)\n                                                                        : \"[0-9a-fA-F]{6}\")),\n                 QString(\"%1\").arg(after.name()));\n\n    QSvgRenderer svgRenderer(data.toUtf8());\n    QPixmap pix(svgRenderer.defaultSize());\n    pix.fill(Qt::transparent);\n\n    QPainter pixPainter(&pix);\n    svgRenderer.render(&pixPainter);\n\n    return pix;\n}\n"
  },
  {
    "path": "src/dialogs/preferences/AppearanceOptionsWidget.h",
    "content": "\n#ifndef AppearanceOptionsWidget_H\n#define AppearanceOptionsWidget_H\n\n#include <QDialog>\n#include <QPushButton>\n#include <memory>\n\n#include \"core/Cutter.h\"\n\nclass PreferencesDialog;\n\nnamespace Ui {\nclass AppearanceOptionsWidget;\n}\n\nclass AppearanceOptionsWidget : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit AppearanceOptionsWidget(PreferencesDialog *dialog);\n    ~AppearanceOptionsWidget();\n\nprivate:\n    std::unique_ptr<Ui::AppearanceOptionsWidget> ui;\n\nprivate slots:\n    void updateFontFromConfig();\n    void updateThemeFromConfig(bool interfaceThemeChanged = true);\n\n    void on_fontSelectionButton_clicked();\n    void onFontZoomBoxValueChanged(int zoom);\n    void on_themeComboBox_currentIndexChanged(int index);\n    void on_copyButton_clicked();\n    void on_deleteButton_clicked();\n\n    /**\n     * @brief Imports theme file specified by user to custom themes\n     * directory.\n     */\n    void on_importButton_clicked();\n\n    /**\n     * @brief Exports current color theme to file\n     * specified by user.\n     */\n    void on_exportButton_clicked();\n\n    /**\n     * @brief Shows dialog to rename current color theme.\n     */\n    void on_renameButton_clicked();\n    void on_editButton_clicked();\n    void onLanguageComboBoxCurrentIndexChanged(int index);\n\nprivate:\n    void updateModificationButtons(const QString &theme);\n    void updateFromConfig();\n\n    /**\n     * @brief Changes all @a before colors in given @a fileName svg file to @a after\n     * and returns result icon. If @a before is not specified, changes all colors.\n     * @param fileName\n     * Path to svg file.\n     * @param after\n     * What color should be inserted instead of old one.\n     * @param before\n     * Color that should be repalced.\n     */\n    QIcon getIconFromSvg(const QString &fileName, const QColor &after,\n                         const QColor &before = QColor());\n};\n\n#endif // ASMOPTIONSDIALOG_H\n"
  },
  {
    "path": "src/dialogs/preferences/AppearanceOptionsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AppearanceOptionsWidget</class>\n <widget class=\"QWidget\" name=\"AppearanceOptionsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>619</width>\n    <height>225</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Appearance</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <property name=\"labelAlignment\">\n      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n     </property>\n     <property name=\"formAlignment\">\n      <set>Qt::AlignCenter</set>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"fontLabel\">\n       <property name=\"text\">\n        <string>Font:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <layout class=\"QHBoxLayout\" name=\"fontSelectionLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"fontSelectionLabel\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n           <horstretch>1</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"text\">\n          <string notr=\"true\"/>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"fontSelectionButton\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"text\">\n          <string>Select font</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer_2\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>30</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"label\">\n         <property name=\"text\">\n          <string>Zoom</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QSpinBox\" name=\"fontZoomBox\">\n         <property name=\"wrapping\">\n          <bool>false</bool>\n         </property>\n         <property name=\"frame\">\n          <bool>true</bool>\n         </property>\n         <property name=\"buttonSymbols\">\n          <enum>QAbstractSpinBox::PlusMinus</enum>\n         </property>\n         <property name=\"showGroupSeparator\" stdset=\"0\">\n          <bool>false</bool>\n         </property>\n         <property name=\"suffix\">\n          <string>%</string>\n         </property>\n         <property name=\"minimum\">\n          <number>10</number>\n         </property>\n         <property name=\"maximum\">\n          <number>3000</number>\n         </property>\n         <property name=\"singleStep\">\n          <number>10</number>\n         </property>\n         <property name=\"value\">\n          <number>100</number>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"languageLabel\">\n       <property name=\"text\">\n        <string>Language:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"languageComboBox\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"themeLabel\">\n       <property name=\"text\">\n        <string>Interface Theme:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"themeComboBox\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <item>\n        <property name=\"text\">\n         <string>Default</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Dark</string>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"colorLabel\">\n       <property name=\"text\">\n        <string>Color Theme:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n       <item>\n        <widget class=\"ColorThemeComboBox\" name=\"colorComboBox\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"editButton\">\n         <property name=\"toolTip\">\n          <string>Edit Theme</string>\n         </property>\n         <property name=\"text\">\n          <string/>\n         </property>\n         <property name=\"icon\">\n          <iconset resource=\"../../resources.qrc\">\n           <normaloff>:/img/icons/pencil_thin.svg</normaloff>:/img/icons/pencil_thin.svg</iconset>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"renameButton\">\n         <property name=\"toolTip\">\n          <string>Rename</string>\n         </property>\n         <property name=\"text\">\n          <string/>\n         </property>\n         <property name=\"icon\">\n          <iconset resource=\"../../resources.qrc\">\n           <normaloff>:/img/icons/rename.svg</normaloff>:/img/icons/rename.svg</iconset>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"copyButton\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"toolTip\">\n          <string>Copy</string>\n         </property>\n         <property name=\"text\">\n          <string/>\n         </property>\n         <property name=\"icon\">\n          <iconset resource=\"../../resources.qrc\">\n           <normaloff>:/img/icons/copy.svg</normaloff>:/img/icons/copy.svg</iconset>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"deleteButton\">\n         <property name=\"toolTip\">\n          <string>Delete</string>\n         </property>\n         <property name=\"text\">\n          <string/>\n         </property>\n         <property name=\"icon\">\n          <iconset resource=\"../../resources.qrc\">\n           <normaloff>:/img/icons/trash_bin.svg</normaloff>:/img/icons/trash_bin.svg</iconset>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"exportButton\">\n         <property name=\"toolTip\">\n          <string>Export</string>\n         </property>\n         <property name=\"text\">\n          <string/>\n         </property>\n         <property name=\"icon\">\n          <iconset resource=\"../../resources.qrc\">\n           <normaloff>:/img/icons/upload_black.svg</normaloff>:/img/icons/upload_black.svg</iconset>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"importButton\">\n         <property name=\"toolTip\">\n          <string>Import</string>\n         </property>\n         <property name=\"text\">\n          <string/>\n         </property>\n         <property name=\"icon\">\n          <iconset resource=\"../../resources.qrc\">\n           <normaloff>:/img/icons/download_black.svg</normaloff>:/img/icons/download_black.svg</iconset>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\"/>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QCheckBox\" name=\"useDecompilerHighlighter\">\n     <property name=\"toolTip\">\n      <string>Use information provided by decompiler when highlighting code.</string>\n     </property>\n     <property name=\"text\">\n      <string>Decompiler based highlighting</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n  <action name=\"actionSaveAsDefault\">\n   <property name=\"text\">\n    <string>Save as Default</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ColorThemeComboBox</class>\n   <extends>QComboBox</extends>\n   <header>widgets/ColorThemeComboBox.h</header>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"../../resources.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/AsmOptionsWidget.cpp",
    "content": "#include <QLabel>\n#include <QFontDialog>\n\n#include \"AsmOptionsWidget.h\"\n#include \"ui_AsmOptionsWidget.h\"\n\n#include \"PreferencesDialog.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n\nAsmOptionsWidget::AsmOptionsWidget(PreferencesDialog *dialog)\n    : QDialog(dialog), ui(new Ui::AsmOptionsWidget)\n{\n\n    ui->setupUi(this);\n\n    ui->syntaxComboBox->blockSignals(true);\n    for (const auto &syntax : Core()->getConfigOptions(\"asm.syntax\"))\n        ui->syntaxComboBox->addItem(syntax, syntax);\n    ui->syntaxComboBox->blockSignals(false);\n\n    checkboxes = { { ui->describeCheckBox, \"asm.describe\" },\n                   { ui->refptrCheckBox, \"asm.refptr\" },\n                   { ui->xrefCheckBox, \"asm.xrefs\" },\n                   { ui->bblineCheckBox, \"asm.bb.line\" },\n                   { ui->varsubCheckBox, \"asm.sub.var\" },\n                   { ui->varsubOnlyCheckBox, \"asm.sub.varonly\" },\n                   { ui->lbytesCheckBox, \"asm.lbytes\" },\n                   { ui->bytespaceCheckBox, \"asm.bytes.space\" },\n                   { ui->bytesCheckBox, \"asm.bytes\" },\n                   { ui->xrefCheckBox, \"asm.xrefs\" },\n                   { ui->indentCheckBox, \"asm.indent\" },\n                   { ui->offsetCheckBox, \"asm.offset\" },\n                   { ui->relOffsetCheckBox, \"asm.reloff\" },\n                   { ui->relOffFlagsCheckBox, \"asm.reloff.flags\" },\n                   { ui->slowCheckBox, \"asm.slow\" },\n                   { ui->linesCheckBox, \"asm.lines\" },\n                   { ui->fcnlinesCheckBox, \"asm.lines.fcn\" },\n                   { ui->flgoffCheckBox, \"asm.flags.offset\" },\n                   { ui->emuCheckBox, \"asm.emu\" },\n                   { ui->emuStrCheckBox, \"emu.str\" },\n                   { ui->varsumCheckBox, \"asm.var.summary\" },\n                   { ui->sizeCheckBox, \"asm.size\" },\n                   { ui->realnameCheckBox, \"asm.flags.real\" } };\n\n    QList<ConfigCheckbox>::iterator confCheckbox;\n\n    // Connect each checkbox from \"checkboxes\" to the generic signal \"checkboxEnabler\"\n    for (confCheckbox = checkboxes.begin(); confCheckbox != checkboxes.end(); ++confCheckbox) {\n        QString val = confCheckbox->config;\n        QCheckBox &cb = *confCheckbox->checkBox;\n        connect(confCheckbox->checkBox, &QCheckBox::stateChanged,\n                [this, val, &cb]() { checkboxEnabler(&cb, val); });\n    }\n\n    using indexSignalType = void (QComboBox::*)(int);\n    connect(ui->commentsComboBox, static_cast<indexSignalType>(&QComboBox::currentIndexChanged),\n            this, &AsmOptionsWidget::commentsComboBoxChanged);\n    connect(ui->asmComboBox, static_cast<indexSignalType>(&QComboBox::currentIndexChanged), this,\n            &AsmOptionsWidget::asmComboBoxChanged);\n    connect(ui->offsetCheckBox, &QCheckBox::toggled, this,\n            &AsmOptionsWidget::offsetCheckBoxToggled);\n    connect(ui->relOffsetCheckBox, &QCheckBox::toggled, this,\n            &AsmOptionsWidget::relOffCheckBoxToggled);\n    connect(Core(), &CutterCore::asmOptionsChanged, this,\n            &AsmOptionsWidget::updateAsmOptionsFromVars);\n\n    connect(ui->varTooltipsCheckBox, &QCheckBox::toggled, [this](bool checked) {\n        Config()->setShowVarTooltips(checked);\n        triggerAsmOptionsChanged();\n    });\n\n    updateAsmOptionsFromVars();\n}\n\nAsmOptionsWidget::~AsmOptionsWidget() {}\n\nvoid AsmOptionsWidget::updateAsmOptionsFromVars()\n{\n    bool cmtRightEnabled = Config()->getConfigBool(\"asm.cmt.right\");\n    ui->cmtcolSpinBox->blockSignals(true);\n    ui->cmtcolSpinBox->setValue(Config()->getConfigInt(\"asm.cmt.col\"));\n    ui->cmtcolSpinBox->blockSignals(false);\n    ui->cmtcolSpinBox->setEnabled(cmtRightEnabled);\n\n    bool offsetsEnabled = Config()->getConfigBool(\"asm.offset\");\n    ui->relOffsetLabel->setEnabled(offsetsEnabled);\n    ui->relOffsetCheckBox->setEnabled(offsetsEnabled);\n    ui->relOffFlagsCheckBox->setEnabled(Config()->getConfigBool(\"asm.offset\")\n                                        && Config()->getConfigBool(\"asm.reloff\"));\n\n    bool bytesEnabled = Config()->getConfigBool(\"asm.bytes\");\n    ui->bytespaceCheckBox->setEnabled(bytesEnabled);\n    ui->lbytesCheckBox->setEnabled(bytesEnabled);\n    ui->nbytesSpinBox->blockSignals(true);\n    ui->nbytesSpinBox->setValue(Config()->getConfigInt(\"asm.nbytes\"));\n    ui->nbytesSpinBox->blockSignals(false);\n    ui->nbytesLabel->setEnabled(bytesEnabled);\n    ui->nbytesSpinBox->setEnabled(bytesEnabled);\n    bool varsubEnabled = Config()->getConfigBool(\"asm.sub.var\");\n    ui->varsubOnlyCheckBox->setEnabled(varsubEnabled);\n\n    ui->asmComboBox->blockSignals(true);\n    if (Config()->getConfigBool(\"asm.esil\")) {\n        ui->asmComboBox->setCurrentIndex(1);\n    } else if (Config()->getConfigBool(\"asm.pseudo\")) {\n        ui->asmComboBox->setCurrentIndex(2);\n    } else {\n        ui->asmComboBox->setCurrentIndex(0);\n    }\n    ui->asmComboBox->blockSignals(false);\n\n    QString currentSyntax = Config()->getConfigString(\"asm.syntax\");\n    for (int i = 0; i < ui->syntaxComboBox->count(); i++) {\n        if (ui->syntaxComboBox->itemData(i) == currentSyntax) {\n            ui->syntaxComboBox->blockSignals(true);\n            ui->syntaxComboBox->setCurrentIndex(i);\n            ui->syntaxComboBox->blockSignals(false);\n            break;\n        }\n    }\n\n    ui->caseComboBox->blockSignals(true);\n    if (Config()->getConfigBool(\"asm.ucase\")) {\n        ui->caseComboBox->setCurrentIndex(1);\n    } else if (Config()->getConfigBool(\"asm.capitalize\")) {\n        ui->caseComboBox->setCurrentIndex(2);\n    } else {\n        ui->caseComboBox->setCurrentIndex(0);\n    }\n    ui->caseComboBox->blockSignals(false);\n\n    ui->asmTabsSpinBox->blockSignals(true);\n    ui->asmTabsSpinBox->setValue(Config()->getConfigInt(\"asm.tabs\"));\n    ui->asmTabsSpinBox->blockSignals(false);\n\n    ui->asmTabsOffSpinBox->blockSignals(true);\n    ui->asmTabsOffSpinBox->setValue(Config()->getConfigInt(\"asm.tabs.off\"));\n    ui->asmTabsOffSpinBox->blockSignals(false);\n\n    ui->previewCheckBox->blockSignals(true);\n    ui->previewCheckBox->setChecked(Config()->getPreviewValue());\n    ui->previewCheckBox->blockSignals(false);\n\n    qhelpers::setCheckedWithoutSignals(ui->varTooltipsCheckBox, Config()->getShowVarTooltips());\n\n    QList<ConfigCheckbox>::iterator confCheckbox;\n\n    // Set the value for each checkbox in \"checkboxes\" as it exists in the configuration\n    for (confCheckbox = checkboxes.begin(); confCheckbox != checkboxes.end(); ++confCheckbox) {\n        qhelpers::setCheckedWithoutSignals(confCheckbox->checkBox,\n                                           Config()->getConfigBool(confCheckbox->config));\n    }\n}\n\nvoid AsmOptionsWidget::resetToDefault()\n{\n    Config()->resetToDefaultAsmOptions();\n    updateAsmOptionsFromVars();\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::triggerAsmOptionsChanged()\n{\n    disconnect(Core(), &CutterCore::asmOptionsChanged, this,\n               &AsmOptionsWidget::updateAsmOptionsFromVars);\n    Core()->triggerAsmOptionsChanged();\n    connect(Core(), &CutterCore::asmOptionsChanged, this,\n            &AsmOptionsWidget::updateAsmOptionsFromVars);\n}\n\nvoid AsmOptionsWidget::on_cmtcolSpinBox_valueChanged(int value)\n{\n    Config()->setConfig(\"asm.cmt.col\", value);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_bytesCheckBox_toggled(bool checked)\n{\n    Config()->setConfig(\"asm.bytes\", checked);\n    ui->bytespaceCheckBox->setEnabled(checked);\n    ui->lbytesCheckBox->setEnabled(checked);\n    ui->nbytesLabel->setEnabled(checked);\n    ui->nbytesSpinBox->setEnabled(checked);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_nbytesSpinBox_valueChanged(int value)\n{\n    Config()->setConfig(\"asm.nbytes\", value);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_syntaxComboBox_currentIndexChanged(int index)\n{\n    Config()->setConfig(\"asm.syntax\",\n                        ui->syntaxComboBox->itemData(index).toString().toUtf8().constData());\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_caseComboBox_currentIndexChanged(int index)\n{\n    bool ucase;\n    bool capitalize;\n\n    switch (index) {\n    // lowercase\n    case 0:\n    default:\n        ucase = false;\n        capitalize = false;\n        break;\n\n    // uppercase\n    case 1:\n        ucase = true;\n        capitalize = false;\n        break;\n\n    case 2:\n        ucase = false;\n        capitalize = true;\n        break;\n    }\n\n    Config()->setConfig(\"asm.ucase\", ucase);\n    Config()->setConfig(\"asm.capitalize\", capitalize);\n\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_asmTabsSpinBox_valueChanged(int value)\n{\n    Config()->setConfig(\"asm.tabs\", value);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_asmTabsOffSpinBox_valueChanged(int value)\n{\n    Config()->setConfig(\"asm.tabs.off\", value);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_varsubCheckBox_toggled(bool checked)\n{\n    Config()->setConfig(\"asm.sub.var\", checked);\n    ui->varsubOnlyCheckBox->setEnabled(checked);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_previewCheckBox_toggled(bool checked)\n{\n    Config()->setPreviewValue(checked);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::on_buttonBox_clicked(QAbstractButton *button)\n{\n    switch (ui->buttonBox->buttonRole(button)) {\n    case QDialogButtonBox::ButtonRole::ResetRole:\n        resetToDefault();\n        break;\n    default:\n        break;\n    }\n}\n\nvoid AsmOptionsWidget::commentsComboBoxChanged(int index)\n{\n    // Check if comments should be set to right\n    Config()->setConfig(\"asm.cmt.right\", index != 1);\n    // Check if comments are disabled\n    ui->cmtcolSpinBox->setEnabled(index != 1);\n\n    // Show\\Hide comments in disassembly based on whether \"Off\" is selected\n    Config()->setConfig(\"asm.comments\", index != 2);\n    // Enable comments-related checkboxes only if Comments are enabled\n    ui->xrefCheckBox->setEnabled(index != 2);\n    ui->refptrCheckBox->setEnabled(index != 2);\n    ui->describeCheckBox->setEnabled(index != 2);\n\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::asmComboBoxChanged(int index)\n{\n    // Check if ESIL enabled\n    Config()->setConfig(\"asm.esil\", index == 1);\n\n    // Check if Pseudocode enabled\n    Config()->setConfig(\"asm.pseudo\", index == 2);\n    triggerAsmOptionsChanged();\n}\n\nvoid AsmOptionsWidget::offsetCheckBoxToggled(bool checked)\n{\n    ui->relOffsetLabel->setEnabled(checked);\n    ui->relOffsetCheckBox->setEnabled(checked);\n    ui->relOffFlagsCheckBox->setEnabled(checked && Config()->getConfigBool(\"asm.reloff\"));\n}\n\nvoid AsmOptionsWidget::relOffCheckBoxToggled(bool checked)\n{\n    ui->relOffFlagsCheckBox->setEnabled(checked && Config()->getConfigBool(\"asm.offset\"));\n}\n\n/**\n * @brief A generic signal to handle the simple cases where a checkbox is toggled\n * while it only responsible for a single independent boolean configuration eval.\n * @param checkBox - The checkbox which is responsible for the siganl\n * @param config - the configuration string to be toggled\n */\nvoid AsmOptionsWidget::checkboxEnabler(QCheckBox *checkBox, QString config)\n{\n    Config()->setConfig(config, checkBox->isChecked());\n    triggerAsmOptionsChanged();\n}\n"
  },
  {
    "path": "src/dialogs/preferences/AsmOptionsWidget.h",
    "content": "\n#ifndef ASMOPTIONSWIDGET_H\n#define ASMOPTIONSWIDGET_H\n\n#include <QDialog>\n#include <QPushButton>\n#include <memory>\n\n#include \"core/Cutter.h\"\n\nclass PreferencesDialog;\n\nnamespace Ui {\nclass AsmOptionsWidget;\n}\n\nclass AsmOptionsWidget : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit AsmOptionsWidget(PreferencesDialog *dialog);\n    ~AsmOptionsWidget();\n\nprivate:\n    std::unique_ptr<Ui::AsmOptionsWidget> ui;\n    struct ConfigCheckbox\n    {\n        QCheckBox *checkBox;\n        QString config;\n    };\n    QList<ConfigCheckbox> checkboxes;\n\n    void triggerAsmOptionsChanged();\n\nprivate slots:\n    void resetToDefault();\n\n    void updateAsmOptionsFromVars();\n\n    void on_cmtcolSpinBox_valueChanged(int value);\n\n    void on_syntaxComboBox_currentIndexChanged(int index);\n    void on_caseComboBox_currentIndexChanged(int index);\n    void on_asmTabsSpinBox_valueChanged(int value);\n    void on_asmTabsOffSpinBox_valueChanged(int value);\n    void on_nbytesSpinBox_valueChanged(int value);\n\n    void on_bytesCheckBox_toggled(bool checked);\n    void on_varsubCheckBox_toggled(bool checked);\n    void on_previewCheckBox_toggled(bool checked);\n\n    void on_buttonBox_clicked(QAbstractButton *button);\n\n    void commentsComboBoxChanged(int index);\n    void asmComboBoxChanged(int index);\n    void offsetCheckBoxToggled(bool checked);\n    void relOffCheckBoxToggled(bool checked);\n    void checkboxEnabler(QCheckBox *checkbox, QString config);\n};\n\n#endif // ASMOPTIONSWIDGET_H\n"
  },
  {
    "path": "src/dialogs/preferences/AsmOptionsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AsmOptionsWidget</class>\n <widget class=\"QWidget\" name=\"AsmOptionsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>652</width>\n    <height>686</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Disassembly</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n     <property name=\"leftMargin\">\n      <number>16</number>\n     </property>\n     <item>\n      <widget class=\"QTabWidget\" name=\"asmOptionsTab\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"currentIndex\">\n        <number>0</number>\n       </property>\n       <widget class=\"QWidget\" name=\"asmStyleTab\">\n        <attribute name=\"title\">\n         <string>Style</string>\n        </attribute>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n         <item>\n          <widget class=\"QScrollArea\" name=\"scrollArea\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>686</width>\n              <height>886</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n             <item>\n              <widget class=\"QGroupBox\" name=\"disassemblyGroupBox\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"title\">\n                <string>Disassembly</string>\n               </property>\n               <layout class=\"QGridLayout\" name=\"gridLayout\">\n                <item row=\"5\" column=\"2\">\n                 <widget class=\"QComboBox\" name=\"caseComboBox\">\n                  <item>\n                   <property name=\"text\">\n                    <string>Lowercase</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>Uppercase (asm.ucase)</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>Capitalize (asm.capitalize)</string>\n                   </property>\n                  </item>\n                 </widget>\n                </item>\n                <item row=\"17\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"asmTabsLabel\">\n                  <property name=\"text\">\n                   <string>Tabs in assembly (asm.tabs):</string>\n                  </property>\n                  <property name=\"textInteractionFlags\">\n                   <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"11\" column=\"1\">\n                 <widget class=\"QCheckBox\" name=\"bytesCheckBox\">\n                  <property name=\"text\">\n                   <string>Display the bytes of each instruction (asm.bytes)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"label\">\n                  <property name=\"text\">\n                   <string>Show Disassembly as:</string>\n                  </property>\n                  <property name=\"textInteractionFlags\">\n                   <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"1\" colspan=\"2\">\n                 <spacer name=\"verticalSpacer_3\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Fixed</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>10</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n                <item row=\"13\" column=\"1\">\n                 <widget class=\"QCheckBox\" name=\"realnameCheckBox\">\n                  <property name=\"text\">\n                   <string>Display flags' real name (asm.flags.real)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"8\" column=\"1\">\n                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n                  <item>\n                   <widget class=\"QLabel\" name=\"relOffsetLabel\">\n                    <property name=\"text\">\n                     <string>Show offsets relative to:</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <widget class=\"QCheckBox\" name=\"relOffsetCheckBox\">\n                    <property name=\"text\">\n                     <string>Functions (asm.reloff)</string>\n                    </property>\n                   </widget>\n                  </item>\n                 </layout>\n                </item>\n                <item row=\"18\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"asmTabsOffLabel\">\n                  <property name=\"text\">\n                   <string>The number of tabulate spaces after the offset (asm.tabs.off):</string>\n                  </property>\n                  <property name=\"textInteractionFlags\">\n                   <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"19\" column=\"1\" colspan=\"2\">\n                 <widget class=\"QCheckBox\" name=\"indentCheckBox\">\n                  <property name=\"text\">\n                   <string>Indent disassembly based on reflines depth (asm.indent)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"7\" column=\"1\">\n                 <widget class=\"QCheckBox\" name=\"offsetCheckBox\">\n                  <property name=\"text\">\n                   <string>Show offsets (asm.offset)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"3\" column=\"2\" colspan=\"2\">\n                 <widget class=\"QComboBox\" name=\"asmComboBox\">\n                  <item>\n                   <property name=\"text\">\n                    <string>Normal</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>ESIL (asm.esil)</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>Pseudocode (asm.pseudo)</string>\n                   </property>\n                  </item>\n                 </widget>\n                </item>\n                <item row=\"14\" column=\"1\" colspan=\"2\">\n                 <widget class=\"QCheckBox\" name=\"bblineCheckBox\">\n                  <property name=\"text\">\n                   <string>Show empty line after every basic block (asm.bb.line)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"15\" column=\"1\">\n                 <widget class=\"QCheckBox\" name=\"previewCheckBox\">\n                  <property name=\"text\">\n                   <string>Show preview when hovering</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"4\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"syntaxLabel\">\n                  <property name=\"text\">\n                   <string>Syntax (asm.syntax):</string>\n                  </property>\n                  <property name=\"textInteractionFlags\">\n                   <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"21\" column=\"1\" colspan=\"2\">\n                 <widget class=\"QCheckBox\" name=\"bytespaceCheckBox\">\n                  <property name=\"text\">\n                   <string>Separate bytes with whitespace (asm.bytes.space)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"18\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"asmTabsOffSpinBox\">\n                  <property name=\"maximum\">\n                   <number>100</number>\n                  </property>\n                  <property name=\"singleStep\">\n                   <number>5</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"12\" column=\"1\">\n                 <widget class=\"QLabel\" name=\"nbytesLabel\">\n                  <property name=\"enabled\">\n                   <bool>true</bool>\n                  </property>\n                  <property name=\"text\">\n                   <string>Number of bytes to display (asm.nbytes):</string>\n                  </property>\n                  <property name=\"textInteractionFlags\">\n                   <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"20\" column=\"1\" colspan=\"2\">\n                 <widget class=\"QCheckBox\" name=\"lbytesCheckBox\">\n                  <property name=\"text\">\n                   <string>Align bytes to the left (asm.lbytes)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"17\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"asmTabsSpinBox\">\n                  <property name=\"maximum\">\n                   <number>100</number>\n                  </property>\n                  <property name=\"singleStep\">\n                   <number>5</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"8\" column=\"2\">\n                 <widget class=\"QCheckBox\" name=\"relOffFlagsCheckBox\">\n                  <property name=\"text\">\n                   <string>Flags (asm.reloff.flags)</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"4\" column=\"2\">\n                 <widget class=\"QComboBox\" name=\"syntaxComboBox\"/>\n                </item>\n                <item row=\"12\" column=\"2\">\n                 <widget class=\"QSpinBox\" name=\"nbytesSpinBox\">\n                  <property name=\"enabled\">\n                   <bool>true</bool>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"16\" column=\"1\">\n                 <widget class=\"QCheckBox\" name=\"varTooltipsCheckBox\">\n                  <property name=\"text\">\n                   <string>Show known variable values when hovering</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QGroupBox\" name=\"commentsGroupBox\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n                 <horstretch>0</horstretch>\n                 <verstretch>1</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"title\">\n                <string>Comments</string>\n               </property>\n               <layout class=\"QGridLayout\" name=\"gridLayout_6\">\n                <item row=\"1\" column=\"0\">\n                 <layout class=\"QGridLayout\" name=\"gridLayout_5\" columnminimumwidth=\"0,0\">\n                  <item row=\"1\" column=\"0\" colspan=\"2\">\n                   <widget class=\"QCheckBox\" name=\"describeCheckBox\">\n                    <property name=\"text\">\n                     <string>Show opcode description (asm.describe)</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"0\" column=\"1\">\n                   <widget class=\"QComboBox\" name=\"commentsComboBox\">\n                    <item>\n                     <property name=\"text\">\n                      <string>Normal</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>Above instructions</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>Off</string>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                  <item row=\"0\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"label_2\">\n                    <property name=\"text\">\n                     <string>Show comments:</string>\n                    </property>\n                    <property name=\"textInteractionFlags\">\n                     <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"4\" column=\"1\">\n                   <widget class=\"QSpinBox\" name=\"cmtcolSpinBox\">\n                    <property name=\"maximum\">\n                     <number>100</number>\n                    </property>\n                    <property name=\"singleStep\">\n                     <number>5</number>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"4\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"cmtcolLabel\">\n                    <property name=\"text\">\n                     <string>Column to align comments (asm.cmt.col):</string>\n                    </property>\n                    <property name=\"textInteractionFlags\">\n                     <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"2\" column=\"0\">\n                   <widget class=\"QCheckBox\" name=\"xrefCheckBox\">\n                    <property name=\"text\">\n                     <string>Show x-refs (asm.xrefs)</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"3\" column=\"0\" colspan=\"2\">\n                   <widget class=\"QCheckBox\" name=\"refptrCheckBox\">\n                    <property name=\"text\">\n                     <string>Show refpointer information (asm.refptr)</string>\n                    </property>\n                   </widget>\n                  </item>\n                 </layout>\n                </item>\n                <item row=\"0\" column=\"0\">\n                 <spacer name=\"verticalSpacer_2\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Fixed</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>10</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"tab_2\">\n        <attribute name=\"title\">\n         <string>Metadata</string>\n        </attribute>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n         <item>\n          <widget class=\"QScrollArea\" name=\"scrollArea_2\">\n           <property name=\"frameShape\">\n            <enum>QFrame::StyledPanel</enum>\n           </property>\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_2\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>518</width>\n              <height>326</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n             <item>\n              <widget class=\"QCheckBox\" name=\"slowCheckBox\">\n               <property name=\"text\">\n                <string>Slow Analysis (asm.slow)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"linesCheckBox\">\n               <property name=\"text\">\n                <string>Show jump lines (asm.lines)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"fcnlinesCheckBox\">\n               <property name=\"text\">\n                <string>Show function boundary lines (asm.lines.fcn)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"flgoffCheckBox\">\n               <property name=\"text\">\n                <string>Show offset before flags (asm.flags.off)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"emuCheckBox\">\n               <property name=\"text\">\n                <string>Run ESIL emulation analysis (asm.emu)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"emuStrCheckBox\">\n               <property name=\"text\">\n                <string>Show only strings if any in the asm.emu output (emu.str)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"sizeCheckBox\">\n               <property name=\"text\">\n                <string>Show size of opcodes in disassembly (asm.size)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"varsumCheckBox\">\n               <property name=\"text\">\n                <string>Show variables summary instead of full list (asm.var.summary)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"varsubCheckBox\">\n               <property name=\"text\">\n                <string>Substitute variables in disassembly (asm.sub.var)</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"varsubOnlyCheckBox\">\n               <property name=\"text\">\n                <string>Substitute entire variable expressions with names (asm.sub.varonly)</string>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"verticalSpacer_4\">\n           <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>20</width>\n             <height>40</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </widget>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Fixed</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::RestoreDefaults</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n  <action name=\"actionSaveAsDefault\">\n   <property name=\"text\">\n    <string>Save as Default</string>\n   </property>\n  </action>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/ColorThemeEditDialog.cpp",
    "content": "#include \"ColorThemeEditDialog.h\"\n#include \"ui_ColorThemeEditDialog.h\"\n\n#include \"common/ColorThemeWorker.h\"\n#include \"common/Configuration.h\"\n\n#include \"widgets/ColorThemeListView.h\"\n#include \"widgets/DisassemblyWidget.h\"\n\n#include <QScreen>\n#include <QKeyEvent>\n#include <QSortFilterProxyModel>\n\nColorThemeEditDialog::ColorThemeEditDialog(QWidget *parent)\n    : QDialog(parent),\n      ui(new Ui::ColorThemeEditDialog),\n      configSignalBlocker(\n              Config()), // Blocks signals from Config to avoid updating of widgets during editing\n      colorTheme(Config()->getColorTheme())\n{\n    showAlphaOptions = { \"gui.overview.border\", \"gui.overview.fill\", \"wordHighlightBg\",\n                         \"wordHighlightFg\",     \"lineHighlight\",     \"searchCurrent\",\n                         \"searchHighlight\" };\n    ui->setupUi(this);\n    ui->colorComboBox->setShowOnlyCustom(true);\n\n    previewDisasmWidget = new DisassemblyWidget(nullptr);\n    previewDisasmWidget->setObjectName(\"Preview Disasm\");\n    previewDisasmWidget->setPreviewMode(true);\n#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))\n    // default size limit is acceptable\n    if (auto screen = qApp->screenAt(previewDisasmWidget->pos())) {\n        previewDisasmWidget->setMinimumSize(screen->size() * 0.5);\n    }\n#endif\n    previewDisasmWidget->setWindowTitle(tr(\"Disassembly Preview\"));\n    previewDisasmWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);\n    ui->colorPickerAndPreviewLayout->addWidget(previewDisasmWidget);\n\n    connect(ui->colorThemeListView, &ColorThemeListView::blink, previewDisasmWidget,\n            &DisassemblyWidget::colorsUpdatedSlot);\n\n    connect(ui->colorThemeListView, &ColorThemeListView::itemChanged, this,\n            [this](const QColor &color) {\n                ui->colorPicker->updateColor(color);\n                QString optionName = ui->colorThemeListView->currentIndex()\n                                             .data(Qt::UserRole)\n                                             .value<ColorOption>()\n                                             .optionName;\n                ui->colorPicker->setAlphaEnabled(showAlphaOptions.contains(optionName));\n            });\n\n    connect(ui->filterLineEdit, &QLineEdit::textChanged, this, [this](const QString &s) {\n        static_cast<QSortFilterProxyModel *>(ui->colorThemeListView->model())\n                ->setFilterFixedString(s);\n    });\n\n    ui->colorThemeListView->setCurrentIndex(ui->colorThemeListView->model()->index(0, 0));\n\n    connect(ui->colorPicker, &ColorPicker::colorChanged, this,\n            &ColorThemeEditDialog::colorOptionChanged);\n\n    connect(ui->colorComboBox, &ColorThemeComboBox::currentTextChanged, this,\n            &ColorThemeEditDialog::editThemeChanged);\n}\n\nColorThemeEditDialog::~ColorThemeEditDialog()\n{\n    delete ui;\n    previewDisasmWidget->deleteLater();\n}\n\nvoid ColorThemeEditDialog::accept()\n{\n    colorTheme = Config()->getColorTheme();\n    ColorThemeWorker::Theme sch = ui->colorThemeListView->colorSettingsModel()->getTheme();\n    if (ThemeWorker().isCustomTheme(colorTheme)) {\n        QString err = ThemeWorker().save(sch, colorTheme);\n        if (!err.isEmpty()) {\n            QMessageBox::critical(this, tr(\"Error\"), err);\n            return;\n        }\n    }\n\n    configSignalBlocker.unblock();\n    Config()->setColorTheme(colorTheme);\n\n    QDialog::accept();\n}\n\nvoid ColorThemeEditDialog::reject()\n{\n    if (themeWasEdited(ui->colorComboBox->currentText())\n        && QMessageBox::question(this, tr(\"Unsaved changes\"),\n                                 tr(\"Are you sure you want to exit without saving? \"\n                                    \"All changes will be lost.\"))\n                == QMessageBox::No) {\n        return;\n    }\n    configSignalBlocker.unblock();\n    Config()->setColorTheme(colorTheme);\n\n    QDialog::reject();\n}\n\nvoid ColorThemeEditDialog::keyPressEvent(QKeyEvent *event)\n{\n    switch (event->key()) {\n    case Qt::Key_Escape:\n        if (ui->colorPicker->isPickingFromScreen()) {\n            ui->colorPicker->stopPickingFromScreen();\n        }\n        // fallthrough\n    case Qt::Key_Return:\n        event->accept();\n        return;\n    default:\n        QDialog::keyPressEvent(event);\n    }\n}\n\nvoid ColorThemeEditDialog::colorOptionChanged(const QColor &newColor)\n{\n    QModelIndex currIndex = ui->colorThemeListView->currentIndex();\n\n    if (!currIndex.isValid()) {\n        return;\n    }\n\n    ColorOption currOption = currIndex.data(Qt::UserRole).value<ColorOption>();\n    currOption.color = newColor;\n    currOption.changed = true;\n    ui->colorThemeListView->model()->setData(currIndex, QVariant::fromValue(currOption));\n\n    Config()->setColor(currOption.optionName, currOption.color);\n    if (!ColorThemeWorker::cutterSpecificOptions.contains(currOption.optionName)) {\n        Core()->setColor(currOption.optionName, currOption.color.name());\n    }\n    previewDisasmWidget->colorsUpdatedSlot();\n}\n\nvoid ColorThemeEditDialog::editThemeChanged(const QString &newTheme)\n{\n    if (themeWasEdited(colorTheme)) {\n        int ret = QMessageBox::question(this, tr(\"Unsaved changes\"),\n                                        tr(\"Are you sure you want to exit without saving? \"\n                                           \"All changes will be lost.\"));\n        if (ret == QMessageBox::No) {\n            QSignalBlocker s(ui->colorComboBox); // avoid second call of this func\n            int index = ui->colorComboBox->findText(colorTheme);\n            index = index == -1 ? 0 : index;\n            ui->colorComboBox->setCurrentIndex(index);\n            Config()->setColorTheme(colorTheme);\n            return;\n        }\n    }\n    colorTheme = newTheme;\n    ui->colorThemeListView->colorSettingsModel()->updateTheme();\n    previewDisasmWidget->colorsUpdatedSlot();\n    setWindowTitle(tr(\"Theme Editor - <%1>\").arg(colorTheme));\n}\n\nbool ColorThemeEditDialog::themeWasEdited(const QString &theme) const\n{\n    auto model = ui->colorThemeListView->colorSettingsModel();\n    return ThemeWorker().getTheme(theme) != model->getTheme();\n}\n"
  },
  {
    "path": "src/dialogs/preferences/ColorThemeEditDialog.h",
    "content": "#ifndef COLORTHEMEEDITDIALOG_H\n#define COLORTHEMEEDITDIALOG_H\n\n#include <QDialog>\n\nclass DisassemblyWidget;\n\nnamespace Ui {\nclass ColorThemeEditDialog;\n}\n\nclass ColorThemeEditDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit ColorThemeEditDialog(QWidget *parent = nullptr);\n    ~ColorThemeEditDialog() override;\n\npublic slots:\n    void accept() override;\n    void reject() override;\n\nprotected slots:\n    void keyPressEvent(QKeyEvent *event) override;\n\nprivate slots:\n    /**\n     * @brief Sets @a newColor color for current option.\n     * @param newColor\n     * New color for current color option.\n     */\n    void colorOptionChanged(const QColor &newColor);\n\n    /**\n     * @brief Changes current theme to edit.\n     * @param newTheme\n     * Name of new theme.\n     */\n    void editThemeChanged(const QString &newTheme);\n\nprivate:\n    bool themeWasEdited(const QString &theme) const;\n\nprivate:\n    QList<QString> showAlphaOptions;\n    Ui::ColorThemeEditDialog *ui;\n    QSignalBlocker configSignalBlocker;\n    DisassemblyWidget *previewDisasmWidget;\n    QString colorTheme;\n};\n\n#endif // COLORTHEMEEDITDIALOG_H\n"
  },
  {
    "path": "src/dialogs/preferences/ColorThemeEditDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ColorThemeEditDialog</class>\n <widget class=\"QDialog\" name=\"ColorThemeEditDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1437</width>\n    <height>617</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n         <item>\n          <widget class=\"QLabel\" name=\"label\">\n           <property name=\"text\">\n            <string>Color Theme:</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"ColorThemeComboBox\" name=\"colorComboBox\"/>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n           <item>\n            <widget class=\"QLineEdit\" name=\"filterLineEdit\">\n             <property name=\"placeholderText\">\n              <string>Search</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"ColorThemeListView\" name=\"colorThemeListView\" native=\"true\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>400</width>\n               <height>400</height>\n              </size>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <layout class=\"QVBoxLayout\" name=\"colorPickerAndPreviewLayout\">\n           <item>\n            <widget class=\"ColorPicker\" name=\"colorPicker\" native=\"true\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>0</width>\n               <height>0</height>\n              </size>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ColorPicker</class>\n   <extends>QWidget</extends>\n   <header>widgets/ColorPicker.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ColorThemeListView</class>\n   <extends>QWidget</extends>\n   <header>widgets/ColorThemeListView.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ColorThemeComboBox</class>\n   <extends>QComboBox</extends>\n   <header>widgets/ColorThemeComboBox.h</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ColorThemeEditDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ColorThemeEditDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/DebugOptionsWidget.cpp",
    "content": "#include \"DebugOptionsWidget.h\"\n#include \"ui_DebugOptionsWidget.h\"\n#include <QLabel>\n#include <QTimer>\n#include <QComboBox>\n#include <QShortcut>\n#include <QFontDialog>\n#include \"PreferencesDialog.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n\nDebugOptionsWidget::DebugOptionsWidget(PreferencesDialog *dialog)\n    : QDialog(dialog), ui(new Ui::DebugOptionsWidget)\n{\n    ui->setupUi(this);\n\n    updateDebugPlugin();\n\n    connect(ui->pluginComboBox, &QComboBox::currentTextChanged, this,\n            &DebugOptionsWidget::onDebugPluginChanged);\n}\n\nDebugOptionsWidget::~DebugOptionsWidget() {}\n\nvoid DebugOptionsWidget::updateDebugPlugin()\n{\n    ui->traceContinue->setChecked(Config()->getConfigBool(\"dbg.trace_continue\"));\n    connect(ui->traceContinue, &QCheckBox::toggled, this,\n            [](bool checked) { Config()->setConfig(\"dbg.trace_continue\", checked); });\n    ui->esilBreakOnInvalid->setChecked(Config()->getConfigBool(\"esil.breakoninvalid\"));\n    connect(ui->esilBreakOnInvalid, &QCheckBox::toggled, this,\n            [](bool checked) { Config()->setConfig(\"esil.breakoninvalid\", checked); });\n\n    disconnect(ui->pluginComboBox, &QComboBox::currentTextChanged, this,\n               &DebugOptionsWidget::onDebugPluginChanged);\n\n    QStringList plugins = Core()->getDebugPlugins();\n    for (const QString &str : plugins)\n        ui->pluginComboBox->addItem(str);\n\n    QString plugin = Core()->getActiveDebugPlugin();\n    ui->pluginComboBox->setCurrentText(plugin);\n\n    connect(ui->pluginComboBox, &QComboBox::currentTextChanged, this,\n            &DebugOptionsWidget::onDebugPluginChanged);\n\n    QString stackSize = Core()->getConfig(\"esil.stack.size\");\n    ui->stackSize->setText(stackSize);\n    ui->stackSize->setPlaceholderText(stackSize);\n    QString stackAddr = Core()->getConfig(\"esil.stack.addr\");\n    ui->stackAddr->setText(stackAddr);\n    ui->stackAddr->setPlaceholderText(stackAddr);\n    connect(ui->stackAddr, &QLineEdit::editingFinished, this, &DebugOptionsWidget::updateStackAddr);\n    connect(ui->stackSize, &QLineEdit::editingFinished, this, &DebugOptionsWidget::updateStackSize);\n}\n\nvoid DebugOptionsWidget::onDebugPluginChanged(const QString &plugin)\n{\n    Core()->setDebugPlugin(plugin);\n}\n\nvoid DebugOptionsWidget::updateStackSize()\n{\n    QString newSize = ui->stackSize->text();\n    Core()->setConfig(\"esil.stack.size\", newSize);\n    ui->stackSize->setPlaceholderText(newSize);\n}\n\nvoid DebugOptionsWidget::updateStackAddr()\n{\n    QString newAddr = ui->stackAddr->text();\n    Core()->setConfig(\"esil.stack.addr\", newAddr);\n    ui->stackAddr->setPlaceholderText(newAddr);\n}\n"
  },
  {
    "path": "src/dialogs/preferences/DebugOptionsWidget.h",
    "content": "\n#pragma once\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n\nclass PreferencesDialog;\n\nnamespace Ui {\nclass DebugOptionsWidget;\n}\n\nclass DebugOptionsWidget : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit DebugOptionsWidget(PreferencesDialog *dialog);\n    ~DebugOptionsWidget();\n\nprivate:\n    std::unique_ptr<Ui::DebugOptionsWidget> ui;\n\nprivate slots:\n    void updateDebugPlugin();\n    void updateStackAddr();\n    void updateStackSize();\n    void onDebugPluginChanged(const QString &index);\n};\n"
  },
  {
    "path": "src/dialogs/preferences/DebugOptionsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DebugOptionsWidget</class>\n <widget class=\"QWidget\" name=\"DebugOptionsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>742</width>\n    <height>698</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Debug</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_1\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n     <item>\n      <layout class=\"QFormLayout\" name=\"formLayout_1\">\n       <property name=\"sizeConstraint\">\n        <enum>QLayout::SetDefaultConstraint</enum>\n       </property>\n       <property name=\"fieldGrowthPolicy\">\n        <enum>QFormLayout::ExpandingFieldsGrow</enum>\n       </property>\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"pluginLabel\">\n         <property name=\"text\">\n          <string>Debug plugin:</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QComboBox\" name=\"pluginComboBox\"/>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"EsilOptions\">\n       <property name=\"title\">\n        <string>ESIL options</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n        <property name=\"sizeConstraint\">\n         <enum>QLayout::SetDefaultConstraint</enum>\n        </property>\n        <property name=\"topMargin\">\n         <number>24</number>\n        </property>\n        <item>\n         <widget class=\"QCheckBox\" name=\"esilBreakOnInvalid\">\n          <property name=\"text\">\n           <string>Break esil execution when instruction is invalid (esil.breakoninvalid)</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QFormLayout\" name=\"formLayout_2\">\n          <property name=\"sizeConstraint\">\n           <enum>QLayout::SetDefaultConstraint</enum>\n          </property>\n          <property name=\"fieldGrowthPolicy\">\n           <enum>QFormLayout::FieldsStayAtSizeHint</enum>\n          </property>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"stackAddrLabel\">\n            <property name=\"text\">\n             <string>ESIL stack address:</string>\n            </property>\n            <property name=\"scaledContents\">\n             <bool>false</bool>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"stackSizeLabel\">\n            <property name=\"toolTip\">\n             <string>Hide text when zooming out and it is smaller than the given value. Higher values can increase Performance.</string>\n            </property>\n            <property name=\"text\">\n             <string>ESIL stack size:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"stackAddr\"/>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"stackSize\"/>\n          </item>\n         </layout>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"TraceOptions\">\n       <property name=\"title\">\n        <string>Trace options</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n        <property name=\"topMargin\">\n         <number>24</number>\n        </property>\n        <item>\n         <widget class=\"QCheckBox\" name=\"traceContinue\">\n          <property name=\"text\">\n           <string>Trace each step during continue in a trace session (dbg.trace_continue)</string>\n          </property>\n          <property name=\"toolTip\">\n           <string>Disabling this option means that stepping back after continue will return to the previous PC. Significantly improves performance.</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/GraphOptionsWidget.cpp",
    "content": "#include <QLabel>\n#include <QFontDialog>\n\n#include \"GraphOptionsWidget.h\"\n#include \"ui_GraphOptionsWidget.h\"\n\n#include \"PreferencesDialog.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n\nGraphOptionsWidget::GraphOptionsWidget(PreferencesDialog *dialog)\n    : QDialog(dialog), ui(new Ui::GraphOptionsWidget)\n{\n    ui->setupUi(this);\n    ui->checkTransparent->setChecked(Config()->getBitmapTransparentState());\n    ui->blockEntryCheckBox->setChecked(Config()->getGraphBlockEntryOffset());\n    ui->graphPreviewCheckBox->setChecked(Config()->getGraphPreview());\n    ui->bitmapGraphScale->setValue(Config()->getBitmapExportScaleFactor() * 100.0);\n    updateOptionsFromVars();\n\n    connect<void (QDoubleSpinBox::*)(double)>(ui->bitmapGraphScale, (&QDoubleSpinBox::valueChanged),\n                                              this,\n                                              &GraphOptionsWidget::bitmapGraphScaleValueChanged);\n    connect(ui->checkTransparent, &QCheckBox::stateChanged, this,\n            &GraphOptionsWidget::checkTransparentStateChanged);\n    connect(ui->blockEntryCheckBox, &QCheckBox::stateChanged, this,\n            &GraphOptionsWidget::checkGraphBlockEntryOffsetChanged);\n\n    connect(Core(), &CutterCore::graphOptionsChanged, this,\n            &GraphOptionsWidget::updateOptionsFromVars);\n    QSpinBox *graphSpacingWidgets[] = { ui->horizontalEdgeSpacing, ui->horizontalBlockSpacing,\n                                        ui->verticalEdgeSpacing, ui->verticalBlockSpacing };\n    for (auto widget : graphSpacingWidgets) {\n        connect<void (QSpinBox::*)(int)>(widget, &QSpinBox::valueChanged, this,\n                                         &GraphOptionsWidget::layoutSpacingChanged);\n    }\n}\n\nGraphOptionsWidget::~GraphOptionsWidget() {}\n\nvoid GraphOptionsWidget::updateOptionsFromVars()\n{\n    ui->maxColsSpinBox->blockSignals(true);\n    ui->maxColsSpinBox->setValue(Config()->getGraphBlockMaxChars());\n    ui->maxColsSpinBox->blockSignals(false);\n    ui->minFontSizeSpinBox->blockSignals(true);\n    ui->minFontSizeSpinBox->setValue(Config()->getGraphMinFontSize());\n    ui->minFontSizeSpinBox->blockSignals(false);\n    auto blockSpacing = Config()->getGraphBlockSpacing();\n    ui->horizontalBlockSpacing->setValue(blockSpacing.x());\n    ui->verticalBlockSpacing->setValue(blockSpacing.y());\n    auto edgeSpacing = Config()->getGraphEdgeSpacing();\n    ui->horizontalEdgeSpacing->setValue(edgeSpacing.x());\n    ui->verticalEdgeSpacing->setValue(edgeSpacing.y());\n}\n\nvoid GraphOptionsWidget::triggerOptionsChanged()\n{\n    disconnect(Core(), &CutterCore::graphOptionsChanged, this,\n               &GraphOptionsWidget::updateOptionsFromVars);\n    Core()->triggerGraphOptionsChanged();\n    connect(Core(), &CutterCore::graphOptionsChanged, this,\n            &GraphOptionsWidget::updateOptionsFromVars);\n}\n\nvoid GraphOptionsWidget::on_maxColsSpinBox_valueChanged(int value)\n{\n    Config()->setGraphBlockMaxChars(value);\n    triggerOptionsChanged();\n}\n\nvoid GraphOptionsWidget::on_minFontSizeSpinBox_valueChanged(int value)\n{\n    Config()->setGraphMinFontSize(value);\n    triggerOptionsChanged();\n}\n\nvoid GraphOptionsWidget::on_graphPreviewCheckBox_toggled(bool checked)\n{\n    Config()->setGraphPreview(checked);\n    triggerOptionsChanged();\n}\n\nvoid GraphOptionsWidget::checkTransparentStateChanged(int checked)\n{\n    Config()->setBitmapTransparentState(checked);\n}\n\nvoid GraphOptionsWidget::bitmapGraphScaleValueChanged(double value)\n{\n    double value_decimal = value / (double)100.0;\n    Config()->setBitmapExportScaleFactor(value_decimal);\n}\n\nvoid GraphOptionsWidget::layoutSpacingChanged()\n{\n    QPoint blockSpacing { ui->horizontalBlockSpacing->value(), ui->verticalBlockSpacing->value() };\n    QPoint edgeSpacing { ui->horizontalEdgeSpacing->value(), ui->verticalEdgeSpacing->value() };\n    Config()->setGraphSpacing(blockSpacing, edgeSpacing);\n    triggerOptionsChanged();\n}\n\nvoid GraphOptionsWidget::checkGraphBlockEntryOffsetChanged(bool checked)\n{\n    Config()->setGraphBlockEntryOffset(checked);\n    triggerOptionsChanged();\n}\n"
  },
  {
    "path": "src/dialogs/preferences/GraphOptionsWidget.h",
    "content": "\n#ifndef GRAPHOPTIONSWIDGET_H\n#define GRAPHOPTIONSWIDGET_H\n\n#include <QDialog>\n#include <QPushButton>\n#include <memory>\n\n#include \"core/Cutter.h\"\n\nclass PreferencesDialog;\n\nnamespace Ui {\nclass GraphOptionsWidget;\n}\n\nclass GraphOptionsWidget : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit GraphOptionsWidget(PreferencesDialog *dialog);\n    ~GraphOptionsWidget();\n\nprivate:\n    std::unique_ptr<Ui::GraphOptionsWidget> ui;\n\n    void triggerOptionsChanged();\n\nprivate slots:\n    void updateOptionsFromVars();\n\n    void on_maxColsSpinBox_valueChanged(int value);\n    void on_minFontSizeSpinBox_valueChanged(int value);\n    void on_graphPreviewCheckBox_toggled(bool checked);\n\n    void checkTransparentStateChanged(int checked);\n    void bitmapGraphScaleValueChanged(double value);\n    void checkGraphBlockEntryOffsetChanged(bool checked);\n    void layoutSpacingChanged();\n};\n\n#endif // GRAPHOPTIONSWIDGET_H\n"
  },
  {
    "path": "src/dialogs/preferences/GraphOptionsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GraphOptionsWidget</class>\n <widget class=\"QWidget\" name=\"GraphOptionsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>742</width>\n    <height>698</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Graph</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_8\">\n     <item>\n      <widget class=\"QGroupBox\" name=\"GraphBlockOptions\">\n       <property name=\"title\">\n        <string>Graph Block Options </string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_9\">\n        <property name=\"topMargin\">\n         <number>24</number>\n        </property>\n        <item>\n         <widget class=\"QCheckBox\" name=\"blockEntryCheckBox\">\n          <property name=\"toolTip\">\n           <string>The offset of the first instruction of a graph block is shown in the header of the respective graph block</string>\n          </property>\n          <property name=\"text\">\n           <string>Show offset of the first instruction in each graph block</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QCheckBox\" name=\"graphPreviewCheckBox\">\n          <property name=\"text\">\n           <string>Show preview when hovering (graph.preview)</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QFormLayout\" name=\"formLayout\">\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"maxColsLabel\">\n            <property name=\"text\">\n             <string>Maximum Line Length:</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"maxColsSpinBox\">\n            <property name=\"minimum\">\n             <number>25</number>\n            </property>\n            <property name=\"maximum\">\n             <number>256</number>\n            </property>\n            <property name=\"value\">\n             <number>100</number>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"minFontSizeLabel\">\n            <property name=\"toolTip\">\n             <string>Hide text when zooming out and it is smaller than the given value. Higher values can increase Performance.</string>\n            </property>\n            <property name=\"text\">\n             <string>Minimum Font Size</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"minFontSizeSpinBox\">\n            <property name=\"toolTip\">\n             <string>Hide text when zooming out and it is smaller than the given value. Higher values can increase Performance.</string>\n            </property>\n            <property name=\"maximum\">\n             <number>8</number>\n            </property>\n            <property name=\"value\">\n             <number>4</number>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"LayoutBlock\">\n       <property name=\"title\">\n        <string>Graph Layout Options</string>\n       </property>\n       <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n        <item row=\"1\" column=\"1\">\n         <widget class=\"QSpinBox\" name=\"horizontalBlockSpacing\">\n          <property name=\"maximum\">\n           <number>400</number>\n          </property>\n          <property name=\"singleStep\">\n           <number>10</number>\n          </property>\n          <property name=\"value\">\n           <number>10</number>\n          </property>\n         </widget>\n        </item>\n        <item row=\"0\" column=\"2\">\n         <widget class=\"QLabel\" name=\"label_4\">\n          <property name=\"text\">\n           <string>Vertical</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"2\" column=\"1\">\n         <widget class=\"QSpinBox\" name=\"horizontalEdgeSpacing\">\n          <property name=\"minimum\">\n           <number>1</number>\n          </property>\n          <property name=\"maximum\">\n           <number>400</number>\n          </property>\n          <property name=\"singleStep\">\n           <number>5</number>\n          </property>\n          <property name=\"value\">\n           <number>10</number>\n          </property>\n         </widget>\n        </item>\n        <item row=\"0\" column=\"1\">\n         <widget class=\"QLabel\" name=\"label_3\">\n          <property name=\"text\">\n           <string>Horizontal</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"2\" column=\"2\">\n         <widget class=\"QSpinBox\" name=\"verticalEdgeSpacing\">\n          <property name=\"minimum\">\n           <number>1</number>\n          </property>\n          <property name=\"maximum\">\n           <number>400</number>\n          </property>\n          <property name=\"singleStep\">\n           <number>5</number>\n          </property>\n          <property name=\"value\">\n           <number>10</number>\n          </property>\n         </widget>\n        </item>\n        <item row=\"1\" column=\"2\">\n         <widget class=\"QSpinBox\" name=\"verticalBlockSpacing\">\n          <property name=\"maximum\">\n           <number>400</number>\n          </property>\n          <property name=\"singleStep\">\n           <number>10</number>\n          </property>\n          <property name=\"value\">\n           <number>40</number>\n          </property>\n         </widget>\n        </item>\n        <item row=\"1\" column=\"0\">\n         <widget class=\"QLabel\" name=\"label\">\n          <property name=\"text\">\n           <string>Block spacing:</string>\n          </property>\n         </widget>\n        </item>\n        <item row=\"2\" column=\"0\">\n         <widget class=\"QLabel\" name=\"label_2\">\n          <property name=\"text\">\n           <string>Edge spacing</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QGroupBox\" name=\"BitmapExportBlock\">\n       <property name=\"title\">\n        <string>Bitmap Export Options</string>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_10\">\n        <property name=\"topMargin\">\n         <number>24</number>\n        </property>\n        <item>\n         <widget class=\"QCheckBox\" name=\"checkTransparent\">\n          <property name=\"text\">\n           <string>Export Transparent Bitmap Graphs</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n          <item>\n           <widget class=\"QLabel\" name=\"bitmapGraphScaleLabel\">\n            <property name=\"text\">\n             <string>Graph Bitmap Export Scale: </string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QDoubleSpinBox\" name=\"bitmapGraphScale\">\n            <property name=\"suffix\">\n             <string>%</string>\n            </property>\n            <property name=\"decimals\">\n             <number>0</number>\n            </property>\n            <property name=\"minimum\">\n             <double>100.000000000000000</double>\n            </property>\n            <property name=\"maximum\">\n             <double>30000.000000000000000</double>\n            </property>\n            <property name=\"singleStep\">\n             <double>50.000000000000000</double>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>blockEntryCheckBox</tabstop>\n  <tabstop>maxColsSpinBox</tabstop>\n  <tabstop>horizontalBlockSpacing</tabstop>\n  <tabstop>verticalBlockSpacing</tabstop>\n  <tabstop>horizontalEdgeSpacing</tabstop>\n  <tabstop>verticalEdgeSpacing</tabstop>\n  <tabstop>checkTransparent</tabstop>\n  <tabstop>bitmapGraphScale</tabstop>\n </tabstops>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/InitializationFileEditor.cpp",
    "content": "#include <QLabel>\n#include <QFontDialog>\n#include <QTextEdit>\n#include <QDir>\n#include <QFile>\n#include <QTextStream>\n#include <QDialogButtonBox>\n#include <QUrl>\n\n#include \"InitializationFileEditor.h\"\n#include \"ui_InitializationFileEditor.h\"\n\n#include \"PreferencesDialog.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n\nInitializationFileEditor::InitializationFileEditor(PreferencesDialog *dialog)\n    : QDialog(dialog), ui(new Ui::InitializationFileEditor)\n{\n    ui->setupUi(this);\n    connect(ui->saveRC, &QDialogButtonBox::accepted, this, &InitializationFileEditor::saveCutterRC);\n    connect(ui->executeNow, &QDialogButtonBox::accepted, this,\n            &InitializationFileEditor::executeCutterRC);\n    connect(ui->ConfigFileEdit, &QPlainTextEdit::modificationChanged, ui->saveRC,\n            &QWidget::setEnabled);\n\n    const QDir cutterRCDirectory = Core()->getCutterRCDefaultDirectory();\n    auto cutterRCFileInfo = QFileInfo(cutterRCDirectory, \"rc\");\n    QString cutterRCLocation = cutterRCFileInfo.absoluteFilePath();\n\n    ui->cutterRCLoaded->setTextInteractionFlags(Qt::TextBrowserInteraction);\n    ui->cutterRCLoaded->setOpenExternalLinks(true);\n    ui->cutterRCLoaded->setText(\n            tr(\"Script is loaded from <a href=\\\"%1\\\">%2</a>\")\n                    .arg(QUrl::fromLocalFile(cutterRCDirectory.absolutePath()).toString(),\n                         cutterRCLocation.toHtmlEscaped()));\n\n    ui->executeNow->button(QDialogButtonBox::Retry)->setText(tr(\"Execute\", \"script\"));\n    ui->ConfigFileEdit->clear();\n    if (cutterRCFileInfo.exists()) {\n        QFile cutterRC(cutterRCLocation);\n        if (cutterRC.open(QIODevice::ReadWrite | QIODevice::Text)) {\n            ui->ConfigFileEdit->setPlainText(cutterRC.readAll());\n        }\n        cutterRC.close();\n    }\n    ui->saveRC->setDisabled(true);\n}\n\nInitializationFileEditor::~InitializationFileEditor() {};\n\nvoid InitializationFileEditor::saveCutterRC()\n{\n    const QDir cutterRCDirectory = Core()->getCutterRCDefaultDirectory();\n    if (!cutterRCDirectory.exists()) {\n        cutterRCDirectory.mkpath(\".\");\n    }\n    auto cutterRCFileInfo = QFileInfo(cutterRCDirectory, \"rc\");\n    QString cutterRCLocation = cutterRCFileInfo.absoluteFilePath();\n\n    QFile cutterRC(cutterRCLocation);\n    if (cutterRC.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {\n        QTextStream out(&cutterRC);\n        QString text = ui->ConfigFileEdit->toPlainText();\n        out << text;\n        cutterRC.close();\n    }\n    ui->ConfigFileEdit->document()->setModified(false);\n}\n\nvoid InitializationFileEditor::executeCutterRC()\n{\n    saveCutterRC();\n    Core()->loadDefaultCutterRC();\n}\n"
  },
  {
    "path": "src/dialogs/preferences/InitializationFileEditor.h",
    "content": "#ifndef INITIALIZATIONFILEEDITOR_H\n#define INITIALIZATIONFILEEDITOR_H\n\n#include <QDialog>\n#include <QPushButton>\n#include <memory>\n#include \"core/Cutter.h\"\n\nclass PreferencesDialog;\n\nnamespace Ui {\nclass InitializationFileEditor;\n}\n\nclass InitializationFileEditor : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit InitializationFileEditor(PreferencesDialog *dialog);\n    ~InitializationFileEditor();\n    void saveCutterRC();\n    void executeCutterRC();\n\nprivate:\n    std::unique_ptr<Ui::InitializationFileEditor> ui;\n};\n\n#endif // INITIALIZATIONFILEEDITOR_H"
  },
  {
    "path": "src/dialogs/preferences/InitializationFileEditor.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>InitializationFileEditor</class>\n <widget class=\"QWidget\" name=\"InitializationFileEditor\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>667</width>\n    <height>486</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>CutterRC Editor</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QLabel\" name=\"cutterRCLoaded\">\n     <property name=\"text\">\n      <string>TextLabel</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"2\">\n    <widget class=\"QDialogButtonBox\" name=\"saveRC\">\n     <property name=\"maximumSize\">\n      <size>\n       <width>649</width>\n       <height>16777215</height>\n      </size>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Save</set>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"0\" colspan=\"3\">\n    <widget class=\"QPlainTextEdit\" name=\"ConfigFileEdit\"/>\n   </item>\n   <item row=\"1\" column=\"1\">\n    <widget class=\"QDialogButtonBox\" name=\"executeNow\">\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Retry</set>\n     </property>\n     <property name=\"centerButtons\">\n      <bool>false</bool>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/PluginsOptionsWidget.cpp",
    "content": "\n#include \"PluginsOptionsWidget.h\"\n\n#include \"PreferencesDialog.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n#include \"plugins/PluginManager.h\"\n#include \"dialogs/RizinPluginsDialog.h\"\n\n#include <QLabel>\n#include <QPushButton>\n#include <QTreeWidget>\n#include <QVBoxLayout>\n#include <QUrl>\n\nPluginsOptionsWidget::PluginsOptionsWidget(PreferencesDialog *dialog) : QDialog(dialog)\n{\n    auto layout = new QVBoxLayout(this);\n    setLayout(layout);\n\n    auto dirLabel = new QLabel(this);\n    dirLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);\n    dirLabel->setOpenExternalLinks(true);\n    layout->addWidget(dirLabel);\n    auto pluginPath = Plugins()->getUserPluginsDirectory();\n    dirLabel->setText(\n            tr(\"Plugins are loaded from <a href=\\\"%1\\\">%2</a>\")\n                    .arg(QUrl::fromLocalFile(pluginPath).toString(), pluginPath.toHtmlEscaped()));\n\n    auto treeWidget = new QTreeWidget(this);\n    layout->addWidget(treeWidget);\n    treeWidget->setRootIsDecorated(false);\n    treeWidget->setHeaderLabels({ tr(\"Name\"), tr(\"Description\"), tr(\"Version\"), tr(\"Author\") });\n\n    for (auto &plugin : Plugins()->getPlugins()) {\n        auto item = new QTreeWidgetItem();\n        item->setText(0, plugin->getName());\n        item->setText(1, plugin->getDescription());\n        item->setText(2, plugin->getVersion());\n        item->setText(3, plugin->getAuthor());\n        treeWidget->addTopLevelItem(item);\n    }\n    qhelpers::adjustColumns(treeWidget, 0);\n\n    auto rzPluginsButton = new QPushButton(this);\n    layout->addWidget(rzPluginsButton);\n    rzPluginsButton->setText(tr(\"Show Rizin plugin information\"));\n    connect(rzPluginsButton, &QPushButton::clicked, this, [this]() {\n        RizinPluginsDialog dialog(this);\n        dialog.exec();\n    });\n}\n\nPluginsOptionsWidget::~PluginsOptionsWidget() {}\n"
  },
  {
    "path": "src/dialogs/preferences/PluginsOptionsWidget.h",
    "content": "\n#ifndef PLUGINSOPTIONSWIDGET_H\n#define PLUGINSOPTIONSWIDGET_H\n\n#include <QDialog>\n\nclass PreferencesDialog;\n\nclass PluginsOptionsWidget : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit PluginsOptionsWidget(PreferencesDialog *dialog);\n    ~PluginsOptionsWidget();\n};\n\n#endif // CUTTER_PLUGINSOPTIONSWIDGET_H\n"
  },
  {
    "path": "src/dialogs/preferences/PreferenceCategory.cpp",
    "content": "#include \"PreferenceCategory.h\"\n\nPreferenceCategory::PreferenceCategory(const QString &name, const QIcon &icon)\n    : name(name), icon(icon), widget(nullptr), children {}\n{\n}\n\nPreferenceCategory::PreferenceCategory(const QString &name, QWidget *widget, const QIcon &icon)\n    : name(name), icon(icon), widget(widget), children {}\n{\n}\n\nPreferenceCategory::PreferenceCategory(const QString &name, QWidget *widget, const QIcon &icon,\n                                       const QList<PreferenceCategory> &children)\n    : name(name), icon(icon), widget(widget), children(children)\n{\n}\n\nPreferenceCategory::PreferenceCategory(const QString &name, const QIcon &icon,\n                                       const QList<PreferenceCategory> &children)\n    : name(name), icon(icon), widget(nullptr), children(children)\n{\n}\n\nvoid PreferenceCategory::addItem(QTreeWidget &tree, QStackedWidget &panel)\n{\n    QTreeWidgetItem *w = new QTreeWidgetItem({ name });\n\n    tree.addTopLevelItem(w);\n    for (auto &c : children)\n        c.addItem(*w, panel);\n\n    w->setExpanded(true);\n    w->setIcon(0, icon);\n\n    if (widget) {\n        panel.addWidget(widget);\n        w->setData(0, Qt::UserRole, panel.count());\n    }\n}\n\nvoid PreferenceCategory::addItem(QTreeWidgetItem &tree, QStackedWidget &panel)\n{\n    QTreeWidgetItem *w = new QTreeWidgetItem({ name });\n\n    tree.addChild(w);\n    for (auto &c : children)\n        c.addItem(*w, panel);\n\n    w->setExpanded(true);\n    w->setIcon(0, icon);\n\n    if (widget) {\n        panel.addWidget(widget);\n        w->setData(0, Qt::UserRole, panel.count());\n    }\n}\n"
  },
  {
    "path": "src/dialogs/preferences/PreferenceCategory.h",
    "content": "#ifndef PREFERENCECATEGORY_H\n#define PREFERENCECATEGORY_H\n\n#include <QString>\n#include <QTreeWidget>\n#include <QStackedWidget>\n\nclass PreferenceCategory\n{\npublic:\n    PreferenceCategory(const QString &name, const QIcon &icon);\n    PreferenceCategory(const QString &name, QWidget *widget, const QIcon &icon);\n    PreferenceCategory(const QString &name, QWidget *widget, const QIcon &icon,\n                       const QList<PreferenceCategory> &children);\n    PreferenceCategory(const QString &name, const QIcon &icon,\n                       const QList<PreferenceCategory> &children);\n\n    void addItem(QTreeWidget &tree, QStackedWidget &panel);\n    void addItem(QTreeWidgetItem &tree, QStackedWidget &panel);\n\nprivate:\n    QString name;\n    QIcon icon;\n    QWidget *widget;\n    QList<PreferenceCategory> children;\n};\n\n#endif // PREFERENCECATEGORY_H\n"
  },
  {
    "path": "src/dialogs/preferences/PreferencesDialog.cpp",
    "content": "#include \"PreferencesDialog.h\"\n#include \"ui_PreferencesDialog.h\"\n\n#include \"AppearanceOptionsWidget.h\"\n#include \"AsmOptionsWidget.h\"\n#include \"GraphOptionsWidget.h\"\n#include \"DebugOptionsWidget.h\"\n#include \"PluginsOptionsWidget.h\"\n#include \"InitializationFileEditor.h\"\n#include \"AnalysisOptionsWidget.h\"\n#include \"ShortcutOptionsWidget.h\"\n\n#include \"PreferenceCategory.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n\n#include <QDialogButtonBox>\n\nPreferencesDialog::PreferencesDialog(QWidget *parent)\n    : QDialog(parent), ui(new Ui::PreferencesDialog)\n{\n    setAttribute(Qt::WA_DeleteOnClose);\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    QList<PreferenceCategory> prefs {\n\n        { tr(\"Disassembly\"),\n          new AsmOptionsWidget(this),\n          QIcon(\":/img/icons/disas.svg\"),\n          {\n                  { tr(\"Graph\"), new GraphOptionsWidget(this), QIcon(\":/img/icons/graph.svg\") },\n          } },\n        { tr(\"Debug\"), new DebugOptionsWidget(this), QIcon(\":/img/icons/bug.svg\") },\n        { tr(\"Appearance\"), new AppearanceOptionsWidget(this), QIcon(\":/img/icons/polar.svg\") },\n        { tr(\"Plugins\"), new PluginsOptionsWidget(this), QIcon(\":/img/icons/plugins.svg\") },\n        { tr(\"Initialization Script\"), new InitializationFileEditor(this),\n          QIcon(\":/img/icons/initialization.svg\") },\n        { tr(\"Analysis\"), new AnalysisOptionsWidget(this), QIcon(\":/img/icons/cog_light.svg\") },\n        { tr(\"Shortcuts\"), new ShortcutOptionsWidget(this), QIcon(\":/img/icons/edit_light.svg\") }\n    };\n\n    for (auto &c : prefs) {\n        c.addItem(*ui->configCategories, *ui->configPanel);\n    }\n\n    connect(ui->configCategories, &QTreeWidget::currentItemChanged, this,\n            &PreferencesDialog::changePage);\n    connect(ui->saveButtons, &QDialogButtonBox::accepted, this, &QWidget::close);\n\n    QTreeWidgetItem *defitem = ui->configCategories->topLevelItem(0);\n    ui->configCategories->setCurrentItem(defitem, 0);\n\n    connect(Config(), &Configuration::interfaceThemeChanged, this,\n            &PreferencesDialog::chooseThemeIcons);\n    chooseThemeIcons();\n}\n\nPreferencesDialog::~PreferencesDialog() {}\n\nvoid PreferencesDialog::showSection(PreferencesDialog::Section section)\n{\n    QTreeWidgetItem *defitem;\n    switch (section) {\n    case Section::Appearance:\n        ui->configPanel->setCurrentIndex(0);\n        defitem = ui->configCategories->topLevelItem(0);\n        ui->configCategories->setCurrentItem(defitem, 0);\n        break;\n    case Section::Disassembly:\n        ui->configPanel->setCurrentIndex(1);\n        defitem = ui->configCategories->topLevelItem(1);\n        ui->configCategories->setCurrentItem(defitem, 1);\n        break;\n    }\n}\n\nvoid PreferencesDialog::changePage(QTreeWidgetItem *current, QTreeWidgetItem *previous)\n{\n    if (!current)\n        current = previous;\n\n    int index = current->data(0, Qt::UserRole).toInt();\n\n    if (index)\n        ui->configPanel->setCurrentIndex(index - 1);\n}\n\nvoid PreferencesDialog::chooseThemeIcons()\n{\n    // List of QActions which have alternative icons in different themes\n    const QList<QPair<QString, QString>> kCategoryIconsNames {\n        { QStringLiteral(\"Debug\"), QStringLiteral(\"bug.svg\") },\n        { QStringLiteral(\"Assembly\"), QStringLiteral(\"disas.svg\") },\n        { QStringLiteral(\"Graph\"), QStringLiteral(\"graph.svg\") },\n        { QStringLiteral(\"Appearance\"), QStringLiteral(\"polar.svg\") },\n        { QStringLiteral(\"Plugins\"), QStringLiteral(\"plugins.svg\") },\n        { QStringLiteral(\"Initialization Script\"), QStringLiteral(\"initialization.svg\") },\n        { QStringLiteral(\"Analysis\"), QStringLiteral(\"cog_light.svg\") },\n    };\n    QList<QPair<void *, QString>> supportedIconsNames;\n\n    foreach (const auto &p, kCategoryIconsNames) {\n        QList<QTreeWidgetItem *> items =\n                ui->configCategories->findItems(p.first, Qt::MatchContains | Qt::MatchRecursive, 0);\n        if (items.isEmpty()) {\n            continue;\n        }\n        supportedIconsNames.append({ items.first(), p.second });\n    }\n\n    // Set the correct icon for the QAction\n    qhelpers::setThemeIcons(supportedIconsNames, [](void *obj, const QIcon &icon) {\n        // here we have our setter functor, in this case it is almost \"unique\" because it specified\n        // the column in `setIcon` call\n        static_cast<QTreeWidgetItem *>(obj)->setIcon(0, icon);\n    });\n}\n"
  },
  {
    "path": "src/dialogs/preferences/PreferencesDialog.h",
    "content": "\n#ifndef PREFERENCESDIALOG_H\n#define PREFERENCESDIALOG_H\n\n#include \"core/Cutter.h\"\n\n#include <QDialog>\n\n#include <memory>\n\nclass QTreeWidgetItem;\n\nnamespace Ui {\nclass PreferencesDialog;\n}\n\nclass PreferencesDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    enum class Section { Appearance, Disassembly };\n\n    explicit PreferencesDialog(QWidget *parent = nullptr);\n    ~PreferencesDialog();\n\n    void showSection(Section section);\n\npublic slots:\n    void changePage(QTreeWidgetItem *current, QTreeWidgetItem *previous);\n\nprivate:\n    std::unique_ptr<Ui::PreferencesDialog> ui;\n    void chooseThemeIcons();\n};\n\n#endif // PREFERENCESDIALOG_H\n"
  },
  {
    "path": "src/dialogs/preferences/PreferencesDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>PreferencesDialog</class>\n <widget class=\"QDialog\" name=\"PreferencesDialog\">\n  <property name=\"windowTitle\">\n   <string>Preferences</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <item>\n    <widget class=\"QTreeWidget\" name=\"configCategories\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Expanding\"/>\n     </property>\n     <property name=\"maximumSize\">\n      <size>\n       <width>175</width>\n       <height>16777215</height>\n      </size>\n     </property>\n     <property name=\"rootIsDecorated\">\n      <bool>false</bool>\n     </property>\n     <attribute name=\"headerVisible\">\n      <bool>false</bool>\n     </attribute>\n     <column>\n     </column>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <item>\n      <widget class=\"QStackedWidget\" name=\"configPanel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\"/>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"saveButtons\">\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/dialogs/preferences/ShortcutOptionsWidget.cpp",
    "content": "#include \"ShortcutOptionsWidget.h\"\n#include \"ui_ShortcutOptionsWidget.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\nShortcutOptionsWidget::ShortcutOptionsWidget(QWidget *parent)\n    : QDialog(parent), ui(new Ui::ShortcutOptionsWidget)\n{\n    ui->setupUi(this);\n    populateShortcutTree();\n    setupUiElements();\n}\n\nvoid ShortcutOptionsWidget::setupUiElements()\n{\n    connect(ui->filterEdit, &QLineEdit::textEdited, this,\n            &ShortcutOptionsWidget::filterShortcutTree);\n\n    ui->shortcutTree->setHeaderLabels({ tr(\"Command\"), tr(\"Shortcut\"), tr(\"Description\") });\n    ui->shortcutTree->setColumnWidth(0, 400);\n    ui->shortcutTree->setSortingEnabled(true);\n    ui->shortcutTree->sortItems(0, Qt::AscendingOrder);\n    ui->shortcutTree->expandAll();\n}\n\nvoid ShortcutOptionsWidget::populateShortcutTree()\n{\n    const QHash<QString, QString> categories = {\n        { \"General\", tr(\"General\") },       { \"Disassembly\", tr(\"Disassembly\") },\n        { \"Decompiler\", tr(\"Decompiler\") }, { \"Strings\", tr(\"Strings\") },\n        { \"Graph\", tr(\"Graph\") },           { \"Breakpoint\", tr(\"Breakpoint\") },\n        { \"Console\", tr(\"Console\") },       { \"Hex\", tr(\"Hex\") },\n        { \"Debug\", tr(\"Debug\") },           { \"Functions\", tr(\"Functions\") },\n        { \"Omnibar\", tr(\"Omnibar\") },       { \"Exports\", tr(\"Exports\") },\n        { \"Imports\", tr(\"Imports\") },       { \"Overview\", tr(\"Graph Overview\") },\n    };\n\n    QHash<QString, QTreeWidgetItem *> prefixToItem;\n    for (auto it = categories.cbegin(); it != categories.cend(); ++it) {\n        prefixToItem[it.key()] = createCategoryItem(it.value());\n    }\n\n    const auto shortcuts = Shortcuts()->getAllShortcuts();\n    for (auto it = shortcuts.cbegin(); it != shortcuts.cend(); ++it) {\n        QString name = it.key();\n        Shortcut s = it.value();\n        QTreeWidgetItem *parent = prefixToItem[\"General\"];\n\n        for (auto it = prefixToItem.cbegin(); it != prefixToItem.cend(); ++it) {\n            const QString &prefix = it.key() + \".\";\n            if (name.startsWith(prefix)) {\n                name = name.mid(prefix.length());\n                parent = it.value();\n                break;\n            }\n        }\n\n        QTreeWidgetItem *item = new QTreeWidgetItem(parent);\n        item->setText(0, name);\n\n        QStringList shortcutTexts;\n        const QList<QKeySequence> &sequences = s.keySequences;\n        for (const QKeySequence &seq : sequences) {\n            shortcutTexts << seq.toString(QKeySequence::NativeText);\n        }\n        item->setText(1, shortcutTexts.join(\", \"));\n\n        item->setText(2, QCoreApplication::translate(s.context, s.text));\n    }\n}\n\nQTreeWidgetItem *ShortcutOptionsWidget::createCategoryItem(const QString &label)\n{\n    QTreeWidgetItem *item = new QTreeWidgetItem(ui->shortcutTree);\n    QFont bold;\n    bold.setBold(true);\n    item->setText(0, label);\n    item->setFont(0, bold);\n    item->setFirstColumnSpanned(true);\n    return item;\n}\n\nvoid ShortcutOptionsWidget::filterShortcutTree(const QString &input)\n{\n    const QString filter = input.trimmed().toLower();\n\n    for (int i = 0; i < ui->shortcutTree->topLevelItemCount(); ++i) {\n        QTreeWidgetItem *category = ui->shortcutTree->topLevelItem(i);\n        bool categoryMatches = category->text(0).toLower().contains(filter);\n        bool hasVisibleChildren = false;\n\n        for (int j = 0; j < category->childCount(); ++j) {\n            QTreeWidgetItem *child = category->child(j);\n            bool match = false;\n\n            if (categoryMatches) {\n                match = true;\n            } else {\n                for (int col = 0; col < child->columnCount(); ++col) {\n                    if (child->text(col).toLower().contains(filter)) {\n                        match = true;\n                        break;\n                    }\n                }\n            }\n            child->setHidden(!match);\n            if (match)\n                hasVisibleChildren = true;\n        }\n        category->setHidden(!(categoryMatches || hasVisibleChildren));\n    }\n}\n\nShortcutOptionsWidget::~ShortcutOptionsWidget() {}\n"
  },
  {
    "path": "src/dialogs/preferences/ShortcutOptionsWidget.h",
    "content": "#ifndef SHORTCUTOPTIONSWIDGET_H\n#define SHORTCUTOPTIONSWIDGET_H\n\n#include <QDialog>\n#include <memory>\n\nclass QTreeWidgetItem;\n\nnamespace Ui {\nclass ShortcutOptionsWidget;\n}\n\nclass ShortcutOptionsWidget : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit ShortcutOptionsWidget(QWidget *parent = nullptr);\n    ~ShortcutOptionsWidget();\n\nprivate:\n    std::unique_ptr<Ui::ShortcutOptionsWidget> ui;\n\n    void setupUiElements();\n    void populateShortcutTree();\n    QTreeWidgetItem *createCategoryItem(const QString &label);\n\nprivate slots:\n    void filterShortcutTree(const QString &input);\n};\n\n#endif // SHORTCUTOPTIONSWIDGET_H\n"
  },
  {
    "path": "src/dialogs/preferences/ShortcutOptionsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ShortcutOptionsWidget</class>\n <widget class=\"QWidget\" name=\"ShortcutOptionsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>671</width>\n    <height>371</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Appearance</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTreeWidget\" name=\"shortcutTree\">\n     <column>\n      <property name=\"text\">\n       <string notr=\"true\">Command</string>\n      </property>\n     </column>\n     <column>\n      <property name=\"text\">\n       <string>Shortcut</string>\n      </property>\n     </column>\n     <column>\n      <property name=\"text\">\n       <string>Description</string>\n      </property>\n     </column>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLineEdit\" name=\"filterEdit\">\n     <property name=\"placeholderText\">\n      <string>Filter</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n  <action name=\"actionSaveAsDefault\">\n   <property name=\"text\">\n    <string>Save as Default</string>\n   </property>\n  </action>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/img/cutter.rc",
    "content": "IDR_MAINFRAME ICON \"cutter.ico\""
  },
  {
    "path": "src/img/icons/Iconic-LICENSE",
    "content": "-----------------------------------------------------------------------\nLicense for icons from Iconic: https://github.com/somerandomdude/Iconic\n-----------------------------------------------------------------------\n\nThis work is licensed under Creative Commons' Attribution-ShareAlike 3.0 United States (CC BY-SA 3.0) - http://creativecommons.org/licenses/by-sa/3.0/us/\n\nIf you use these icons, please add a link to Iconic (http://somerandomdude.com/work/iconic/) somewhere on your site or in your app.\n\n\n\nLEGAL MUMBO-JUMBO\n\nYou are free:\n\nto Share — to copy, distribute and transmit the work\nto Remix — to adapt the work\nto make commercial use of the work\n\nUnder the following conditions:\n\nAttribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).\nShare Alike — If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one.\nWith the understanding that:\n\nWaiver — Any of the above conditions can be waived if you get permission from the copyright holder.\nPublic Domain — Where the work or any of its elements is in the public domain under applicable law, that status is in no way affected by the license.\nOther Rights — In no way are any of the following rights affected by the license:\nYour fair dealing or fair use rights, or other applicable copyright exceptions and limitations;\nApart from the remix rights granted under this license, the author's moral rights;\nRights other persons may have either in the work itself or in how the work is used, such as publicity or privacy rights.\nNotice — For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page.\n\n\nFull License \n\nTHE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE (\"CCPL\" OR \"LICENSE\"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.\n\nBY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.\n\n1. Definitions\n\n\"Collective Work\" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with one or more other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.\n\"Creative Commons Compatible License\" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of derivatives of works made available under that license under this License or either a Creative Commons unported license or a Creative Commons jurisdiction license with the same License Elements as this License.\n\"Derivative Work\" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image (\"synching\") will be considered a Derivative Work for the purpose of this License.\n\"License Elements\" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.\n\"Licensor\" means the individual, individuals, entity or entities that offers the Work under the terms of this License.\n\"Original Author\" means the individual, individuals, entity or entities who created the Work.\n\"Work\" means the copyrightable work of authorship offered under the terms of this License.\n\"You\" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.\n2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.\n\n3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:\n\nto reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;\nto create and reproduce Derivative Works provided that any such Derivative Work, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked \"The original work was translated from English to Spanish,\" or a modification could indicate \"The original work has been modified.\";\nto distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;\nto distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.\nFor the avoidance of doubt, where the Work is a musical composition:\n\nPerformance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or, in the event that Licensor is a member of a performance rights society (e.g. ASCAP, BMI, SESAC), via that society, royalties for the public performance or public digital performance (e.g. webcast) of the Work.\nMechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work (\"cover version\") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).\nWebcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).\nThe above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.\n\n4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:\n\nYou may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of a recipient of the Work to exercise of the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. When You distribute, publicly display, publicly perform, or publicly digitally perform the Work, You may not impose any technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise of the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by Section 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by Section 4(c), as requested.\nYou may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under: (i) the terms of this License; (ii) a later version of this License with the same License Elements as this License; (iii) either the Creative Commons (Unported) license or a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g. Attribution-ShareAlike 3.0 (Unported)); (iv) a Creative Commons Compatible License. If you license the Derivative Work under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Derivative Work under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the \"Applicable License\"), you must comply with the terms of the Applicable License generally and with the following provisions: (I) You must include a copy of, or the Uniform Resource Identifier for, the Applicable License with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform; (II) You may not offer or impose any terms on the Derivative Works that restrict the terms of the Applicable License or the ability of a recipient of the Work to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties; and, (IV) when You distribute, publicly display, publicly perform, or publicly digitally perform the Work, You may not impose any technological measures on the Derivative Work that restrict the ability of a recipient of the Derivative Work from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of the Applicable License.\nIf You distribute, publicly display, publicly perform, or publicly digitally perform the Work (as defined in Section 1 above) or any Derivative Works (as defined in Section 1 above) or Collective Works (as defined in Section 1 above), You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution (\"Attribution Parties\") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, consistent with Section 3(b) in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., \"French translation of the Work by Original Author,\" or \"Screenplay based on original Work by Original Author\"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear, if a credit for all contributing authors of the Derivative Work or Collective Work appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.\n5. Representations, Warranties and Disclaimer\n\nUNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE EXTENT OF ANY RIGHTS HELD IN THE LICENSED WORK BY THE LICENSOR. THE LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MARKETABILITY, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.\n\n6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n7. Termination\n\nThis License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.\nSubject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.\n8. Miscellaneous\n\nEach time You distribute or publicly digitally perform the Work (as defined in Section 1 above) or a Collective Work (as defined in Section 1 above), the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.\nEach time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.\nIf any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.\nNo term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.\nThis License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.\n"
  },
  {
    "path": "src/menus/AddressableItemContextMenu.cpp",
    "content": "#include \"AddressableItemContextMenu.h\"\n#include \"dialogs/XrefsDialog.h\"\n#include \"MainWindow.h\"\n#include \"dialogs/CommentsDialog.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QtCore>\n#include <QShortcut>\n#include <QJsonArray>\n#include <QClipboard>\n#include <QApplication>\n#include <QPushButton>\n\nAddressableItemContextMenu::AddressableItemContextMenu(QWidget *parent, MainWindow *mainWindow)\n    : QMenu(parent), mainWindow(mainWindow)\n{\n    actionShowInMenu = new QAction(tr(\"Show in\"), this);\n    actionCopyAddress = Shortcuts()->makeAction(\"General.copyAddress\", this);\n    actionShowXrefs = Shortcuts()->makeAction(\"General.showXRefs\", this);\n    actionAddComment = Shortcuts()->makeAction(\"General.addComment\", this);\n    actionToggleBreakpoint = Shortcuts()->makeAction(\"Debug.toggleBreakpoint\", this);\n\n    connect(actionCopyAddress, &QAction::triggered, this,\n            &AddressableItemContextMenu::onActionCopyAddress);\n    actionCopyAddress->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    connect(actionShowXrefs, &QAction::triggered, this,\n            &AddressableItemContextMenu::onActionShowXrefs);\n    actionShowXrefs->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    connect(actionAddComment, &QAction::triggered, this,\n            &AddressableItemContextMenu::onActionAddComment);\n    actionAddComment->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    connect(actionToggleBreakpoint, &QAction::triggered, this,\n            &AddressableItemContextMenu::onActionToggleBreakpoint);\n    actionToggleBreakpoint->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n\n    addAction(actionShowInMenu);\n    addAction(actionCopyAddress);\n    addAction(actionShowXrefs);\n    addSeparator();\n    addAction(actionAddComment);\n    addAction(actionToggleBreakpoint);\n\n    addSeparator();\n    pluginMenu = mainWindow->getContextMenuExtensions(MainWindow::ContextMenuType::Addressable);\n    pluginMenuAction = addMenu(pluginMenu);\n    addSeparator();\n\n    setHasTarget(hasTarget);\n\n    connect(this, &QMenu::aboutToShow, this, &AddressableItemContextMenu::aboutToShowSlot);\n}\n\nAddressableItemContextMenu::~AddressableItemContextMenu() {}\n\nvoid AddressableItemContextMenu::setWholeFunction(bool wholeFunciton)\n{\n    this->wholeFunction = wholeFunciton;\n}\n\nvoid AddressableItemContextMenu::setOffset(RVA offset)\n{\n    setTarget(offset);\n}\n\nvoid AddressableItemContextMenu::setTarget(RVA offset, QString name)\n{\n    this->offset = offset;\n    this->name = name;\n    setHasTarget(true);\n}\n\nvoid AddressableItemContextMenu::clearTarget()\n{\n    setHasTarget(false);\n}\n\nvoid AddressableItemContextMenu::toggleBreakpointAction(bool enabled)\n{\n    breakpointActionEnabled = enabled;\n    // Update actionToggleBreakpoint visibility\n    setHasTarget(hasTarget);\n}\n\nvoid AddressableItemContextMenu::onActionCopyAddress()\n{\n    auto clipboard = QApplication::clipboard();\n    clipboard->setText(RzAddressString(offset));\n}\n\nvoid AddressableItemContextMenu::onActionShowXrefs()\n{\n    emit xrefsTriggered();\n    XrefsDialog dialog(mainWindow, true);\n    QString tmpName = name;\n    if (name.isEmpty()) {\n        name = RzAddressString(offset);\n    }\n    dialog.fillRefsForAddress(offset, name, wholeFunction);\n    dialog.exec();\n}\n\nvoid AddressableItemContextMenu::onActionAddComment()\n{\n    CommentsDialog::addOrEditComment(offset, this);\n}\n\nvoid AddressableItemContextMenu::onActionToggleBreakpoint()\n{\n    Core()->toggleBreakpoint(offset);\n}\n\nvoid AddressableItemContextMenu::aboutToShowSlot()\n{\n    if (Core()->getCommentAt(offset).isEmpty()) {\n        actionAddComment->setText(tr(\"Add Comment\"));\n    } else {\n        actionAddComment->setText(tr(\"Edit Comment\"));\n    }\n\n    if (Core()->breakpointIndexAt(offset) < 0) {\n        actionToggleBreakpoint->setText(tr(\"Add Breakpoint\"));\n    } else {\n        actionToggleBreakpoint->setText(tr(\"Remove Breakpoint\"));\n    }\n\n    if (actionShowInMenu->menu()) {\n        actionShowInMenu->menu()->deleteLater();\n    }\n    actionShowInMenu->setMenu(mainWindow->createShowInMenu(this, offset));\n\n    pluginMenuAction->setVisible(!pluginMenu->isEmpty());\n    for (QAction *pluginAction : pluginMenu->actions()) {\n        pluginAction->setData(QVariant::fromValue(offset));\n    }\n}\n\nvoid AddressableItemContextMenu::setHasTarget(bool hasTarget)\n{\n    this->hasTarget = hasTarget;\n    actionShowInMenu->setEnabled(hasTarget);\n    actionCopyAddress->setEnabled(hasTarget);\n    actionShowXrefs->setEnabled(hasTarget);\n    actionAddComment->setEnabled(hasTarget);\n    actionToggleBreakpoint->setEnabled(hasTarget && breakpointActionEnabled);\n    actionToggleBreakpoint->setVisible(hasTarget && breakpointActionEnabled);\n}\n"
  },
  {
    "path": "src/menus/AddressableItemContextMenu.h",
    "content": "#ifndef ADDRESSABLEITEMCONTEXTMENU_H\n#define ADDRESSABLEITEMCONTEXTMENU_H\n\n#include \"core/Cutter.h\"\n#include <QMenu>\n#include <QKeySequence>\n\nclass MainWindow;\n\nclass CUTTER_EXPORT AddressableItemContextMenu : public QMenu\n{\n    Q_OBJECT\n\npublic:\n    AddressableItemContextMenu(QWidget *parent, MainWindow *mainWindow);\n    ~AddressableItemContextMenu();\n\n    /**\n     * @brief Configure if addressable item refers to whole function or specific address\n     * @param wholeFunciton\n     */\n    void setWholeFunction(bool wholeFunciton);\npublic slots:\n    void setOffset(RVA offset);\n    void setTarget(RVA offset, QString name = QString());\n    void clearTarget();\n    void toggleBreakpointAction(bool enabled);\nsignals:\n    void xrefsTriggered();\n\nprivate:\n    void onActionCopyAddress();\n    void onActionShowXrefs();\n    void onActionAddComment();\n    void onActionToggleBreakpoint();\n\n    virtual void aboutToShowSlot();\n\n    QMenu *pluginMenu;\n    QAction *pluginMenuAction;\n    MainWindow *mainWindow;\n\n    RVA offset;\n    bool hasTarget = false;\n\nprotected:\n    void setHasTarget(bool hasTarget);\n    QAction *actionShowInMenu;\n    QAction *actionCopyAddress;\n    QAction *actionShowXrefs;\n    QAction *actionAddComment;\n    QAction *actionToggleBreakpoint;\n\n    QString name;\n    bool wholeFunction = false;\n    bool breakpointActionEnabled = false;\n};\n#endif // ADDRESSABLEITEMCONTEXTMENU_H\n"
  },
  {
    "path": "src/menus/DecompilerContextMenu.cpp",
    "content": "#include \"DecompilerContextMenu.h\"\n#include \"dialogs/preferences/PreferencesDialog.h\"\n#include \"MainWindow.h\"\n#include \"dialogs/BreakpointsDialog.h\"\n#include \"dialogs/CommentsDialog.h\"\n#include \"dialogs/EditVariablesDialog.h\"\n#include \"dialogs/XrefsDialog.h\"\n#include \"common/Configuration.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QtCore>\n#include <QShortcut>\n#include <QJsonArray>\n#include <QClipboard>\n#include <QApplication>\n#include <QPushButton>\n#include <QInputDialog>\n\nDecompilerContextMenu::DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow)\n    : QMenu(parent),\n      mainWindow(mainWindow),\n      curHighlightedWord(QString()),\n      offset(0),\n      decompiledFunctionAddress(RVA_INVALID),\n      isTogglingBreakpoints(false),\n      annotationHere(nullptr),\n      actionCopy(this),\n      actionCopyInstructionAddress(tr(\"Copy instruction address (<address>)\"), this),\n      actionCopyReferenceAddress(this),\n      actionShowInSubmenu(tr(\"Show in\"), this),\n      actionAddComment(this),\n      actionDeleteComment(tr(\"Delete comment\"), this),\n      actionRenameThingHere(this),\n      actionDeleteName(tr(\"Delete <name>\"), this),\n      actionEditFunctionVariables(this),\n      actionXRefs(this),\n      actionToggleBreakpoint(this),\n      actionAdvancedBreakpoint(this),\n      breakpointsInLineMenu(new QMenu(this)),\n      actionContinueUntil(tr(\"Continue until line\"), this),\n      actionSetPC(tr(\"Set PC\"), this)\n{\n    setActionCopy(); // Sets all three copy actions\n    addSeparator();\n\n    setActionShowInSubmenu();\n    copySeparator = addSeparator();\n\n    setActionAddComment();\n    setActionDeleteComment();\n\n    setActionRenameThingHere();\n    setActionDeleteName();\n\n    setActionXRefs();\n\n    setActionEditFunctionVariables();\n\n    addSeparator();\n    addBreakpointMenu();\n    addDebugMenu();\n\n    setShortcutContextInActions(this);\n\n    connect(this, &DecompilerContextMenu::aboutToShow, this,\n            &DecompilerContextMenu::aboutToShowSlot);\n    connect(this, &DecompilerContextMenu::aboutToHide, this,\n            &DecompilerContextMenu::aboutToHideSlot);\n}\n\nDecompilerContextMenu::~DecompilerContextMenu() {}\n\nQWidget *DecompilerContextMenu::parentForDialog()\n{\n    return parentWidget();\n}\n\nvoid DecompilerContextMenu::setAnnotationHere(RzCodeAnnotation *annotation)\n{\n    annotationHere = annotation;\n}\n\nvoid DecompilerContextMenu::setCurHighlightedWord(QString word)\n{\n    curHighlightedWord = word;\n}\n\nvoid DecompilerContextMenu::setOffset(RVA newOffset)\n{\n    offset = newOffset;\n}\n\nvoid DecompilerContextMenu::setDecompiledFunctionAddress(RVA functionAddr)\n{\n    decompiledFunctionAddress = functionAddr;\n}\n\nvoid DecompilerContextMenu::setFirstOffsetInLine(RVA firstOffset)\n{\n    firstOffsetInLine = firstOffset;\n}\n\nRVA DecompilerContextMenu::getFirstOffsetInLine()\n{\n    return firstOffsetInLine;\n}\n\nvoid DecompilerContextMenu::setAvailableBreakpoints(QVector<RVA> offsetList)\n{\n    availableBreakpoints = offsetList;\n}\n\nvoid DecompilerContextMenu::setupBreakpointsInLineMenu()\n{\n    breakpointsInLineMenu->clear();\n    for (auto curOffset : this->availableBreakpoints) {\n        QAction *action = breakpointsInLineMenu->addAction(RzAddressString(curOffset));\n        connect(action, &QAction::triggered, this, [this, curOffset] {\n            BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(curOffset), this);\n        });\n    }\n}\n\nvoid DecompilerContextMenu::setShortcutContextInActions(QMenu *menu)\n{\n    for (QAction *action : menu->actions()) {\n        if (action->isSeparator()) {\n            // Do nothing\n        } else if (action->menu()) {\n            setShortcutContextInActions(action->menu());\n        } else {\n            action->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n        }\n    }\n}\n\nvoid DecompilerContextMenu::setIsTogglingBreakpoints(bool isToggling)\n{\n    isTogglingBreakpoints = isToggling;\n}\n\nbool DecompilerContextMenu::getIsTogglingBreakpoints()\n{\n    return isTogglingBreakpoints;\n}\n\nvoid DecompilerContextMenu::aboutToHideSlot()\n{\n    actionAddComment.setVisible(true);\n    actionRenameThingHere.setVisible(true);\n    actionRenameThingHere.setEnabled(true);\n    actionDeleteName.setVisible(false);\n    actionEditFunctionVariables.setVisible(true);\n    actionEditFunctionVariables.setEnabled(true);\n    actionXRefs.setVisible(true);\n    setToolTipsVisible(false);\n}\n\nvoid DecompilerContextMenu::aboutToShowSlot()\n{\n    if (this->firstOffsetInLine != RVA_MAX) {\n        actionShowInSubmenu.setVisible(true);\n        QString comment = Core()->getCommentAt(firstOffsetInLine);\n        actionAddComment.setVisible(true);\n        if (comment.isEmpty()) {\n            actionDeleteComment.setVisible(false);\n            actionAddComment.setText(tr(\"Add Comment\"));\n        } else {\n            actionDeleteComment.setVisible(true);\n            actionAddComment.setText(tr(\"Edit Comment\"));\n        }\n    } else {\n        actionShowInSubmenu.setVisible(false);\n        actionAddComment.setVisible(false);\n        actionDeleteComment.setVisible(false);\n    }\n\n    setupBreakpointsInLineMenu();\n\n    // Only show debug options if we are currently debugging\n    debugMenu->menuAction()->setVisible(Core()->currentlyDebugging);\n\n    bool hasBreakpoint = !this->availableBreakpoints.isEmpty();\n    int numberOfBreakpoints = this->availableBreakpoints.size();\n    if (numberOfBreakpoints == 0) {\n        actionToggleBreakpoint.setText(tr(\"Add breakpoint\"));\n    } else if (numberOfBreakpoints == 1) {\n        actionToggleBreakpoint.setText(tr(\"Remove breakpoint\"));\n    } else {\n        actionToggleBreakpoint.setText(tr(\"Remove all breakpoints in line\"));\n    }\n    if (numberOfBreakpoints > 1) {\n        actionAdvancedBreakpoint.setMenu(breakpointsInLineMenu);\n    } else {\n        actionAdvancedBreakpoint.setMenu(nullptr);\n    }\n    actionAdvancedBreakpoint.setText(hasBreakpoint ? tr(\"Edit breakpoint\")\n                                                   : tr(\"Advanced breakpoint\"));\n\n    QString progCounterName = Core()->getRegisterName(\"PC\").toUpper();\n    actionSetPC.setText(tr(\"Set %1 here\").arg(progCounterName));\n\n    if (!annotationHere\n        || annotationHere->type\n                == RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) { // If constant, don't show rename\n                                                                // and targeted show-in\n        actionRenameThingHere.setVisible(false);\n        copySeparator->setVisible(false);\n    } else {\n        copySeparator->setVisible(true);\n        if (annotationHere->type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {\n            actionRenameThingHere.setText(\n                    tr(\"Rename function %1\").arg(QString(annotationHere->reference.name)));\n        } else if (annotationHere->type == RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {\n            RzCoreLocked core = Core()->lock();\n            RzFlagItem *flagDetails = rz_flag_get_i(core->flags, annotationHere->reference.offset);\n            if (flagDetails) {\n                actionRenameThingHere.setText(tr(\"Rename %1\").arg(QString(flagDetails->name)));\n                actionDeleteName.setText(tr(\"Remove %1\").arg(QString(flagDetails->name)));\n                actionDeleteName.setVisible(true);\n            } else {\n                actionRenameThingHere.setText(tr(\"Add name to %1\").arg(curHighlightedWord));\n            }\n        }\n    }\n    actionCopyInstructionAddress.setText(\n            tr(\"Copy instruction address (%1)\").arg(RzAddressString(offset)));\n    if (isReference()) {\n        actionCopyReferenceAddress.setVisible(true);\n        RVA referenceAddr = annotationHere->reference.offset;\n        RzCoreLocked core = Core()->lock();\n        RzFlagItem *flagDetails = rz_flag_get_i(core->flags, referenceAddr);\n        if (annotationHere->type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {\n            actionCopyReferenceAddress.setText(tr(\"Copy address of %1 (%2)\")\n                                                       .arg(QString(annotationHere->reference.name),\n                                                            RzAddressString(referenceAddr)));\n        } else if (flagDetails) {\n            actionCopyReferenceAddress.setText(\n                    tr(\"Copy address of %1 (%2)\")\n                            .arg(flagDetails->name, RzAddressString(referenceAddr)));\n        } else {\n            actionCopyReferenceAddress.setText(\n                    tr(\"Copy address (%1)\").arg(RzAddressString(referenceAddr)));\n        }\n    } else {\n        actionXRefs.setVisible(false);\n        actionCopyReferenceAddress.setVisible(false);\n    }\n    if (actionShowInSubmenu.menu() != nullptr) {\n        actionShowInSubmenu.menu()->deleteLater();\n    }\n    actionShowInSubmenu.setMenu(mainWindow->createShowInMenu(this, offset));\n    updateTargetMenuActions();\n\n    if (!isFunctionVariable()) {\n        actionEditFunctionVariables.setVisible(false);\n    } else {\n        actionEditFunctionVariables.setText(\n                tr(\"Edit variable %1\").arg(QString(annotationHere->variable.name)));\n        actionRenameThingHere.setText(\n                tr(\"Rename variable %1\").arg(QString(annotationHere->variable.name)));\n        if (!variablePresentInRizin()) {\n            actionEditFunctionVariables.setDisabled(true);\n            actionRenameThingHere.setDisabled(true);\n            setToolTipsVisible(true);\n        }\n    }\n}\n\n// Set up actions\n\nvoid DecompilerContextMenu::setActionCopy() // Set all three copy actions\n{\n    Shortcuts()->setupAction(actionCopy, \"Decompiler.copy\");\n    connect(&actionCopy, &QAction::triggered, this, &DecompilerContextMenu::actionCopyTriggered);\n    addAction(&actionCopy);\n    connect(&actionCopyInstructionAddress, &QAction::triggered, this,\n            &DecompilerContextMenu::actionCopyInstructionAddressTriggered);\n    addAction(&actionCopyInstructionAddress);\n\n    Shortcuts()->setupAction(actionCopyReferenceAddress, \"Decompiler.copyReferenceAddress\");\n    connect(&actionCopyReferenceAddress, &QAction::triggered, this,\n            &DecompilerContextMenu::actionCopyReferenceAddressTriggered);\n    addAction(&actionCopyReferenceAddress);\n}\n\nvoid DecompilerContextMenu::setActionShowInSubmenu()\n{\n    addAction(&actionShowInSubmenu);\n}\n\nvoid DecompilerContextMenu::setActionAddComment()\n{\n    Shortcuts()->setupAction(actionAddComment, \"General.addComment\");\n    connect(&actionAddComment, &QAction::triggered, this,\n            &DecompilerContextMenu::actionAddCommentTriggered);\n    addAction(&actionAddComment);\n}\n\nvoid DecompilerContextMenu::setActionDeleteComment()\n{\n    connect(&actionDeleteComment, &QAction::triggered, this,\n            &DecompilerContextMenu::actionDeleteCommentTriggered);\n    addAction(&actionDeleteComment);\n}\n\nvoid DecompilerContextMenu::setActionXRefs()\n{\n    Shortcuts()->setupAction(actionXRefs, \"General.showXRefs\");\n    connect(&actionXRefs, &QAction::triggered, this, &DecompilerContextMenu::actionXRefsTriggered);\n    addAction(&actionXRefs);\n}\n\nvoid DecompilerContextMenu::setActionRenameThingHere()\n{\n    Shortcuts()->setupAction(actionRenameThingHere, \"Decompiler.renameThingHere\");\n    connect(&actionRenameThingHere, &QAction::triggered, this,\n            &DecompilerContextMenu::actionRenameThingHereTriggered);\n    addAction(&actionRenameThingHere);\n    actionRenameThingHere.setToolTip(\n            tr(\"Can't rename this variable.<br>\"\n               \"Only local variables defined in disassembly can be renamed.\"));\n}\n\nvoid DecompilerContextMenu::setActionDeleteName()\n{\n    connect(&actionDeleteName, &QAction::triggered, this,\n            &DecompilerContextMenu::actionDeleteNameTriggered);\n    addAction(&actionDeleteName);\n    actionDeleteName.setVisible(false);\n}\n\nvoid DecompilerContextMenu::setActionEditFunctionVariables()\n{\n    Shortcuts()->setupAction(actionEditFunctionVariables, \"Decompiler.editFunctionVariables\");\n    connect(&actionEditFunctionVariables, &QAction::triggered, this,\n            &DecompilerContextMenu::actionEditFunctionVariablesTriggered);\n    addAction(&actionEditFunctionVariables);\n    actionEditFunctionVariables.setToolTip(\n            tr(\"Can't edit this variable.<br>\"\n               \"Only local variables defined in disassembly can be edited.\"));\n}\n\nvoid DecompilerContextMenu::setActionToggleBreakpoint()\n{\n    Shortcuts()->setupAction(actionToggleBreakpoint, \"Debug.toggleBreakpoint\");\n    connect(&actionToggleBreakpoint, &QAction::triggered, this,\n            &DecompilerContextMenu::actionToggleBreakpointTriggered);\n}\n\nvoid DecompilerContextMenu::setActionAdvancedBreakpoint()\n{\n    Shortcuts()->setupAction(actionAdvancedBreakpoint, \"Debug.advancedBreakpoint\");\n    connect(&actionAdvancedBreakpoint, &QAction::triggered, this,\n            &DecompilerContextMenu::actionAdvancedBreakpointTriggered);\n}\n\nvoid DecompilerContextMenu::setActionContinueUntil()\n{\n    connect(&actionContinueUntil, &QAction::triggered, this,\n            &DecompilerContextMenu::actionContinueUntilTriggered);\n}\n\nvoid DecompilerContextMenu::setActionSetPC()\n{\n    connect(&actionSetPC, &QAction::triggered, this, &DecompilerContextMenu::actionSetPCTriggered);\n}\n\n// Set up action responses\n\nvoid DecompilerContextMenu::actionCopyTriggered()\n{\n    emit copy();\n}\n\nvoid DecompilerContextMenu::actionCopyInstructionAddressTriggered()\n{\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(RzAddressString(offset));\n}\n\nvoid DecompilerContextMenu::actionCopyReferenceAddressTriggered()\n{\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(RzAddressString(annotationHere->reference.offset));\n}\n\nvoid DecompilerContextMenu::actionAddCommentTriggered()\n{\n    CommentsDialog::addOrEditComment(this->firstOffsetInLine, parentForDialog());\n}\n\nvoid DecompilerContextMenu::actionDeleteCommentTriggered()\n{\n    Core()->delComment(this->firstOffsetInLine);\n}\n\nvoid DecompilerContextMenu::actionRenameThingHereTriggered()\n{\n    if (!annotationHere || annotationHere->type == RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) {\n        return;\n    }\n    RzCoreLocked core = Core()->lock();\n    bool ok;\n    auto type = annotationHere->type;\n    if (type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {\n        QString currentName(annotationHere->reference.name);\n        RVA func_addr = annotationHere->reference.offset;\n        RzAnalysisFunction *func = Core()->functionAt(func_addr);\n        if (func == NULL) {\n            QString function_name = QInputDialog::getText(\n                    parentForDialog(),\n                    tr(\"Define this function at %2\").arg(RzAddressString(func_addr)),\n                    tr(\"Function name:\"), QLineEdit::Normal, currentName, &ok);\n            if (ok && !function_name.isEmpty()) {\n                Core()->createFunctionAt(func_addr, function_name);\n            }\n        } else {\n            QString newName = QInputDialog::getText(\n                    parentForDialog(), tr(\"Rename function %2\").arg(currentName),\n                    tr(\"Function name:\"), QLineEdit::Normal, currentName, &ok);\n            if (ok && !newName.isEmpty()) {\n                Core()->renameFunction(func_addr, newName);\n            }\n        }\n    } else if (type == RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE) {\n        RVA var_addr = annotationHere->reference.offset;\n        RzFlagItem *flagDetails = rz_flag_get_i(core->flags, var_addr);\n        if (flagDetails) {\n            QString newName = QInputDialog::getText(\n                    parentForDialog(), tr(\"Rename %2\").arg(flagDetails->name), tr(\"Enter name\"),\n                    QLineEdit::Normal, flagDetails->name, &ok);\n            if (ok && !newName.isEmpty()) {\n                Core()->renameFlag(flagDetails->name, newName);\n            }\n        } else {\n            QString newName = QInputDialog::getText(\n                    parentForDialog(), tr(\"Add name to %2\").arg(curHighlightedWord),\n                    tr(\"Enter name\"), QLineEdit::Normal, curHighlightedWord, &ok);\n            if (ok && !newName.isEmpty()) {\n                Core()->addFlag(var_addr, newName, 1);\n            }\n        }\n    } else if (isFunctionVariable()) {\n        if (!variablePresentInRizin()) {\n            // Show can't rename this variable dialog\n            QMessageBox::critical(\n                    parentForDialog(),\n                    tr(\"Rename local variable %1\").arg(QString(annotationHere->variable.name)),\n                    tr(\"Can't rename this variable. \"\n                       \"Only local variables defined in disassembly can be renamed.\"));\n            return;\n        }\n        QString oldName(annotationHere->variable.name);\n        QString newName = QInputDialog::getText(parentForDialog(), tr(\"Rename %2\").arg(oldName),\n                                                tr(\"Enter name\"), QLineEdit::Normal, oldName, &ok);\n        if (ok && !newName.isEmpty()) {\n            Core()->renameFunctionVariable(newName, oldName, decompiledFunctionAddress);\n        }\n    }\n}\n\nvoid DecompilerContextMenu::actionDeleteNameTriggered()\n{\n    Core()->delFlag(annotationHere->reference.offset);\n}\n\nvoid DecompilerContextMenu::actionEditFunctionVariablesTriggered()\n{\n    if (!isFunctionVariable()) {\n        return;\n    } else if (!variablePresentInRizin()) {\n        QMessageBox::critical(\n                parentForDialog(),\n                tr(\"Edit local variable %1\").arg(QString(annotationHere->variable.name)),\n                tr(\"Can't edit this variable. \"\n                   \"Only local variables defined in disassembly can be edited.\"));\n        return;\n    }\n    EditVariablesDialog dialog(decompiledFunctionAddress, QString(annotationHere->variable.name),\n                               parentForDialog());\n    dialog.exec();\n}\n\nvoid DecompilerContextMenu::actionXRefsTriggered()\n{\n    if (!isReference()) {\n        return;\n    }\n    XrefsDialog dialog(mainWindow);\n    QString displayString = (annotationHere->type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME)\n            ? QString(annotationHere->reference.name)\n            : RzAddressString(annotationHere->reference.offset);\n    dialog.fillRefsForAddress(annotationHere->reference.offset, displayString, false);\n    dialog.exec();\n}\n\nvoid DecompilerContextMenu::actionToggleBreakpointTriggered()\n{\n    if (!this->availableBreakpoints.isEmpty()) {\n        setIsTogglingBreakpoints(true);\n        for (auto offsetToRemove : this->availableBreakpoints) {\n            Core()->toggleBreakpoint(offsetToRemove);\n        }\n        this->availableBreakpoints.clear();\n        setIsTogglingBreakpoints(false);\n        return;\n    }\n    if (this->firstOffsetInLine == RVA_MAX)\n        return;\n\n    Core()->toggleBreakpoint(this->firstOffsetInLine);\n}\n\nvoid DecompilerContextMenu::actionAdvancedBreakpointTriggered()\n{\n    if (!availableBreakpoints.empty()) {\n        // Edit the earliest breakpoint in the line\n        BreakpointsDialog::editBreakpoint(\n                Core()->getBreakpointAt(this->availableBreakpoints.first()), this);\n    } else {\n        // Add a breakpoint to the earliest offset in the line\n        BreakpointsDialog::createNewBreakpoint(this->firstOffsetInLine, this);\n    }\n}\n\nvoid DecompilerContextMenu::actionContinueUntilTriggered()\n{\n    Core()->continueUntilDebug(offset);\n}\n\nvoid DecompilerContextMenu::actionSetPCTriggered()\n{\n    QString progCounterName = Core()->getRegisterName(\"PC\");\n    Core()->setRegister(progCounterName, RzAddressString(offset).toUpper());\n}\n\n// Set up menus\n\nvoid DecompilerContextMenu::addBreakpointMenu()\n{\n    breakpointMenu = addMenu(tr(\"Breakpoint\"));\n\n    setActionToggleBreakpoint();\n    breakpointMenu->addAction(&actionToggleBreakpoint);\n    setActionAdvancedBreakpoint();\n    breakpointMenu->addAction(&actionAdvancedBreakpoint);\n}\n\nvoid DecompilerContextMenu::addDebugMenu()\n{\n    debugMenu = addMenu(tr(\"Debug\"));\n\n    setActionContinueUntil();\n    debugMenu->addAction(&actionContinueUntil);\n    setActionSetPC();\n    debugMenu->addAction(&actionSetPC);\n}\n\nvoid DecompilerContextMenu::updateTargetMenuActions()\n{\n    for (auto action : showTargetMenuActions) {\n        removeAction(action);\n        auto menu = action->menu();\n        if (menu) {\n            menu->deleteLater();\n        }\n        action->deleteLater();\n    }\n    showTargetMenuActions.clear();\n    RzCoreLocked core = Core()->lock();\n    if (isReference()) {\n        QString name;\n        QMenu *menu = nullptr;\n        if (annotationHere->type == RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE\n            || annotationHere->type == RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE) {\n            menu = mainWindow->createShowInMenu(this, annotationHere->reference.offset,\n                                                MainWindow::AddressTypeHint::Data);\n            RVA var_addr = annotationHere->reference.offset;\n            RzFlagItem *flagDetails = rz_flag_get_i(core->flags, var_addr);\n            if (flagDetails) {\n                name = tr(\"Show %1 in\").arg(flagDetails->name);\n            } else {\n                name = tr(\"Show %1 in\").arg(RzAddressString(annotationHere->reference.offset));\n            }\n        } else if (annotationHere->type == RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME) {\n            menu = mainWindow->createShowInMenu(this, annotationHere->reference.offset,\n                                                MainWindow::AddressTypeHint::Function);\n            name = tr(\"%1 (%2)\").arg(QString(annotationHere->reference.name),\n                                     RzAddressString(annotationHere->reference.offset));\n        }\n        auto action = new QAction(name, this);\n        showTargetMenuActions.append(action);\n        action->setMenu(menu);\n        insertActions(copySeparator, showTargetMenuActions);\n    }\n}\n\nbool DecompilerContextMenu::isReference()\n{\n    return (annotationHere && rz_annotation_is_reference(annotationHere));\n}\n\nbool DecompilerContextMenu::isFunctionVariable()\n{\n    return (annotationHere && rz_annotation_is_variable(annotationHere));\n}\n\nbool DecompilerContextMenu::variablePresentInRizin()\n{\n    QString variableName(annotationHere->variable.name);\n    QList<VariableDescription> variables = Core()->getVariables(offset);\n    for (const VariableDescription &var : variables) {\n        if (var.name == variableName) {\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "src/menus/DecompilerContextMenu.h",
    "content": "#ifndef DECOMPILERCONTEXTMENU_H\n#define DECOMPILERCONTEXTMENU_H\n\n#include \"core/Cutter.h\"\n#include <QMenu>\n#include <QKeySequence>\n\n#include <rz_util/rz_annotated_code.h>\n\nclass MainWindow;\n\nclass DecompilerContextMenu : public QMenu\n{\n    Q_OBJECT\n\npublic:\n    DecompilerContextMenu(QWidget *parent, MainWindow *mainWindow);\n    ~DecompilerContextMenu();\n\n    bool getIsTogglingBreakpoints();\n    void setAnnotationHere(RzCodeAnnotation *annotation);\n    RVA getFirstOffsetInLine();\n\nsignals:\n    void copy();\n\npublic slots:\n    void setCurHighlightedWord(QString word);\n    void setOffset(RVA newOffset);\n    void setDecompiledFunctionAddress(RVA functionAddr);\n    void setFirstOffsetInLine(RVA firstOffset);\n    void setAvailableBreakpoints(QVector<RVA> offsetList);\n\nprivate slots:\n    void aboutToShowSlot();\n    void aboutToHideSlot();\n\n    void actionCopyTriggered();\n    void actionCopyInstructionAddressTriggered();\n    void actionCopyReferenceAddressTriggered();\n\n    void actionAddCommentTriggered();\n    void actionDeleteCommentTriggered();\n\n    void actionRenameThingHereTriggered();\n    void actionDeleteNameTriggered();\n\n    void actionEditFunctionVariablesTriggered();\n\n    void actionXRefsTriggered();\n\n    void actionToggleBreakpointTriggered();\n    void actionAdvancedBreakpointTriggered();\n\n    void actionContinueUntilTriggered();\n    void actionSetPCTriggered();\n\nprivate:\n    // Private variables\n    MainWindow *mainWindow;\n    QString curHighlightedWord;\n    RVA offset;\n    RVA decompiledFunctionAddress;\n    /**\n     * Lowest offset among all offsets present in the line under cursor in the decompiler widget.\n     */\n    RVA firstOffsetInLine;\n    /**\n     * When the actionToggleBreakpoint has been triggered, and it hasn't finished executing,\n     * the value of this variable will be true, otherwise false\n     */\n    bool isTogglingBreakpoints;\n    /**\n     * List of the offsets of all the breakpoints (enabled and disabled) that are present in the\n     * line under cursor.\n     */\n    QVector<RVA> availableBreakpoints;\n    /**\n     * Context-related annotation for the data under cursor in the decompiler widget.\n     * If such an annotation doesn't exist, its value is nullptr.\n     */\n    RzCodeAnnotation *annotationHere;\n\n    // Actions and menus in the context menu\n    QAction actionCopy;\n    QAction actionCopyInstructionAddress;\n    QAction actionCopyReferenceAddress;\n    QAction *copySeparator;\n\n    QAction actionShowInSubmenu;\n    QList<QAction *> showTargetMenuActions;\n\n    QAction actionAddComment;\n    QAction actionDeleteComment;\n\n    QAction actionRenameThingHere;\n    QAction actionDeleteName;\n\n    QAction actionEditFunctionVariables;\n\n    QAction actionXRefs;\n\n    QMenu *breakpointMenu;\n    QAction actionToggleBreakpoint;\n    QAction actionAdvancedBreakpoint;\n\n    QMenu *breakpointsInLineMenu;\n\n    QMenu *debugMenu;\n    QAction actionContinueUntil;\n    QAction actionSetPC;\n\n    // Private Functions\n\n    /**\n     * \\return widget that should be used as parent for presenting dialogs\n     */\n    QWidget *parentForDialog();\n\n    /**\n     * @brief Sets the shortcut context in all the actions contained\n     * in the specified QMenu to Qt::WidgetWithChildrenShortcut.\n     *\n     * @param menu - QMenu specified\n     */\n    void setShortcutContextInActions(QMenu *menu);\n    void setupBreakpointsInLineMenu();\n    void setIsTogglingBreakpoints(bool isToggling);\n\n    // Set actions\n    void setActionCopy();\n\n    void setActionShowInSubmenu();\n\n    void setActionAddComment();\n    void setActionDeleteComment();\n\n    void setActionXRefs();\n\n    void setActionRenameThingHere();\n    void setActionDeleteName();\n\n    void setActionEditFunctionVariables();\n\n    void setActionToggleBreakpoint();\n    void setActionAdvancedBreakpoint();\n\n    void setActionContinueUntil();\n    void setActionSetPC();\n\n    // Add Menus\n    void addBreakpointMenu();\n    void addDebugMenu();\n\n    /**\n     * @brief Updates targeted \"Show in\" menu.\n     *\n     * Removes all actions from the existing targeted \"show in\" menu. If annotationHere\n     * represents an item that has an address assigned to it, insert actions compatible with the\n     * type of this item in the targeted \"Show in\" menu.\n     */\n    void updateTargetMenuActions();\n\n    /**\n     * @brief Check if annotationHere is a reference (function name,\n     * global variable, constant variable with an address).\n     *\n     * @return True if annotationHere is a reference, otherwise false.\n     */\n    bool isReference();\n    /**\n     * @brief Check if annotationHere is a function variable\n     * (local variable or function parameter).\n     *\n     * @return True if annotationHere is a function variable, otherwise false.\n     */\n    bool isFunctionVariable();\n    /**\n     * @brief Check if the function variable annotated by annotationHere is\n     * present in Rizin.\n     *\n     * @return True if the variable is present, otherwise false\n     */\n    bool variablePresentInRizin();\n};\n\n#endif // DECOMPILERCONTEXTMENU_H\n"
  },
  {
    "path": "src/menus/DisassemblyContextMenu.cpp",
    "content": "#include \"DisassemblyContextMenu.h\"\n#include \"dialogs/preferences/PreferencesDialog.h\"\n#include \"dialogs/EditInstructionDialog.h\"\n#include \"dialogs/CommentsDialog.h\"\n#include \"dialogs/FlagDialog.h\"\n#include \"dialogs/GlobalVariableDialog.h\"\n#include \"dialogs/XrefsDialog.h\"\n#include \"dialogs/EditVariablesDialog.h\"\n#include \"dialogs/SetToDataDialog.h\"\n#include \"dialogs/EditFunctionDialog.h\"\n#include \"dialogs/EditStringDialog.h\"\n#include \"dialogs/BreakpointsDialog.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include \"MainWindow.h\"\n\n#include <QtCore>\n#include <QShortcut>\n#include <QJsonArray>\n#include <QClipboard>\n#include <QApplication>\n#include <QPushButton>\n#include <QInputDialog>\n\nDisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *mainWindow)\n    : QMenu(parent),\n      offset(0),\n      canCopy(false),\n      mainWindow(mainWindow),\n      ioModesController(this),\n      actionEditInstruction(this),\n      actionNopInstruction(this),\n      actionJmpReverse(this),\n      actionEditBytes(this),\n      actionCopy(this),\n      actionCopyAddr(this),\n      actionCopyInstrBytes(this),\n      actionAddComment(this),\n      actionAnalyzeFunction(this),\n      actionEditFunction(this),\n      actionRename(this),\n      actionGlobalVar(this),\n      actionSetFunctionVarTypes(this),\n      actionXRefs(this),\n      actionXRefsForVariables(this),\n      actionDeleteComment(this),\n      actionDeleteFlag(this),\n      actionDeleteFunction(this),\n      actionSetBaseBinary(this),\n      actionSetBaseOctal(this),\n      actionSetBaseDecimal(this),\n      actionSetBaseHexadecimal(this),\n      actionSetBasePort(this),\n      actionSetBaseIPAddr(this),\n      actionSetBaseSyscall(this),\n      actionSetBaseString(this),\n      actionSetBits16(this),\n      actionSetBits32(this),\n      actionSetBits64(this),\n      actionContinueUntil(this),\n      actionSetPC(this),\n      actionAddBreakpoint(this),\n      actionAdvancedBreakpoint(this),\n      actionSetToCode(this),\n      actionSetAsStringAuto(this),\n      actionSetAsStringRemove(this),\n      actionSetAsStringAdvanced(this),\n      actionSetToDataEx(this),\n      actionSetToDataByte(this),\n      actionSetToDataWord(this),\n      actionSetToDataDword(this),\n      actionSetToDataQword(this),\n      showInSubmenu(this)\n{\n    initShortcutAction(&actionCopy, \"Disassembly.copy\", SLOT(on_actionCopy_triggered()));\n    addAction(&actionCopy);\n\n    initShortcutAction(&actionCopyAddr, \"General.copyAddress\", SLOT(on_actionCopyAddr_triggered()));\n    addAction(&actionCopyAddr);\n\n    initShortcutAction(&actionCopyInstrBytes, \"Disassembly.copyInstructionBytes\",\n                       SLOT(on_actionCopyInstrBytes_triggered()));\n    addAction(&actionCopyInstrBytes);\n\n    initAction(&showInSubmenu, tr(\"Show in\"), nullptr);\n    addAction(&showInSubmenu);\n\n    copySeparator = addSeparator();\n\n    initShortcutAction(&actionAddComment, \"General.addComment\",\n                       SLOT(on_actionAddComment_triggered()));\n    addAction(&actionAddComment);\n\n    initShortcutAction(&actionSetFunctionVarTypes, \"Disassembly.retypeLocals\",\n                       SLOT(on_actionSetFunctionVarTypes_triggered()));\n    addAction(&actionSetFunctionVarTypes);\n\n    initShortcutAction(&actionEditFunction, \"Disassembly.editFunction\",\n                       SLOT(on_actionEditFunction_triggered()));\n    addAction(&actionEditFunction);\n\n    initAction(&actionDeleteComment, tr(\"Delete comment\"),\n               SLOT(on_actionDeleteComment_triggered()));\n    addAction(&actionDeleteComment);\n\n    initAction(&actionDeleteFlag, tr(\"Delete flag\"), SLOT(on_actionDeleteFlag_triggered()));\n    addAction(&actionDeleteFlag);\n\n    initShortcutAction(&actionDeleteFunction, \"Disassembly.undefineFunction\",\n                       SLOT(on_actionDeleteFunction_triggered()));\n    addAction(&actionDeleteFunction);\n\n    initShortcutAction(&actionAnalyzeFunction, \"Disassembly.defineFunction\",\n                       SLOT(on_actionAnalyzeFunction_triggered()));\n    addAction(&actionAnalyzeFunction);\n\n    addSeparator();\n\n    addAddAtMenu();\n\n    addSetBaseMenu();\n\n    addSetBitsMenu();\n\n    structureOffsetMenu = addMenu(tr(\"Structure offset\"));\n    connect(structureOffsetMenu, &QMenu::triggered, this,\n            &DisassemblyContextMenu::on_actionStructureOffsetMenu_triggered);\n\n    addSetAsMenu();\n\n    addSeparator();\n\n    initShortcutAction(&actionXRefs, \"General.showXRefs\", SLOT(on_actionXRefs_triggered()));\n    addAction(&actionXRefs);\n\n    initShortcutAction(&actionXRefsForVariables, \"Disassembly.XRefsForVariables\",\n                       SLOT(on_actionXRefsForVariables_triggered()));\n    addAction(&actionXRefsForVariables);\n\n    addSeparator();\n\n    addEditMenu();\n\n    addSeparator();\n\n    addBreakpointMenu();\n    addDebugMenu();\n\n    addSeparator();\n\n    if (mainWindow) {\n        pluginMenu = mainWindow->getContextMenuExtensions(MainWindow::ContextMenuType::Disassembly);\n        pluginActionMenuAction = addMenu(pluginMenu);\n    }\n\n    addSeparator();\n\n    connect(this, &DisassemblyContextMenu::aboutToShow, this,\n            &DisassemblyContextMenu::aboutToShowSlot);\n    connect(this, &DisassemblyContextMenu::aboutToHide, this,\n            &DisassemblyContextMenu::aboutToHideSlot);\n}\n\nDisassemblyContextMenu::~DisassemblyContextMenu() {}\n\nQWidget *DisassemblyContextMenu::parentForDialog()\n{\n    return parentWidget();\n}\n\nvoid DisassemblyContextMenu::addAddAtMenu()\n{\n    setAsMenu = addMenu(tr(\"Add at...\"));\n\n    initShortcutAction(&actionRename, \"Disassembly.rename\", SLOT(on_actionRename_triggered()));\n    setAsMenu->addAction(&actionRename);\n\n    initShortcutAction(&actionGlobalVar, \"Disassembly.globalVariable\",\n                       SLOT(on_actionGlobalVar_triggered()));\n    setAsMenu->addAction(&actionGlobalVar);\n}\n\nvoid DisassemblyContextMenu::addSetBaseMenu()\n{\n    setBaseMenu = addMenu(tr(\"Set base of immediate value to..\"));\n\n    initAction(&actionSetBaseBinary, tr(\"Binary\"));\n    setBaseMenu->addAction(&actionSetBaseBinary);\n    connect(&actionSetBaseBinary, &QAction::triggered, this, [this] { setBase(\"b\"); });\n\n    initAction(&actionSetBaseOctal, tr(\"Octal\"));\n    setBaseMenu->addAction(&actionSetBaseOctal);\n    connect(&actionSetBaseOctal, &QAction::triggered, this, [this] { setBase(\"o\"); });\n\n    initAction(&actionSetBaseDecimal, tr(\"Decimal\"));\n    setBaseMenu->addAction(&actionSetBaseDecimal);\n    connect(&actionSetBaseDecimal, &QAction::triggered, this, [this] { setBase(\"d\"); });\n\n    initAction(&actionSetBaseHexadecimal, tr(\"Hexadecimal\"));\n    setBaseMenu->addAction(&actionSetBaseHexadecimal);\n    connect(&actionSetBaseHexadecimal, &QAction::triggered, this, [this] { setBase(\"h\"); });\n\n    initAction(&actionSetBasePort, tr(\"Network Port\"));\n    setBaseMenu->addAction(&actionSetBasePort);\n    connect(&actionSetBasePort, &QAction::triggered, this, [this] { setBase(\"p\"); });\n\n    initAction(&actionSetBaseIPAddr, tr(\"IP Address\"));\n    setBaseMenu->addAction(&actionSetBaseIPAddr);\n    connect(&actionSetBaseIPAddr, &QAction::triggered, this, [this] { setBase(\"i\"); });\n\n    initAction(&actionSetBaseSyscall, tr(\"Syscall\"));\n    setBaseMenu->addAction(&actionSetBaseSyscall);\n    connect(&actionSetBaseSyscall, &QAction::triggered, this, [this] { setBase(\"S\"); });\n\n    initAction(&actionSetBaseString, tr(\"String\"));\n    setBaseMenu->addAction(&actionSetBaseString);\n    connect(&actionSetBaseString, &QAction::triggered, this, [this] { setBase(\"s\"); });\n}\n\nvoid DisassemblyContextMenu::addSetBitsMenu()\n{\n    setBitsMenu = addMenu(tr(\"Set current bits to...\"));\n\n    initAction(&actionSetBits16, \"16\");\n    setBitsMenu->addAction(&actionSetBits16);\n    connect(&actionSetBits16, &QAction::triggered, this, [this] { setBits(16); });\n\n    initAction(&actionSetBits32, \"32\");\n    setBitsMenu->addAction(&actionSetBits32);\n    connect(&actionSetBits32, &QAction::triggered, this, [this] { setBits(32); });\n\n    initAction(&actionSetBits64, \"64\");\n    setBitsMenu->addAction(&actionSetBits64);\n    connect(&actionSetBits64, &QAction::triggered, this, [this] { setBits(64); });\n}\n\nvoid DisassemblyContextMenu::addSetAsMenu()\n{\n    setAsMenu = addMenu(tr(\"Set as...\"));\n\n    initShortcutAction(&actionSetToCode, \"Disassembly.setToCode\",\n                       SLOT(on_actionSetToCode_triggered()));\n    setAsMenu->addAction(&actionSetToCode);\n\n    setAsString = setAsMenu->addMenu(tr(\"String...\"));\n\n    initShortcutAction(&actionSetAsStringAuto, \"Disassembly.setAsString\",\n                       SLOT(on_actionSetAsString_triggered()));\n    initAction(&actionSetAsStringRemove, tr(\"Remove\"),\n               SLOT(on_actionSetAsStringRemove_triggered()));\n    initShortcutAction(&actionSetAsStringAdvanced, \"Disassembly.setAsStringAdvanced\",\n                       SLOT(on_actionSetAsStringAdvanced_triggered()));\n\n    setAsString->addAction(&actionSetAsStringAuto);\n    setAsString->addAction(&actionSetAsStringRemove);\n    setAsString->addAction(&actionSetAsStringAdvanced);\n\n    addSetToDataMenu();\n}\n\nvoid DisassemblyContextMenu::addSetToDataMenu()\n{\n    setToDataMenu = setAsMenu->addMenu(tr(\"Data...\"));\n\n    initAction(&actionSetToDataByte, tr(\"Byte\"));\n    setToDataMenu->addAction(&actionSetToDataByte);\n    connect(&actionSetToDataByte, &QAction::triggered, this, [this] { setToData(1); });\n\n    initAction(&actionSetToDataWord, tr(\"Word\"));\n    setToDataMenu->addAction(&actionSetToDataWord);\n    connect(&actionSetToDataWord, &QAction::triggered, this, [this] { setToData(2); });\n\n    initAction(&actionSetToDataDword, tr(\"Dword\"));\n    setToDataMenu->addAction(&actionSetToDataDword);\n    connect(&actionSetToDataDword, &QAction::triggered, this, [this] { setToData(4); });\n\n    initAction(&actionSetToDataQword, tr(\"Qword\"));\n    setToDataMenu->addAction(&actionSetToDataQword);\n    connect(&actionSetToDataQword, &QAction::triggered, this, [this] { setToData(8); });\n\n    initShortcutAction(&actionSetToDataEx, \"Disassembly.setToDataEx\",\n                       SLOT(on_actionSetToDataEx_triggered()));\n    actionSetToDataEx.setText(tr(\"Advanced\"));\n    setToDataMenu->addAction(&actionSetToDataEx);\n\n    auto switchAction = new QAction(this);\n    initShortcutAction(switchAction, \"Disassembly.setToData\", SLOT(on_actionSetToData_triggered()));\n    setToDataMenu->addAction(switchAction);\n}\n\nvoid DisassemblyContextMenu::addEditMenu()\n{\n    editMenu = addMenu(tr(\"Edit\"));\n\n    initAction(&actionEditInstruction, tr(\"Instruction\"),\n               SLOT(on_actionEditInstruction_triggered()));\n    editMenu->addAction(&actionEditInstruction);\n\n    initAction(&actionNopInstruction, tr(\"Nop Instruction\"),\n               SLOT(on_actionNopInstruction_triggered()));\n    editMenu->addAction(&actionNopInstruction);\n\n    initAction(&actionEditBytes, tr(\"Bytes\"), SLOT(on_actionEditBytes_triggered()));\n    editMenu->addAction(&actionEditBytes);\n\n    initAction(&actionJmpReverse, tr(\"Reverse Jump\"), SLOT(on_actionJmpReverse_triggered()));\n    editMenu->addAction(&actionJmpReverse);\n}\n\nvoid DisassemblyContextMenu::addBreakpointMenu()\n{\n    breakpointMenu = addMenu(tr(\"Breakpoint\"));\n\n    initShortcutAction(&actionAddBreakpoint, \"Debug.toggleBreakpoint\",\n                       SLOT(on_actionAddBreakpoint_triggered()));\n    breakpointMenu->addAction(&actionAddBreakpoint);\n    initShortcutAction(&actionAdvancedBreakpoint, \"Debug.advancedBreakpoint\",\n                       SLOT(on_actionAdvancedBreakpoint_triggered()));\n    breakpointMenu->addAction(&actionAdvancedBreakpoint);\n}\n\nvoid DisassemblyContextMenu::addDebugMenu()\n{\n    debugMenu = addMenu(tr(\"Debug\"));\n\n    initAction(&actionContinueUntil, tr(\"Continue until line\"),\n               SLOT(on_actionContinueUntil_triggered()));\n    debugMenu->addAction(&actionContinueUntil);\n\n    initAction(&actionSetPC, \"Set PC\", SLOT(on_actionSetPC_triggered()));\n    debugMenu->addAction(&actionSetPC);\n}\n\nQVector<DisassemblyContextMenu::ThingUsedHere> DisassemblyContextMenu::getThingUsedHere(RVA offset)\n{\n    RzCoreLocked core(Core());\n    auto p = fromOwned(rz_core_analysis_name(core, offset), rz_core_analysis_name_free);\n    if (!p) {\n        return {};\n    }\n\n    QVector<ThingUsedHere> result;\n    ThingUsedHere th;\n    th.offset = p->offset;\n    th.name = Config()->getConfigBool(\"asm.flags.real\") && p->realname ? p->realname : p->name;\n    switch (p->type) {\n    case RZ_CORE_ANALYSIS_NAME_TYPE_FLAG:\n        th.type = ThingUsedHere::Type::Flag;\n        break;\n    case RZ_CORE_ANALYSIS_NAME_TYPE_FUNCTION:\n        th.type = ThingUsedHere::Type::Function;\n        break;\n    case RZ_CORE_ANALYSIS_NAME_TYPE_VAR:\n        th.type = ThingUsedHere::Type::Var;\n        break;\n    case RZ_CORE_ANALYSIS_NAME_TYPE_ADDRESS:\n    default:\n        th.type = ThingUsedHere::Type::Address;\n        break;\n    }\n    result.push_back(th);\n    return result;\n}\n\nvoid DisassemblyContextMenu::setOffset(RVA offset)\n{\n    this->offset = offset;\n    this->actionSetFunctionVarTypes.setVisible(true);\n}\n\nvoid DisassemblyContextMenu::setCanCopy(bool enabled)\n{\n    this->canCopy = enabled;\n}\n\nvoid DisassemblyContextMenu::setCurHighlightedWord(const QString &text)\n{\n    this->curHighlightedWord = text;\n    // Update the renaming options only when a new word is selected\n    setupRenaming();\n}\n\nDisassemblyContextMenu::ThingUsedHere DisassemblyContextMenu::getThingAt(ut64 address)\n{\n    ThingUsedHere tuh;\n    auto core = Core()->lock();\n    RzAnalysisFunction *fcn = Core()->functionAt(address);\n    RzFlagItem *flag = rz_flag_get_i(core->flags, address);\n\n    // We will lookup through existing rizin types to find something relevant\n\n    if (fcn != nullptr) {\n        // It is a function\n        tuh.type = ThingUsedHere::Type::Function;\n        tuh.name = fcn->name;\n    } else if (flag != nullptr) {\n        // It is a flag\n        tuh.type = ThingUsedHere::Type::Flag;\n        if (Config()->getConfigBool(\"asm.flags.real\") && flag->realname) {\n            tuh.name = flag->realname;\n        } else {\n            tuh.name = flag->name;\n        }\n    } else {\n        // Consider it an address\n        tuh.type = ThingUsedHere::Type::Address;\n    }\n\n    tuh.offset = address;\n    return tuh;\n}\n\nvoid DisassemblyContextMenu::buildRenameMenu(ThingUsedHere *tuh)\n{\n    if (!tuh) {\n        qWarning() << \"Unexpected behavior null pointer passed to \"\n                      \"DisassemblyContextMenu::buildRenameMenu\";\n        doRenameAction = RENAME_DO_NOTHING;\n        return;\n    }\n\n    actionDeleteFlag.setVisible(false);\n    if (tuh->type == ThingUsedHere::Type::Address) {\n        doRenameAction = RENAME_ADD_FLAG;\n        doRenameInfo.name = RzAddressString(tuh->offset);\n        doRenameInfo.addr = tuh->offset;\n        actionRename.setText(tr(\"Add flag at %1 (used here)\").arg(doRenameInfo.name));\n    } else if (tuh->type == ThingUsedHere::Type::Function) {\n        doRenameAction = RENAME_FUNCTION;\n        doRenameInfo.name = tuh->name;\n        doRenameInfo.addr = tuh->offset;\n        actionRename.setText(tr(\"Rename \\\"%1\\\"\").arg(doRenameInfo.name));\n    } else if (tuh->type == ThingUsedHere::Type::Var) {\n        doRenameAction = RENAME_LOCAL;\n        doRenameInfo.name = tuh->name;\n        doRenameInfo.addr = tuh->offset;\n        actionRename.setText(tr(\"Rename local \\\"%1\\\"\").arg(tuh->name));\n    } else if (tuh->type == ThingUsedHere::Type::Flag) {\n        doRenameAction = RENAME_FLAG;\n        doRenameInfo.name = tuh->name;\n        doRenameInfo.addr = tuh->offset;\n        actionRename.setText(tr(\"Rename flag \\\"%1\\\" (used here)\").arg(doRenameInfo.name));\n        actionDeleteFlag.setVisible(true);\n    } else {\n        qWarning() << \"Unexpected renaming type\";\n        doRenameAction = RENAME_DO_NOTHING;\n    }\n}\n\nvoid DisassemblyContextMenu::setupRenaming()\n{\n    // We parse our highlighted word as an address\n    ut64 selection = Core()->num(curHighlightedWord);\n\n    // First, let's try to see if current line (offset) contains a local variable or a function\n    ThingUsedHere *tuh = nullptr;\n    ThingUsedHere thingAt;\n    auto things = getThingUsedHere(offset);\n    for (auto &thing : things) {\n        if (thing.offset == selection || thing.name == curHighlightedWord) {\n            // We matched something on current line\n            tuh = &thing;\n            break;\n        }\n    }\n\n    if (!tuh) {\n        // Nothing matched on current line, is there anything valid coming from our selection?\n        thingAt = getThingAt(selection);\n\n        if (thingAt.offset == 0) {\n            // We parsed something which resolved to 0, it's very likely nothing interesting was\n            // selected So we fallback on current line offset\n            thingAt = getThingAt(offset);\n        }\n\n        // However, since for the moment selection selects *every* lines which match a specific\n        // offset, make sure we didn't want to select a local variable rather than the function\n        // itself\n        if (thingAt.type == ThingUsedHere::Type::Function) {\n            auto vars = Core()->getVariables(offset);\n            for (auto v : vars) {\n                if (v.name == curHighlightedWord) {\n                    // This is a local variable\n                    thingAt.type = ThingUsedHere::Type::Var;\n                    thingAt.name = v.name;\n                    break;\n                }\n            }\n        }\n\n        // In any case, thingAt will contain something we can rename\n        tuh = &thingAt;\n    }\n\n    // Now, build the renaming menu and show it\n    buildRenameMenu(tuh);\n\n    auto name = RzAddressString(tuh->offset);\n    actionGlobalVar.setText(tr(\"Add or change global variable at %1 (used here)\").arg(name));\n\n    actionRename.setVisible(true);\n    actionGlobalVar.setVisible(true);\n}\n\nvoid DisassemblyContextMenu::aboutToShowSlot()\n{\n    // check if set immediate base menu makes sense\n    auto ab = Core()->getRzAnalysisBytesSingle(offset);\n\n    bool immBase = ab && ab->op && (ab->op->val || ab->op->ptr);\n    setBaseMenu->menuAction()->setVisible(immBase);\n    setBitsMenu->menuAction()->setVisible(true);\n\n    // Create structure offset menu if it makes sense\n    QString memBaseReg; // Base register\n    st64 memDisp = 0; // Displacement\n\n    if (ab && ab->op) {\n        CutterJson operands =\n                Core()->parseJson(\"opex\", rz_structured_data_to_json(ab->op->opex), nullptr);\n\n        // Loop through both the operands of the instruction\n        for (const CutterJson operand : operands) {\n            if (operand[\"type\"].toString() == \"mem\" && !operand[\"base\"].toString().contains(\"bp\")\n                && operand[\"disp\"].toSt64() > 0) {\n\n                // The current operand is the one which has an immediate displacement\n                memBaseReg = operand[\"base\"].toString();\n                memDisp = operand[\"disp\"].toSt64();\n                break;\n            }\n        }\n    }\n\n    if (memBaseReg.isEmpty()) {\n        // hide structure offset menu\n        structureOffsetMenu->menuAction()->setVisible(false);\n    } else {\n        // show structure offset menu\n        structureOffsetMenu->menuAction()->setVisible(true);\n        structureOffsetMenu->clear();\n\n        RzCoreLocked core(Core());\n        RzList *typeoffs = rz_type_db_get_by_offset(core->analysis->typedb, memDisp);\n        if (typeoffs) {\n            for (const auto &ty : CutterRzList<RzTypePath>(typeoffs)) {\n                if (RZ_STR_ISEMPTY(ty->path)) {\n                    continue;\n                }\n                structureOffsetMenu->addAction(\"[\" + memBaseReg + \" + \" + ty->path + \"]\")\n                        ->setData(QString(ty->path));\n            }\n            rz_list_free(typeoffs);\n        }\n\n        if (structureOffsetMenu->isEmpty()) {\n            // No possible offset was found so hide the menu\n            structureOffsetMenu->menuAction()->setVisible(false);\n        }\n    }\n\n    actionAnalyzeFunction.setVisible(true);\n\n    // Show the option to remove a defined string only if a string is defined in this address\n    QString stringDefinition = Core()->getMetaString(offset);\n    actionSetAsStringRemove.setVisible(!stringDefinition.isEmpty());\n\n    QString comment = Core()->getCommentAt(offset);\n\n    if (comment.isNull() || comment.isEmpty()) {\n        actionDeleteComment.setVisible(false);\n        actionAddComment.setText(tr(\"Add Comment\"));\n    } else {\n        actionDeleteComment.setVisible(true);\n        actionAddComment.setText(tr(\"Edit Comment\"));\n    }\n\n    actionCopy.setVisible(canCopy);\n    copySeparator->setVisible(canCopy);\n\n    // Handle renaming of variable, function, flag, ...\n    // Note: This might be useless if we consider setCurrentHighlightedWord is always called before\n    setupRenaming();\n\n    // Only show retype for local vars if in a function\n    RzAnalysisFunction *in_fcn = Core()->functionIn(offset);\n    if (in_fcn) {\n        auto vars = Core()->getVariables(offset);\n        actionSetFunctionVarTypes.setVisible(!vars.empty());\n        actionEditFunction.setVisible(true);\n        actionEditFunction.setText(tr(\"Edit function \\\"%1\\\"\").arg(in_fcn->name));\n    } else {\n        actionSetFunctionVarTypes.setVisible(false);\n        actionEditFunction.setVisible(false);\n    }\n\n    // Decide to show Reverse jmp option\n    showReverseJmpQuery();\n\n    if (showInSubmenu.menu() != nullptr) {\n        showInSubmenu.menu()->deleteLater();\n    }\n    showInSubmenu.setMenu(mainWindow->createShowInMenu(this, offset));\n\n    // Only show debug options if we are currently debugging\n    debugMenu->menuAction()->setVisible(Core()->currentlyDebugging);\n    bool hasBreakpoint = Core()->breakpointIndexAt(offset) > -1;\n    actionAddBreakpoint.setText(hasBreakpoint ? tr(\"Remove breakpoint\") : tr(\"Add breakpoint\"));\n    actionAdvancedBreakpoint.setText(hasBreakpoint ? tr(\"Edit breakpoint\")\n                                                   : tr(\"Advanced breakpoint\"));\n    QString progCounterName = Core()->getRegisterName(\"PC\").toUpper();\n    actionSetPC.setText(\"Set \" + progCounterName + \" here\");\n\n    if (pluginMenu) {\n        pluginActionMenuAction->setVisible(!pluginMenu->isEmpty());\n        for (QAction *pluginAction : pluginMenu->actions()) {\n            pluginAction->setData(QVariant::fromValue(offset));\n        }\n    }\n\n    bool isLocalVar = isHighlightedWordLocalVar();\n    actionXRefsForVariables.setVisible(isLocalVar);\n    if (isLocalVar) {\n        actionXRefsForVariables.setText(tr(\"X-Refs for %1\").arg(curHighlightedWord));\n    }\n\n    actionEditInstruction.setEnabled(Core()->hasAssembler());\n}\n\nvoid DisassemblyContextMenu::aboutToHideSlot()\n{\n    actionXRefsForVariables.setVisible(true);\n}\n\nvoid DisassemblyContextMenu::on_actionEditInstruction_triggered()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    EditInstructionDialog e(EDIT_TEXT, parentForDialog());\n    e.setWindowTitle(tr(\"Edit Instruction at %1\").arg(RzAddressString(offset)));\n\n    QString oldInstructionOpcode = Core()->getInstructionOpcode(offset);\n    QString oldInstructionBytes = Core()->getInstructionBytes(offset);\n\n    e.setInstruction(oldInstructionOpcode);\n\n    if (e.exec()) {\n        bool fillWithNops = e.needsNops();\n        QString userInstructionOpcode = e.getInstruction();\n        if (userInstructionOpcode != oldInstructionOpcode) {\n            Core()->editInstruction(offset, userInstructionOpcode, fillWithNops);\n        }\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionNopInstruction_triggered()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    Core()->nopInstruction(offset);\n}\n\nvoid DisassemblyContextMenu::showReverseJmpQuery()\n{\n    actionJmpReverse.setVisible(false);\n    auto ab = Core()->getRzAnalysisBytesSingle(offset);\n    if (!(ab && ab->op)) {\n        return;\n    }\n    if (ab->op->type == RZ_ANALYSIS_OP_TYPE_CJMP) {\n        actionJmpReverse.setVisible(true);\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionJmpReverse_triggered()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    Core()->jmpReverse(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionEditBytes_triggered()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    EditInstructionDialog e(EDIT_BYTES, parentForDialog());\n    e.setWindowTitle(tr(\"Edit Bytes at %1\").arg(RzAddressString(offset)));\n\n    QString oldBytes = Core()->getInstructionBytes(offset);\n    e.setInstruction(oldBytes);\n\n    if (e.exec()) {\n        QString bytes = e.getInstruction();\n        if (bytes != oldBytes) {\n            Core()->editBytes(offset, bytes);\n        }\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionCopy_triggered()\n{\n    emit copy();\n}\n\nvoid DisassemblyContextMenu::on_actionCopyAddr_triggered()\n{\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(RzAddressString(offset));\n}\n\nvoid DisassemblyContextMenu::on_actionCopyInstrBytes_triggered()\n{\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(Core()->getInstructionBytes(offset));\n}\n\nvoid DisassemblyContextMenu::on_actionAddBreakpoint_triggered()\n{\n    Core()->toggleBreakpoint(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionAdvancedBreakpoint_triggered()\n{\n    int index = Core()->breakpointIndexAt(offset);\n    if (index >= 0) {\n        BreakpointsDialog::editBreakpoint(Core()->getBreakpointAt(offset), parentForDialog());\n    } else {\n        BreakpointsDialog::createNewBreakpoint(offset, parentForDialog());\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionContinueUntil_triggered()\n{\n    Core()->continueUntilDebug(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionSetPC_triggered()\n{\n    QString progCounterName = Core()->getRegisterName(\"PC\");\n    Core()->setRegister(progCounterName, RzAddressString(offset).toUpper());\n}\n\nvoid DisassemblyContextMenu::on_actionAddComment_triggered()\n{\n    CommentsDialog::addOrEditComment(offset, parentForDialog());\n}\n\nvoid DisassemblyContextMenu::on_actionAnalyzeFunction_triggered()\n{\n    RVA flagOffset;\n    QString name = Core()->nearestFlag(offset, &flagOffset);\n    if (name.isEmpty() || flagOffset != offset) {\n        // Create a default name for the function\n        QString pfx = Config()->getConfigString(\"analysis.fcnprefix\");\n        if (pfx.isEmpty()) {\n            pfx = QString(\"fcn\");\n        }\n        name = pfx + QString::asprintf(\".%llx\", offset);\n    }\n\n    // Create dialog\n    QInputDialog inputDialog(parentForDialog());\n    inputDialog.resize(500, 100);\n    inputDialog.setWindowTitle(tr(\"New function at %1\").arg(RzAddressString(offset)));\n    inputDialog.setLabelText(tr(\"Function name:\"));\n    inputDialog.setTextValue(name);\n    inputDialog.setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint);\n\n    if (inputDialog.exec() != QDialog::Accepted) {\n        return;\n    }\n\n    QString functionName = inputDialog.textValue().trimmed();\n\n    if (!functionName.isEmpty()) {\n        Core()->createFunctionAt(offset, functionName);\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionRename_triggered()\n{\n    bool ok = false;\n    if (doRenameAction == RENAME_FUNCTION) {\n        QString newName = QInputDialog::getText(\n                this->mainWindow, tr(\"Rename function %2\").arg(doRenameInfo.name),\n                tr(\"Function name:\"), QLineEdit::Normal, doRenameInfo.name, &ok);\n        if (ok && !newName.isEmpty()) {\n            Core()->renameFunction(doRenameInfo.addr, newName);\n        }\n    } else if (doRenameAction == RENAME_FLAG || doRenameAction == RENAME_ADD_FLAG) {\n        FlagDialog dialog(doRenameInfo.addr, parentForDialog());\n        ok = dialog.exec();\n    } else if (doRenameAction == RENAME_LOCAL) {\n        RzAnalysisFunction *fcn = Core()->functionIn(offset);\n        if (fcn) {\n            EditVariablesDialog dialog(fcn->addr, curHighlightedWord, parentForDialog());\n            if (!dialog.empty()) {\n                // Don't show the dialog if there are no variables\n                ok = dialog.exec();\n            }\n        }\n    } else if (doRenameAction == RENAME_DO_NOTHING) {\n        // Do nothing\n    } else {\n        qWarning() << \"Unhandled renaming action: \" << doRenameAction;\n        assert(false);\n    }\n\n    if (ok) {\n        // Rebuild menu in case the user presses the rename shortcut directly before clicking\n        setupRenaming();\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionGlobalVar_triggered()\n{\n    bool ok = false;\n    GlobalVariableDialog dialog(doRenameInfo.addr, parentForDialog());\n    ok = dialog.exec();\n\n    if (ok) {\n        // Rebuild menu in case the user presses the rename shortcut directly before clicking\n        setupRenaming();\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()\n{\n    RzAnalysisFunction *fcn = Core()->functionIn(offset);\n\n    if (!fcn) {\n        QMessageBox::critical(this, tr(\"Re-type Local Variables\"),\n                              tr(\"You must be in a function to define variable types.\"));\n        return;\n    }\n\n    EditVariablesDialog dialog(fcn->addr, curHighlightedWord, parentForDialog());\n    if (dialog.empty()) { // don't show the dialog if there are no variables\n        return;\n    }\n    dialog.exec();\n}\n\nvoid DisassemblyContextMenu::on_actionXRefs_triggered()\n{\n    XrefsDialog dialog(mainWindow);\n    dialog.fillRefsForAddress(offset, RzAddressString(offset), false);\n    dialog.exec();\n}\n\nvoid DisassemblyContextMenu::on_actionXRefsForVariables_triggered()\n{\n    if (isHighlightedWordLocalVar()) {\n        XrefsDialog dialog(mainWindow);\n        dialog.fillRefsForVariable(curHighlightedWord, offset);\n        dialog.exec();\n    }\n}\n\nvoid DisassemblyContextMenu::on_actionSetToCode_triggered()\n{\n    Core()->setToCode(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionSetAsString_triggered()\n{\n    Core()->setAsString(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionSetAsStringRemove_triggered()\n{\n    Core()->removeString(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionSetAsStringAdvanced_triggered()\n{\n    EditStringDialog dialog(parentForDialog());\n    const int predictedStrSize = Core()->getString(offset).size();\n    dialog.setStringSizeValue(predictedStrSize);\n    dialog.setStringStartAddress(offset);\n\n    if (!dialog.exec()) {\n        return;\n    }\n\n    uint64_t strAddr = 0U;\n    if (!dialog.getStringStartAddress(strAddr)) {\n        QMessageBox::critical(this->window(), tr(\"Wrong address\"),\n                              tr(\"Can't edit string at this address\"));\n        return;\n    }\n    CutterCore::StringTypeFormats coreStringType = CutterCore::StringTypeFormats::None;\n\n    const auto strSize = dialog.getStringSizeValue();\n    const auto strType = dialog.getStringType();\n    switch (strType) {\n    case EditStringDialog::StringType::Auto:\n        coreStringType = CutterCore::StringTypeFormats::None;\n        break;\n    case EditStringDialog::StringType::ASCII_LATIN1:\n        coreStringType = CutterCore::StringTypeFormats::ASCII_LATIN1;\n        break;\n    case EditStringDialog::StringType::UTF8:\n        coreStringType = CutterCore::StringTypeFormats::UTF8;\n        break;\n    };\n\n    Core()->setAsString(strAddr, strSize, coreStringType);\n}\n\nvoid DisassemblyContextMenu::on_actionSetToData_triggered()\n{\n    int size = Core()->sizeofDataMeta(offset);\n    if (size > 8 || (size && (size & (size - 1)))) {\n        return;\n    }\n    if (size == 0 || size == 8) {\n        size = 1;\n    } else {\n        size *= 2;\n    }\n    setToData(size);\n}\n\nvoid DisassemblyContextMenu::on_actionSetToDataEx_triggered()\n{\n    SetToDataDialog dialog(offset, parentForDialog());\n    if (!dialog.exec()) {\n        return;\n    }\n    setToData(dialog.getItemSize(), dialog.getItemCount());\n}\n\nvoid DisassemblyContextMenu::on_actionStructureOffsetMenu_triggered(QAction *action)\n{\n    Core()->applyStructureOffset(action->data().toString(), offset);\n}\n\nvoid DisassemblyContextMenu::on_actionDeleteComment_triggered()\n{\n    Core()->delComment(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionDeleteFlag_triggered()\n{\n    Core()->delFlag(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionDeleteFunction_triggered()\n{\n    Core()->delFunction(offset);\n}\n\nvoid DisassemblyContextMenu::on_actionEditFunction_triggered()\n{\n    auto core = Core()->lock();\n    EditFunctionDialog dialog(parentForDialog());\n    RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, offset, 0);\n\n    if (fcn) {\n        dialog.setWindowTitle(tr(\"Edit function %1\").arg(fcn->name));\n        dialog.setNameText(fcn->name);\n\n        QString startAddrText = \"0x\" + QString::number(fcn->addr, 16);\n        dialog.setStartAddrText(startAddrText);\n\n        dialog.setStackSizeText(QString::number(fcn->stack));\n\n        QStringList callConList;\n        RzList *list = rz_analysis_calling_conventions(core->analysis);\n        if (!list) {\n            return;\n        }\n        RzListIter *iter;\n        const char *cc;\n        CutterRzListForeach (list, iter, const char, cc) {\n            callConList << cc;\n        }\n        rz_list_free(list);\n\n        dialog.setCallConList(callConList);\n        dialog.setCallConSelected(fcn->cc);\n\n        if (dialog.exec()) {\n            QString new_name = dialog.getNameText();\n            rz_core_analysis_function_rename(core, fcn->addr, new_name.toStdString().c_str());\n            QString new_start_addr = dialog.getStartAddrText();\n            fcn->addr = Core()->math(new_start_addr);\n            QString new_stack_size = dialog.getStackSizeText();\n            fcn->stack = int(Core()->math(new_stack_size));\n\n            QByteArray newCC = dialog.getCallConSelected().toUtf8();\n            if (!newCC.isEmpty() && rz_analysis_cc_exist(core->analysis, newCC.constData())) {\n                fcn->cc = rz_str_constpool_get(&core->analysis->constpool, newCC.constData());\n            }\n\n            emit Core()->functionsChanged();\n        }\n    }\n}\n\nvoid DisassemblyContextMenu::setBase(QString base)\n{\n    Core()->setImmediateBase(base, offset);\n}\n\nvoid DisassemblyContextMenu::setBits(int bits)\n{\n    Core()->setCurrentBits(bits, offset);\n}\n\nvoid DisassemblyContextMenu::setToData(int size, int repeat)\n{\n    Core()->setToData(offset, size, repeat);\n}\n\nQAction *DisassemblyContextMenu::addAnonymousAction(QString name, const char *slot,\n                                                    QKeySequence keySequence)\n{\n    auto action = new QAction(this);\n    addAction(action);\n    anonymousActions.append(action);\n    initAction(action, name, slot, keySequence);\n    return action;\n}\n\nvoid DisassemblyContextMenu::initAction(QAction *action, QString name, const char *slot)\n{\n    action->setParent(this);\n    parentWidget()->addAction(action);\n    action->setText(name);\n    if (slot) {\n        connect(action, SIGNAL(triggered(bool)), this, slot);\n    }\n}\n\nvoid DisassemblyContextMenu::initAction(QAction *action, QString name, const char *slot,\n                                        QKeySequence keySequence)\n{\n    initAction(action, name, slot);\n    if (keySequence.isEmpty()) {\n        return;\n    }\n    action->setShortcut(keySequence);\n    action->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n}\n\nvoid DisassemblyContextMenu::initShortcutAction(QAction *action, const QString &id,\n                                                const char *slot)\n{\n    Shortcuts()->setupAction(*action, id);\n    action->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n    if (slot) {\n        connect(action, SIGNAL(triggered(bool)), this, slot);\n    }\n}\n\nbool DisassemblyContextMenu::isHighlightedWordLocalVar()\n{\n    QList<VariableDescription> variables = Core()->getVariables(offset);\n    for (const VariableDescription &var : variables) {\n        if (var.name == curHighlightedWord) {\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "src/menus/DisassemblyContextMenu.h",
    "content": "#ifndef DISASSEMBLYCONTEXTMENU_H\n#define DISASSEMBLYCONTEXTMENU_H\n\n#include \"core/Cutter.h\"\n#include \"common/IOModesController.h\"\n#include <QMenu>\n#include <QKeySequence>\n\nclass MainWindow;\n\nclass CUTTER_EXPORT DisassemblyContextMenu : public QMenu\n{\n    Q_OBJECT\n\npublic:\n    DisassemblyContextMenu(QWidget *parent, MainWindow *mainWindow);\n    ~DisassemblyContextMenu();\n\nsignals:\n    void copy();\n\npublic slots:\n    void setOffset(RVA offset);\n    void setCanCopy(bool enabled);\n\n    /**\n     * @brief Sets the value of curHighlightedWord\n     * @param text The current highlighted word\n     */\n    void setCurHighlightedWord(const QString &text);\n\nprivate slots:\n    void aboutToShowSlot();\n    void aboutToHideSlot();\n\n    void on_actionEditFunction_triggered();\n    void on_actionEditInstruction_triggered();\n    void on_actionNopInstruction_triggered();\n    void on_actionJmpReverse_triggered();\n    void on_actionEditBytes_triggered();\n    void showReverseJmpQuery();\n\n    void on_actionCopy_triggered();\n    void on_actionCopyAddr_triggered();\n    void on_actionCopyInstrBytes_triggered();\n    void on_actionAddComment_triggered();\n    void on_actionAnalyzeFunction_triggered();\n    void on_actionRename_triggered();\n    void on_actionGlobalVar_triggered();\n    void on_actionSetFunctionVarTypes_triggered();\n    void on_actionXRefs_triggered();\n    void on_actionXRefsForVariables_triggered();\n\n    void on_actionDeleteComment_triggered();\n    void on_actionDeleteFlag_triggered();\n    void on_actionDeleteFunction_triggered();\n\n    void on_actionAddBreakpoint_triggered();\n    void on_actionAdvancedBreakpoint_triggered();\n    void on_actionContinueUntil_triggered();\n    void on_actionSetPC_triggered();\n\n    void on_actionSetToCode_triggered();\n    void on_actionSetAsString_triggered();\n    void on_actionSetAsStringRemove_triggered();\n    void on_actionSetAsStringAdvanced_triggered();\n    void on_actionSetToData_triggered();\n    void on_actionSetToDataEx_triggered();\n\n    /**\n     * @brief Executed on selecting an offset from the structureOffsetMenu\n     * Uses the applyStructureOffset() function of CutterCore to apply the\n     * structure offset\n     * \\param action The action which trigered the event\n     */\n    void on_actionStructureOffsetMenu_triggered(QAction *action);\n\nprivate:\n    RVA offset;\n    bool canCopy;\n    QString curHighlightedWord; // The current highlighted word\n    MainWindow *mainWindow;\n    IOModesController ioModesController;\n\n    QList<QAction *> anonymousActions;\n\n    QMenu *editMenu;\n    QAction actionEditInstruction;\n    QAction actionNopInstruction;\n    QAction actionJmpReverse;\n    QAction actionEditBytes;\n\n    QAction actionCopy;\n    QAction *copySeparator;\n    QAction actionCopyAddr;\n    QAction actionCopyInstrBytes;\n\n    QAction actionAddComment;\n    QAction actionAnalyzeFunction;\n    QAction actionEditFunction;\n    QAction actionRename;\n    QAction actionGlobalVar;\n    QAction actionSetFunctionVarTypes;\n    QAction actionXRefs;\n    QAction actionXRefsForVariables;\n\n    QAction actionDeleteComment;\n    QAction actionDeleteFlag;\n    QAction actionDeleteFunction;\n\n    QMenu *structureOffsetMenu;\n\n    QMenu *setBaseMenu;\n    QAction actionSetBaseBinary;\n    QAction actionSetBaseOctal;\n    QAction actionSetBaseDecimal;\n    QAction actionSetBaseHexadecimal;\n    QAction actionSetBasePort;\n    QAction actionSetBaseIPAddr;\n    QAction actionSetBaseSyscall;\n    QAction actionSetBaseString;\n\n    QMenu *setBitsMenu;\n    QAction actionSetBits16;\n    QAction actionSetBits32;\n    QAction actionSetBits64;\n\n    QMenu *debugMenu;\n    QAction actionContinueUntil;\n    QAction actionSetPC;\n\n    QMenu *breakpointMenu;\n    QAction actionAddBreakpoint;\n    QAction actionAdvancedBreakpoint;\n\n    QAction actionSetToCode;\n\n    QAction actionSetAsStringAuto;\n    QAction actionSetAsStringRemove;\n    QAction actionSetAsStringAdvanced;\n\n    QMenu *setToDataMenu;\n    QMenu *setAsMenu;\n    QMenu *setAsString;\n    QAction actionSetToDataEx;\n    QAction actionSetToDataByte;\n    QAction actionSetToDataWord;\n    QAction actionSetToDataDword;\n    QAction actionSetToDataQword;\n\n    QAction showInSubmenu;\n    QList<QAction *> showTargetMenuActions;\n    QMenu *pluginMenu = nullptr;\n    QAction *pluginActionMenuAction = nullptr;\n\n    /**\n     * \\return widget that should be used as parent for presenting dialogs\n     */\n    QWidget *parentForDialog();\n\n    // For creating anonymous entries (that are always visible)\n    QAction *addAnonymousAction(QString name, const char *slot, QKeySequence shortcut);\n\n    void initAction(QAction *action, QString name, const char *slot = nullptr);\n    void initAction(QAction *action, QString name, const char *slot, QKeySequence keySequence);\n    void initShortcutAction(QAction *action, const QString &id, const char *slot);\n\n    void setBase(QString base);\n    void setToData(int size, int repeat = 1);\n    void setBits(int bits);\n\n    void addSetBaseMenu();\n    void addSetBitsMenu();\n    void addSetAsMenu();\n    void addSetToDataMenu();\n    void addEditMenu();\n    void addAddAtMenu();\n    void addBreakpointMenu();\n    void addDebugMenu();\n\n    enum DoRenameAction {\n        RENAME_FUNCTION,\n        RENAME_FLAG,\n        RENAME_ADD_FLAG,\n        RENAME_LOCAL,\n        RENAME_DO_NOTHING,\n    };\n    struct DoRenameInfo\n    {\n        ut64 addr;\n        QString name;\n    };\n    DoRenameAction doRenameAction = RENAME_DO_NOTHING;\n    DoRenameInfo doRenameInfo = {};\n\n    /*\n     * @brief Setups up the \"Rename\" option in the context menu\n     *\n     * This function takes into account cursor location so it can choose between current address and\n     * pointed value i.e. `0x000040f3  lea rdi, [0x000199b1]` -> does the user want to add a flag at\n     * 0x40f3 or at 0x199b1? and for that we will rely on |curHighlightedWord| which is the\n     * currently selected word.\n     */\n    void setupRenaming();\n\n    /**\n     * @brief Checks if the currently highlighted word in the disassembly widget\n     * is a local variable or function paramter.\n     * @return Return true if the highlighted word is the name of a local variable or function\n     * parameter, return false otherwise.\n     */\n    bool isHighlightedWordLocalVar();\n    struct ThingUsedHere\n    {\n        QString name;\n        RVA offset;\n        enum class Type { Var, Function, Flag, Address };\n        Type type;\n    };\n    QVector<ThingUsedHere> getThingUsedHere(RVA offset);\n\n    /*\n     * @brief This function checks if the given address contains a function,\n     * a flag or if it is just an address.\n     */\n    ThingUsedHere getThingAt(ut64 address);\n\n    /*\n     * @brief This function will set the text for the renaming menu given a ThingUsedHere\n     * and provide information on how to handle the renaming of this specific thing.\n     * Indeed, selected dialogs are different when it comes to adding a flag, renaming an existing\n     * function, renaming a local variable...\n     *\n     * This function handles every possible object.\n     */\n    void buildRenameMenu(ThingUsedHere *tuh);\n};\n#endif // DISASSEMBLYCONTEXTMENU_H\n"
  },
  {
    "path": "src/menus/FlirtContextMenu.cpp",
    "content": "#include \"FlirtContextMenu.h\"\n#include \"MainWindow.h\"\n\n#include <QtCore>\n#include <QShortcut>\n#include <QJsonArray>\n#include <QClipboard>\n#include <QApplication>\n#include <QPushButton>\n\nFlirtContextMenu::FlirtContextMenu(QWidget *parent, MainWindow *mainWindow)\n    : QMenu(parent), mainWindow(mainWindow)\n{\n    actionCopyLine = new QAction(tr(\"Copy Line\"), this);\n    actionApplySignature = new QAction(tr(\"Apply Signature File\"), this);\n\n    connect(actionCopyLine, &QAction::triggered, this, &FlirtContextMenu::onActionCopyLine);\n    connect(actionApplySignature, &QAction::triggered, this,\n            &FlirtContextMenu::onActionApplySignature);\n\n    addAction(actionCopyLine);\n    addSeparator();\n    addAction(actionApplySignature);\n\n    setHasTarget(false);\n}\n\nFlirtContextMenu::~FlirtContextMenu() {}\n\nvoid FlirtContextMenu::setTarget(const FlirtDescription &flirt)\n{\n    this->entry = flirt;\n    setHasTarget(true);\n}\n\nvoid FlirtContextMenu::clearTarget()\n{\n    setHasTarget(false);\n}\n\nvoid FlirtContextMenu::onActionCopyLine()\n{\n    auto clipboard = QApplication::clipboard();\n    QString text = entry.bin_name + \"\\t\" + entry.arch_name + \"\\t\" + entry.arch_bits + \"\\t\"\n            + entry.n_modules + \"\\t\" + entry.base_name + \"\\t\" + entry.details;\n    clipboard->setText(text);\n}\n\nvoid FlirtContextMenu::onActionApplySignature()\n{\n    if (this->hasTarget) {\n        Core()->applySignature(entry.file_path);\n    }\n}\n\nvoid FlirtContextMenu::setHasTarget(bool hasTarget)\n{\n    this->hasTarget = hasTarget;\n    for (const auto &action : this->actions()) {\n        action->setEnabled(hasTarget);\n    }\n}\n"
  },
  {
    "path": "src/menus/FlirtContextMenu.h",
    "content": "#ifndef FLIRT_CONTEXTMENU_H\n#define FLIRT_CONTEXTMENU_H\n\n#include \"core/Cutter.h\"\n#include <QMenu>\n#include <QKeySequence>\n\nclass MainWindow;\n\nclass CUTTER_EXPORT FlirtContextMenu : public QMenu\n{\n    Q_OBJECT\n\npublic:\n    FlirtContextMenu(QWidget *parent, MainWindow *mainWindow);\n    ~FlirtContextMenu();\n\npublic slots:\n    void setTarget(const FlirtDescription &flirt);\n    void clearTarget();\n\nprivate:\n    void onActionCopyLine();\n    void onActionApplySignature();\n\n    QMenu *pluginMenu;\n    QAction *pluginMenuAction;\n    MainWindow *mainWindow;\n\n    bool hasTarget = false;\n\nprotected:\n    void setHasTarget(bool hasTarget);\n    QAction *actionApplySignature;\n    QAction *actionCopyLine;\n\n    FlirtDescription entry;\n};\n#endif // FLIRT_CONTEXTMENU_H\n"
  },
  {
    "path": "src/plugins/CutterPlugin.h",
    "content": "#ifndef CUTTERPLUGIN_H\n#define CUTTERPLUGIN_H\n\nclass MainWindow;\n\n#include \"widgets/CutterDockWidget.h\"\n\nclass CUTTER_EXPORT CutterPlugin\n{\npublic:\n    virtual ~CutterPlugin() = default;\n\n    /**\n     * @brief Initialize the Plugin\n     *\n     * called right when the plugin is loaded initially\n     */\n    virtual void setupPlugin() = 0;\n\n    /**\n     * @brief Setup any UI components for the Plugin\n     * @param main the MainWindow to add any UI to\n     *\n     * called after Cutter's core UI has been initialized\n     */\n    virtual void setupInterface(MainWindow *main) = 0;\n\n    /**\n     * @brief Register any decompiler implemented by the Plugin\n     *\n     * called during initialization of Cutter, after setupPlugin()\n     */\n    virtual void registerDecompilers() {}\n\n    /**\n     * @brief Shutdown the Plugin\n     *\n     * called just before the Plugin is deleted.\n     * This method is usually only relevant for Python Plugins where there is no\n     * direct equivalent of the destructor.\n     */\n    virtual void terminate() {};\n\n    virtual QString getName() const = 0;\n    virtual QString getAuthor() const = 0;\n    virtual QString getDescription() const = 0;\n    virtual QString getVersion() const = 0;\n};\n\n#define CutterPlugin_iid \"re.rizin.cutter.plugins.CutterPlugin\"\n\nQ_DECLARE_INTERFACE(CutterPlugin, CutterPlugin_iid)\n\n#endif // CUTTERPLUGIN_H\n"
  },
  {
    "path": "src/plugins/PluginManager.cpp",
    "content": "\n#include <cassert>\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n#    include <Python.h>\n#    include <cutterbindings_python.h>\n#    include \"PythonManager.h\"\n#endif\n\n#include \"PluginManager.h\"\n#include \"CutterPlugin.h\"\n#include \"CutterConfig.h\"\n#include \"common/Helpers.h\"\n#include \"common/ResourcePaths.h\"\n\n#include <QDir>\n#include <QCoreApplication>\n#include <QPluginLoader>\n#include <QStandardPaths>\n#include <QDebug>\n\nQ_GLOBAL_STATIC(PluginManager, uniqueInstance)\n\nPluginManager *PluginManager::getInstance()\n{\n    return uniqueInstance;\n}\n\nPluginManager::PluginManager() {}\n\nPluginManager::~PluginManager() {}\n\nvoid PluginManager::loadPlugins(bool enablePlugins)\n{\n    assert(plugins.empty());\n\n    if (!enablePlugins) {\n        // [#2159] list but don't enable the plugins\n        return;\n    }\n\n    QString userPluginDir = getUserPluginsDirectory();\n    if (!userPluginDir.isEmpty()) {\n        loadPluginsFromDir(QDir(userPluginDir), true);\n    }\n    const auto pluginDirs = getPluginDirectories();\n    for (auto &dir : pluginDirs) {\n        if (dir.absolutePath() == userPluginDir) {\n            continue;\n        }\n        loadPluginsFromDir(dir);\n    }\n}\n\nvoid PluginManager::loadPluginsFromDir(const QDir &pluginsDir, bool writable)\n{\n    qInfo() << \"Plugins are loaded from\" << pluginsDir.absolutePath();\n    int loadedPlugins = plugins.size();\n    if (!pluginsDir.exists()) {\n        return;\n    }\n\n    QDir nativePluginsDir = pluginsDir;\n    if (writable) {\n        nativePluginsDir.mkdir(\"native\");\n    }\n    if (nativePluginsDir.cd(\"native\")) {\n        qInfo() << \"Native plugins are loaded from\" << nativePluginsDir.absolutePath();\n        loadNativePlugins(nativePluginsDir);\n    }\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n    QDir pythonPluginsDir = pluginsDir;\n    if (writable) {\n        pythonPluginsDir.mkdir(\"python\");\n    }\n    if (pythonPluginsDir.cd(\"python\")) {\n        qInfo() << \"Python plugins are loaded from\" << pythonPluginsDir.absolutePath();\n        loadPythonPlugins(pythonPluginsDir.absolutePath());\n    }\n#endif\n\n    loadedPlugins = plugins.size() - loadedPlugins;\n    qInfo() << \"Loaded\" << loadedPlugins << \"plugin(s).\";\n}\n\nvoid PluginManager::PluginTerminator::operator()(CutterPlugin *plugin) const\n{\n    plugin->terminate();\n    delete plugin;\n}\n\nvoid PluginManager::destroyPlugins()\n{\n    plugins.clear();\n}\n\nQVector<QDir> PluginManager::getPluginDirectories() const\n{\n    QVector<QDir> result;\n    QStringList locations = Cutter::standardLocations(QStandardPaths::AppDataLocation);\n    for (auto &location : locations) {\n        result.push_back(QDir(location).filePath(\"plugins\"));\n    }\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) && defined(Q_OS_UNIX)\n    QChar listSeparator = ':';\n#else\n    QChar listSeparator = QDir::listSeparator();\n#endif\n    QString extra_plugin_dirs = CUTTER_EXTRA_PLUGIN_DIRS;\n    for (auto &path : extra_plugin_dirs.split(listSeparator, CUTTER_QT_SKIP_EMPTY_PARTS)) {\n        result.push_back(QDir(path));\n    }\n\n    return result;\n}\n\nQString PluginManager::getUserPluginsDirectory() const\n{\n    QString location = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n    if (location.isEmpty()) {\n        return QString();\n    }\n    QDir pluginsDir(location);\n    pluginsDir.mkpath(\"plugins\");\n    if (!pluginsDir.cd(\"plugins\")) {\n        return QString();\n    }\n    return pluginsDir.absolutePath();\n}\n\nvoid PluginManager::loadNativePlugins(const QDir &directory)\n{\n    for (const QString &fileName : directory.entryList(QDir::Files)) {\n        if (!QLibrary::isLibrary(fileName)) {\n            // Reduce amount of warnings, by not attempting files which are obviously not plugins\n            continue;\n        }\n        QPluginLoader pluginLoader(directory.absoluteFilePath(fileName));\n        QObject *plugin = pluginLoader.instance();\n        if (!plugin) {\n            auto errorString = pluginLoader.errorString();\n            if (!errorString.isEmpty()) {\n                qWarning() << \"Load Error for plugin\" << fileName << \":\" << errorString;\n            }\n            continue;\n        }\n        PluginPtr cutterPlugin { qobject_cast<CutterPlugin *>(plugin) };\n        if (!cutterPlugin) {\n            continue;\n        }\n        cutterPlugin->setupPlugin();\n        plugins.push_back(std::move(cutterPlugin));\n    }\n}\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n\nvoid PluginManager::loadPythonPlugins(const QDir &directory)\n{\n    Python()->addPythonPath(directory.absolutePath().toLocal8Bit().data());\n\n    for (const QString &fileName :\n         directory.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {\n        if (fileName == \"__pycache__\") {\n            continue;\n        }\n        QString moduleName;\n        if (fileName.endsWith(\".py\")) {\n            moduleName = fileName.chopped(3);\n        } else {\n            moduleName = fileName;\n        }\n        PluginPtr cutterPlugin { loadPythonPlugin(moduleName.toLocal8Bit().constData()) };\n        if (!cutterPlugin) {\n            continue;\n        }\n        cutterPlugin->setupPlugin();\n        plugins.push_back(std::move(cutterPlugin));\n    }\n\n    PythonManager::ThreadHolder threadHolder;\n}\n\nCutterPlugin *PluginManager::loadPythonPlugin(const char *moduleName)\n{\n    PythonManager::ThreadHolder threadHolder;\n\n    PyObject *pluginModule = PyImport_ImportModule(moduleName);\n    if (!pluginModule) {\n        qWarning() << \"Couldn't load module for plugin:\" << QString(moduleName);\n        PyErr_Print();\n        return nullptr;\n    }\n\n    PyObject *createPluginFunc = PyObject_GetAttrString(pluginModule, \"create_cutter_plugin\");\n    if (!createPluginFunc || !PyCallable_Check(createPluginFunc)) {\n        qWarning() << \"Plugin module does not contain create_cutter_plugin() function:\"\n                   << QString(moduleName);\n        if (createPluginFunc) {\n            Py_DECREF(createPluginFunc);\n        }\n        Py_DECREF(pluginModule);\n        return nullptr;\n    }\n\n    PyObject *pluginObject = PyObject_CallFunction(createPluginFunc, nullptr);\n    Py_DECREF(createPluginFunc);\n    Py_DECREF(pluginModule);\n    if (!pluginObject) {\n        qWarning() << \"Plugin's create_cutter_plugin() function failed.\";\n        PyErr_Print();\n        return nullptr;\n    }\n\n    PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppPointerConvertible(\n#    if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)\n            reinterpret_cast<SbkObjectType *>(SbkCutterBindingsTypes[SBK_CUTTERPLUGIN_IDX]),\n#    else\n            reinterpret_cast<PyTypeObject **>(SbkCutterBindingsTypeStructs)[SBK_CUTTERPLUGIN_IDX],\n#    endif\n            pluginObject);\n    if (!pythonToCpp) {\n        qWarning() << \"Plugin's create_cutter_plugin() function did not return an instance of \"\n                      \"CutterPlugin:\"\n                   << QString(moduleName);\n        return nullptr;\n    }\n    CutterPlugin *plugin;\n    pythonToCpp(pluginObject, &plugin);\n    if (!plugin) {\n        qWarning() << \"Error during the setup of CutterPlugin:\" << QString(moduleName);\n        return nullptr;\n    }\n    return plugin;\n}\n#endif\n"
  },
  {
    "path": "src/plugins/PluginManager.h",
    "content": "\n#ifndef PLUGINMANAGER_H\n#define PLUGINMANAGER_H\n\n#include <QObject>\n#include <QDir>\n#include <memory>\n#include <vector>\n\n#include \"plugins/CutterPlugin.h\"\n\nclass PluginManager : public QObject\n{\n    Q_OBJECT\n\npublic:\n    static PluginManager *getInstance();\n\n    class PluginTerminator\n    {\n    public:\n        void operator()(CutterPlugin *) const;\n    };\n    using PluginPtr = std::unique_ptr<CutterPlugin, PluginTerminator>;\n\n    PluginManager();\n    ~PluginManager();\n\n    /**\n     * @brief Load all plugins, should be called once on application start\n     * @param enablePlugins set to false if plugin code shouldn't be started\n     */\n    void loadPlugins(bool enablePlugins = true);\n\n    /**\n     * @brief Destroy all loaded plugins, should be called once on application shutdown\n     */\n    void destroyPlugins();\n\n    const std::vector<PluginPtr> &getPlugins() { return plugins; }\n\n    QVector<QDir> getPluginDirectories() const;\n    QString getUserPluginsDirectory() const;\n\nprivate:\n    std::vector<PluginPtr> plugins;\n\n    void loadNativePlugins(const QDir &directory);\n    void loadPluginsFromDir(const QDir &pluginsDir, bool writable = false);\n\n#ifdef CUTTER_ENABLE_PYTHON_BINDINGS\n    void loadPythonPlugins(const QDir &directory);\n    CutterPlugin *loadPythonPlugin(const char *moduleName);\n#endif\n};\n\n#define Plugins() (PluginManager::getInstance())\n\n#endif // PLUGINMANAGER_H\n"
  },
  {
    "path": "src/plugins/sample-cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.12)\nproject(cutter-sample-plugin)\n\nset(CMAKE_CXX_STANDARD 20)\n\nfind_package(Cutter REQUIRED)\nfind_package(Rizin REQUIRED)\nset(CUTTER_INSTALL_PLUGDIR \"${Cutter_USER_PLUGINDIR}\" CACHE STRING \"Directory to install Cutter plugin into\")\n\nset(CMAKE_AUTOMOC ON)\n\nadd_library(sample_plugin MODULE\n    CutterSamplePlugin.h\n    CutterSamplePlugin.cpp)\ntarget_link_libraries(sample_plugin PRIVATE Cutter::Cutter Rizin::Core)\ninstall(TARGETS sample_plugin DESTINATION \"${CUTTER_INSTALL_PLUGDIR}\")\n"
  },
  {
    "path": "src/plugins/sample-cpp/CutterSamplePlugin.cpp",
    "content": "#include <QLabel>\n#include <QHBoxLayout>\n#include <QPushButton>\n#include <QAction>\n\n#include \"CutterSamplePlugin.h\"\n\n#include <common/TempConfig.h>\n#include <common/Configuration.h>\n#include <MainWindow.h>\n#include <rz_core.h>\n\nvoid CutterSamplePlugin::setupPlugin() {}\n\nvoid CutterSamplePlugin::setupInterface(MainWindow *main)\n{\n    CutterSamplePluginWidget *widget = new CutterSamplePluginWidget(main);\n    main->addPluginDockWidget(widget);\n}\n\nCutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main) : CutterDockWidget(main)\n{\n    this->setObjectName(\"CutterSamplePluginWidget\");\n    this->setWindowTitle(\"Sample C++ Plugin\");\n    QWidget *content = new QWidget();\n    this->setWidget(content);\n\n    QVBoxLayout *layout = new QVBoxLayout(content);\n    content->setLayout(layout);\n    text = new QLabel(content);\n    text->setFont(Config()->getFont());\n    text->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);\n    layout->addWidget(text);\n\n    QPushButton *button = new QPushButton(content);\n    button->setText(\"Want a fortune?\");\n    button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);\n    button->setMaximumHeight(50);\n    button->setMaximumWidth(200);\n    layout->addWidget(button);\n    layout->setAlignment(button, Qt::AlignHCenter);\n\n    connect(Core(), &CutterCore::seekChanged, this, &CutterSamplePluginWidget::on_seekChanged);\n    connect(button, &QPushButton::clicked, this, &CutterSamplePluginWidget::on_buttonClicked);\n}\n\nvoid CutterSamplePluginWidget::on_seekChanged(RVA addr)\n{\n    Q_UNUSED(addr);\n    RzCoreLocked core(Core());\n    TempConfig tempConfig;\n    tempConfig.set(\"scr.color\", 0);\n    QString disasm = Core()->disassembleSingleInstruction(Core()->getOffset());\n    QString res = fromOwnedCharPtr(rz_core_clippy(core, disasm.toUtf8().constData()));\n    text->setText(res);\n}\n\nvoid CutterSamplePluginWidget::on_buttonClicked()\n{\n    RzCoreLocked core(Core());\n    auto fortune = fromOwned(rz_core_fortune_get_random(core));\n    if (!fortune) {\n        return;\n    }\n    QString res = fromOwnedCharPtr(rz_core_clippy(core, fortune.get()));\n    text->setText(res);\n}\n"
  },
  {
    "path": "src/plugins/sample-cpp/CutterSamplePlugin.h",
    "content": "#ifndef CUTTERSAMPLEPLUGIN_H\n#define CUTTERSAMPLEPLUGIN_H\n\n#include <CutterPlugin.h>\n\n#include <QLabel>\n\nclass CutterSamplePlugin : public QObject, CutterPlugin\n{\n    Q_OBJECT\n    Q_PLUGIN_METADATA(IID \"re.rizin.cutter.plugins.CutterPlugin\")\n    Q_INTERFACES(CutterPlugin)\n\npublic:\n    void setupPlugin() override;\n    void setupInterface(MainWindow *main) override;\n\n    QString getName() const override { return \"SamplePlugin\"; }\n    QString getAuthor() const override { return \"xarkes\"; }\n    QString getDescription() const override { return \"Just a sample plugin.\"; }\n    QString getVersion() const override { return \"1.0\"; }\n};\n\nclass CutterSamplePluginWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit CutterSamplePluginWidget(MainWindow *main);\n\nprivate:\n    QLabel *text;\n\nprivate slots:\n    void on_seekChanged(RVA addr);\n    void on_buttonClicked();\n};\n\n#endif // CUTTERSAMPLEPLUGIN_H\n"
  },
  {
    "path": "src/plugins/sample-python/sample_python.py",
    "content": "\nimport cutter\n\nfrom PySide6.QtCore import Qt\nfrom PySide6.QtWidgets import QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton\n\n\nclass FortuneWidget(cutter.CutterDockWidget):\n    def __init__(self, parent):\n        super(FortuneWidget, self).__init__(parent)\n        self.setObjectName(\"FancyDockWidgetFromCoolPlugin\")\n        self.setWindowTitle(\"Sample Python Plugin\")\n\n        content = QWidget()\n        self.setWidget(content)\n\n        # Create layout and label\n        layout = QVBoxLayout(content)\n        content.setLayout(layout)\n        self.text = QLabel(content)\n        self.text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)\n        layout.addWidget(self.text)\n\n        button = QPushButton(content)\n        button.setText(\"Want a fortune?\")\n        button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)\n        button.setMaximumHeight(50)\n        button.setMaximumWidth(200)\n        layout.addWidget(button)\n        layout.setAlignment(button, Qt.AlignHCenter)\n\n        button.clicked.connect(self.generate_fortune)\n        cutter.core().seekChanged.connect(self.generate_fortune)\n\n        self.show()\n\n    def generate_fortune(self):\n        fortune = cutter.cmd(\"fortune\").replace(\"\\n\", \"\")\n        res = cutter.core().cmdRaw(f\"?E {fortune}\")\n        self.text.setText(res)\n\n\nclass CutterSamplePlugin(cutter.CutterPlugin):\n    name = \"Sample Plugin\"\n    description = \"A sample plugin written in python.\"\n    version = \"1.2\"\n    author = \"Cutter developers\"\n\n    # Override CutterPlugin methods\n\n    def __init__(self):\n        super(CutterSamplePlugin, self).__init__()\n        self.disassembly_actions = []\n        self.addressable_item_actions = []\n        self.disas_action = None\n        self.addr_submenu = None\n        self.main = None\n\n    def setupPlugin(self):\n        pass\n\n    def setupInterface(self, main):\n        # Dock widget\n        widget = FortuneWidget(main)\n        main.addPluginDockWidget(widget)\n\n        # Dissassembly context menu\n        menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly)\n        self.disas_action = menu.addAction(\"CutterSamplePlugin dissassembly action\")\n        self.disas_action.triggered.connect(self.handle_disassembler_action)\n        self.main = main\n\n        # Context menu for tables with addressable items like Flags,Functions,Strings,Search results,...\n        addressable_item_menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable)\n        self.addr_submenu = addressable_item_menu.addMenu(\"CutterSamplePlugin\") # create submenu\n        adrr_action = self.addr_submenu.addAction(\"Action 1\")\n        self.addr_submenu.addSeparator() # can use separator and other qt functionality\n        adrr_action2 = self.addr_submenu.addAction(\"Action 2\")\n        adrr_action.triggered.connect(self.handle_addressable_item_action)\n        adrr_action2.triggered.connect(self.handle_addressable_item_action)\n\n    def terminate(self): # optional\n        print(\"CutterSamplePlugin shutting down\")\n        if self.main:\n            menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly)\n            menu.removeAction(self.disas_action)\n            addressable_item_menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable)\n            submenu_action = self.addr_submenu.menuAction()\n            addressable_item_menu.removeAction(submenu_action)\n        print(\"CutterSamplePlugin finished clean up\")\n\n    # Plugin methods\n\n    def handle_addressable_item_action(self):\n        # for actions in plugin menu Cutter sets data to current item address\n        submenu_action = self.addr_submenu.menuAction()\n        cutter.message(\"Context menu action callback 0x{:x}\".format(submenu_action.data()))\n\n    def handle_disassembler_action(self):\n        # for actions in plugin menu Cutter sets data to address for current dissasembly line\n        cutter.message(\"Dissasembly menu action callback 0x{:x}\".format(self.disas_action.data()))\n\n\n# This function will be called by Cutter and should return an instance of the plugin.\ndef create_cutter_plugin():\n    return CutterSamplePlugin()\n"
  },
  {
    "path": "src/plugins/sample-python-qt5/sample_python.py",
    "content": "\nimport cutter\n\nfrom PySide2.QtCore import Qt\nfrom PySide2.QtWidgets import QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton\n\n\nclass FortuneWidget(cutter.CutterDockWidget):\n    def __init__(self, parent):\n        super(FortuneWidget, self).__init__(parent)\n        self.setObjectName(\"FancyDockWidgetFromCoolPlugin\")\n        self.setWindowTitle(\"Sample Python Plugin\")\n\n        content = QWidget()\n        self.setWidget(content)\n\n        # Create layout and label\n        layout = QVBoxLayout(content)\n        content.setLayout(layout)\n        self.text = QLabel(content)\n        self.text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)\n        self.text.setFont(cutter.Configuration.instance().getFont())\n        layout.addWidget(self.text)\n\n        button = QPushButton(content)\n        button.setText(\"Want a fortune?\")\n        button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)\n        button.setMaximumHeight(50)\n        button.setMaximumWidth(200)\n        layout.addWidget(button)\n        layout.setAlignment(button, Qt.AlignHCenter)\n\n        button.clicked.connect(self.generate_fortune)\n        cutter.core().seekChanged.connect(self.generate_fortune)\n\n        self.show()\n\n    def generate_fortune(self):\n        fortune = cutter.cmd(\"fortune\").replace(\"\\n\", \"\")\n        res = cutter.core().cmdRaw(f\"?E {fortune}\")\n        self.text.setText(res)\n\n\nclass CutterSamplePlugin(cutter.CutterPlugin):\n    name = \"Sample Plugin\"\n    description = \"A sample plugin written in python.\"\n    version = \"1.2\"\n    author = \"Cutter developers\"\n\n    # Override CutterPlugin methods\n\n    def __init__(self):\n        super(CutterSamplePlugin, self).__init__()\n        self.disassembly_actions = []\n        self.addressable_item_actions = []\n        self.disas_action = None\n        self.addr_submenu = None\n        self.main = None\n\n    def setupPlugin(self):\n        pass\n\n    def setupInterface(self, main):\n        # Dock widget\n        widget = FortuneWidget(main)\n        main.addPluginDockWidget(widget)\n\n        # Dissassembly context menu\n        menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly)\n        self.disas_action = menu.addAction(\"CutterSamplePlugin dissassembly action\")\n        self.disas_action.triggered.connect(self.handle_disassembler_action)\n        self.main = main\n\n        # Context menu for tables with addressable items like Flags,Functions,Strings,Search results,...\n        addressable_item_menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable)\n        self.addr_submenu = addressable_item_menu.addMenu(\"CutterSamplePlugin\") # create submenu\n        adrr_action = self.addr_submenu.addAction(\"Action 1\")\n        self.addr_submenu.addSeparator() # can use separator and other qt functionality\n        adrr_action2 = self.addr_submenu.addAction(\"Action 2\")\n        adrr_action.triggered.connect(self.handle_addressable_item_action)\n        adrr_action2.triggered.connect(self.handle_addressable_item_action)\n\n    def terminate(self): # optional\n        print(\"CutterSamplePlugin shutting down\")\n        if self.main:\n            menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly)\n            menu.removeAction(self.disas_action)\n            addressable_item_menu = self.main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable)\n            submenu_action = self.addr_submenu.menuAction()\n            addressable_item_menu.removeAction(submenu_action)\n        print(\"CutterSamplePlugin finished clean up\")\n\n    # Plugin methods\n\n    def handle_addressable_item_action(self):\n        # for actions in plugin menu Cutter sets data to current item address\n        submenu_action = self.addr_submenu.menuAction()\n        cutter.message(\"Context menu action callback 0x{:x}\".format(submenu_action.data()))\n\n    def handle_disassembler_action(self):\n        # for actions in plugin menu Cutter sets data to address for current dissasembly line\n        cutter.message(\"Dissasembly menu action callback 0x{:x}\".format(self.disas_action.data()))\n\n\n# This function will be called by Cutter and should return an instance of the plugin.\ndef create_cutter_plugin():\n    return CutterSamplePlugin()\n"
  },
  {
    "path": "src/python/cutter.py",
    "content": "import json\nfrom _cutter import *\n\ntry:\n    from CutterBindings import *\n\n    def core():\n        return CutterCore.instance()\nexcept ImportError:\n    pass\n\n\ndef cmdj(command):\n    \"\"\"Execute a JSON command and return the result as a dictionary\"\"\"\n    return json.loads(cmd(command))\n\n\n"
  },
  {
    "path": "src/python/reg_qtres_importer.py",
    "content": "import sys\nimport importlib\nimport _qtres\n\nclass QtResLoader:\n    @classmethod\n    def get_code(cls, name):\n        return _qtres.get_code(name)\n\n    @classmethod\n    def create_module(cls, spec):\n        return None\n\n    @classmethod\n    def exec_module(cls, module):\n        code = cls.get_code(module.__name__)\n        if code is None:\n            raise ImportError(\"get_code() failed\")\n        exec(code, module.__dict__)\n\nclass QtResFinder:\n    @classmethod\n    def find_spec(cls, fullname, path=None, target=None):\n        if path or target:\n            return None\n        if not _qtres.exists(fullname):\n            return None\n        return importlib._bootstrap.ModuleSpec(fullname, QtResLoader)\n\nsys.meta_path.append(QtResFinder)\n"
  },
  {
    "path": "src/re.rizin.cutter.appdata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop\">\n  <id>re.rizin.cutter</id>\n  <metadata_license>CC0-1.0</metadata_license>\n  <project_license>GPL-3.0</project_license>\n  <name>Cutter</name>\n  <summary>Free and Open Source Reverse Engineering Platform powered by Rizin and Qt</summary>\n  <developer id=\"com.github.rizinorg\">\n    <name>Rizin Organization</name>\n  </developer>\n\n  <description>\n    <p>\n      Cutter is a free and open-source GUI for Rizin. Willing to create an advanced, customizable and free reverse engineering framework.\n    </p>\n  </description>\n\n  <launchable type=\"desktop-id\">re.rizin.cutter.desktop</launchable>\n\n  <screenshots>\n    <screenshot type=\"default\">\n      <image>https://raw.githubusercontent.com/rizinorg/cutter/stable/docs/source/images/screenshot.png</image>\n      <caption>Main UI</caption>\n    </screenshot>\n  </screenshots>\n\n  <url type=\"homepage\">https://cutter.re/</url>\n  <url type=\"bugtracker\">https://github.com/rizinorg/cutter/issues</url>\n  <url type=\"contact\">https://cutter.re/#Join-our-communities</url>\n  <url type=\"contribute\">https://cutter.re/docs/contributing.html</url>\n  <url type=\"help\">https://cutter.re/docs</url>\n  <url type=\"translate\">https://cutter.re/docs/contributing/translations/getting-started.html</url>\n  <url type=\"vcs-browser\">https://github.com/rizinorg/cutter</url>\n\n  <releases>\n    <release version=\"2.4.0\" date=\"2024-04-28\" />\n    <release version=\"2.3.4\" date=\"2024-03-04\" />\n    <release version=\"2.3.3\" date=\"2024-02-24\" />\n    <release version=\"2.3.2\" date=\"2023-09-14\" />\n    <release version=\"2.3.1\" date=\"2023-08-20\" />\n    <release version=\"2.3.0\" date=\"2023-08-05\" />\n    <release version=\"2.2.1\" date=\"2023-05-15\" />\n    <release version=\"2.2.0\" date=\"2023-02-22\" />\n    <release version=\"2.1.2\" date=\"2022-09-11\" />\n    <release version=\"2.1.1\" date=\"2022-09-10\" />\n    <release version=\"2.1.0\" date=\"2022-06-15\" />\n    <release version=\"2.0.5\" date=\"2021-12-26\" />\n    <release version=\"2.0.4\" date=\"2021-11-18\" />\n    <release version=\"2.0.3\" date=\"2021-09-25\" />\n    <release version=\"2.0.2\" date=\"2021-04-25\" />\n    <release version=\"2.0.1\" date=\"2021-04-10\" />\n    <release version=\"2.0.0\" date=\"2021-03-28\" />\n    <release version=\"1.12.0\" date=\"2020-09-03\" />\n    <release version=\"1.11.0\" date=\"2020-07-24\" />\n    <release version=\"1.10.3\" date=\"2020-05-08\" />\n    <release version=\"1.10.2\" date=\"2020-03-10\" />\n    <release version=\"1.10.1\" date=\"2020-01-31\" />\n    <release version=\"1.10.0\" date=\"2019-12-20\" />\n    <release version=\"1.9.0\" date=\"2019-09-06\" />\n    <release version=\"1.8.3\" date=\"2019-07-01\" />\n    <release version=\"1.8.2\" date=\"2019-05-20\" />\n    <release version=\"1.8.1\" date=\"2019-04-14\" />\n    <release version=\"1.8.0\" date=\"2019-03-18\" />\n    <release version=\"1.7.4\" date=\"2019-01-21\" />\n    <release version=\"1.7.3\" date=\"2018-12-26\" />\n    <release version=\"1.7.2\" date=\"2018-10-07\" />\n    <release version=\"1.7.1\" date=\"2018-08-25\" />\n    <release version=\"1.7\" date=\"2018-08-17\" />\n    <release version=\"1.6\" date=\"2018-07-13\" />\n    <release version=\"1.5\" date=\"2018-07-02\" />\n    <release version=\"1.4\" date=\"2018-04-24\" />\n    <release version=\"1.3\" date=\"2018-03-09\" />\n    <release version=\"1.2\" date=\"2018-01-30\" />\n    <release version=\"1.1\" date=\"2017-12-25\" />\n    <release version=\"1.0\" date=\"2017-12-03\" />\n  </releases>\n</component>\n"
  },
  {
    "path": "src/re.rizin.cutter.desktop",
    "content": "[Desktop Entry]\nType=Application\nName=Cutter\nGenericName=Reverse Engineering Platform\nComment=Reverse Engineering Platform powered by rizin\nExec=cutter\nIcon=cutter\nCategories=Development;\n"
  },
  {
    "path": "src/resources.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/\">\n        <file>img/icons/dark/arrow_left.svg</file>\n        <file>img/icons/dark/arrow_right.svg</file>\n        <file>img/icons/arrow_left.svg</file>\n        <file>img/icons/arrow_right.svg</file>\n        <file>img/icons/light/arrow_right.svg</file>\n        <file>img/icons/light/arrow_left.svg</file>\n        <file>img/icons/sidebar.svg</file>\n        <file>img/icons/undo.svg</file>\n        <file>img/icons/redo.svg</file>\n        <file>img/icons/equalizer.svg</file>\n        <file>img/icons/cog.svg</file>\n        <file alias=\"lock\">img/icons/lock.svg</file>\n        <file alias=\"unlock\">img/icons/unlock.svg</file>\n        <file>img/icons/tabs.svg</file>\n        <file>img/icons/themes.svg</file>\n        <file>img/icons/target.svg</file>\n        <file>img/icons/help.svg</file>\n        <file>img/icons/spin.svg</file>\n        <file>img/icons/plus.svg</file>\n        <file>img/icons/play.svg</file>\n        <file>img/icons/play_light_debug.svg</file>\n        <file>img/icons/play_light_emul.svg</file>\n        <file>img/icons/play_light_attach.svg</file>\n        <file>img/icons/play_light_remote.svg</file>\n        <file>img/icons/media-stop_light.svg</file>\n        <file>img/icons/media-suspend_light.svg</file>\n        <file>img/icons/media-skip-forward_light.svg</file>\n        <file>img/icons/continue_until_main.svg</file>\n        <file>img/icons/light/continue_until_main.svg</file>\n        <file>img/icons/continue_until_call.svg</file>\n        <file>img/icons/light/continue_until_call.svg</file>\n        <file>img/icons/continue_until_syscall.svg</file>\n        <file>img/icons/light/continue_until_syscall.svg</file>\n        <file>img/icons/reverse_continue.svg</file>\n        <file>img/icons/detach_debugger.svg</file>\n        <file>img/icons/light/step_into.svg</file>\n        <file>img/icons/step_into.svg</file>\n        <file>img/icons/light/step_over.svg</file>\n        <file>img/icons/step_over.svg</file>\n        <file>img/icons/step_out.svg</file>\n        <file>img/icons/light/step_out.svg</file>\n        <file>img/icons/reverse_step.svg</file>\n        <file>img/icons/start_trace.svg</file>\n        <file>img/icons/stop_trace.svg</file>\n        <file>img/icons/cloud.svg</file>\n        <file>img/icons/down.svg</file>\n        <file>img/icons/down_white.svg</file>\n        <file>img/icons/up.svg</file>\n        <file>img/icons/up_white.svg</file>\n        <file>img/icons/clear_light.svg</file>\n        <file>img/icons/load_light.svg</file>\n        <file>img/icons/save_light.svg</file>\n        <file>img/icons/trash_light.svg</file>\n        <file>img/icons/disas.svg</file>\n        <file>img/icons/light/disas.svg</file>\n        <file>img/icons/disas_white.svg</file>\n        <file>img/icons/graph_white.svg</file>\n        <file>img/icons/graph.svg</file>\n        <file>img/icons/light/graph.svg</file>\n        <file>img/icons/initialization.svg</file>\n        <file>img/icons/light/initialization.svg</file>\n        <file>img/icons/hexdump_light.svg</file>\n        <file>img/icons/hexdump_white.svg</file>\n        <file>img/icons/bug.svg</file>\n        <file>img/icons/light/bug.svg</file>\n        <file>img/icons/cog_light.svg</file>\n        <file>img/icons/light/cog_light.svg</file>\n        <file>img/icons/cog_white.svg</file>\n        <file>img/icons/new_light.svg</file>\n        <file>img/icons/run_light.svg</file>\n        <file>img/icons/edit_light.svg</file>\n        <file>img/icons/polar.svg</file>\n        <file>img/icons/light/polar.svg</file>\n        <file>img/icons/radar_light.svg</file>\n        <file>img/icons/polar_white.svg</file>\n        <file>img/icons/radar_white.svg</file>\n        <file>img/icons/cloud_white.svg</file>\n        <file>img/icons/spin_white.svg</file>\n        <file>img/icons/tabs_white.svg</file>\n        <file>img/icons/themes_white.svg</file>\n        <file>img/icons/unlock_white.svg</file>\n        <file>img/icons/lock_white.svg</file>\n        <file>img/icons/left_light.svg</file>\n        <file>img/icons/right_light.svg</file>\n        <file>img/icons/eye.svg</file>\n        <file>img/icons/eye_white.svg</file>\n        <file>img/icons/transfer.svg</file>\n        <file>img/icons/transfer_white.svg</file>\n        <file>img/icons/spin_light.svg</file>\n        <file>img/icons/function_light.svg</file>\n        <file>img/icons/function_import_light.svg</file>\n        <file>img/icons/function_entry_light.svg</file>\n        <file>img/icons/function_export_light.svg</file>\n        <file>img/icons/function_tlscb_light.svg</file>\n        <file>img/icons/function_main_light.svg</file>\n        <file>img/icons/function_dark.svg</file>\n        <file>img/icons/function_import_dark.svg</file>\n        <file>img/icons/function_entry_dark.svg</file>\n        <file>img/icons/function_export_dark.svg</file>\n        <file>img/icons/function_tlscb_dark.svg</file>\n        <file>img/icons/function_main_dark.svg</file>\n        <file>img/icons/home.svg</file>\n        <file>fonts/Anonymous Pro.ttf</file>\n        <file>fonts/Inconsolata-Regular.ttf</file>\n        <file>img/cutter_plain.svg</file>\n        <file>img/cutter_white_plain.svg</file>\n        <file>img/cutter.svg</file>\n        <file>img/cutter_macos_simple.svg</file>\n        <file>img/icons/copy.svg</file>\n        <file>img/icons/delete.svg</file>\n        <file>img/icons/previous.svg</file>\n        <file>img/icons/list.svg</file>\n        <file>img/icons/fork.svg</file>\n        <file>img/icons/plugins.svg</file>\n        <file>img/icons/light/plugins.svg</file>\n        <file>python/cutter.py</file>\n        <file>python/reg_qtres_importer.py</file>\n        <file>img/icons/download_black.svg</file>\n        <file>img/icons/pencil_thin.svg</file>\n        <file>img/icons/save_black.svg</file>\n        <file>img/icons/trash_bin.svg</file>\n        <file>img/icons/upload_black.svg</file>\n        <file>img/icons/rename.svg</file>\n        <file>img/icons/reset.svg</file>\n        <file>img/icons/down_light.svg</file>\n        <file>img/icons/delete_light.svg</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "src/shortcuts/DefaultShortcuts.cpp",
    "content": "#include \"DefaultShortcuts.h\"\n\nconst QHash<QString, Shortcut> &getDefaultShortcuts()\n{\n    static const QHash<QString, Shortcut> defaultShortcuts = {\n\n        // General\n        { \"General.seek\",\n          { { Qt::Key_S }, QT_TRANSLATE_NOOP(\"MainWindow\", \"Seek\"), \"MainWindow\" } },\n        { \"General.seekToFunctionEnd\",\n          { { Qt::Key_Dollar },\n            QT_TRANSLATE_NOOP(\"MainWindow\", \"Seek to Function End\"),\n            \"MainWindow\" } },\n        { \"General.seekToFunctionStart\",\n          { { Qt::Key_AsciiCircum },\n            QT_TRANSLATE_NOOP(\"MainWindow\", \"Seek to Function Start\"),\n            \"MainWindow\" } },\n        { \"General.refreshContents\",\n          { { Qt::CTRL | Qt::Key_R },\n            QT_TRANSLATE_NOOP(\"MainWindow\", \"Refresh Contents\"),\n            \"MainWindow\" } },\n        { \"General.back\",\n          { QKeySequence::keyBindings(QKeySequence::Back),\n            QT_TRANSLATE_NOOP(\"MainWindow\", \"Undo Seek\"), \"MainWindow\" } },\n        { \"General.forward\",\n          { QKeySequence::keyBindings(QKeySequence::Forward),\n            QT_TRANSLATE_NOOP(\"MainWindow\", \"Redo Seek\"), \"MainWindow\" } },\n        { \"General.showFilter\",\n          { QKeySequence::keyBindings(QKeySequence::Find),\n            QT_TRANSLATE_NOOP(\"MainWindow\", \"Show Filter\"), \"MainWindow\" } },\n        { \"General.clearFilter\",\n          { { Qt::Key_Escape }, QT_TRANSLATE_NOOP(\"MainWindow\", \"Clear Filter\"), \"MainWindow\" } },\n        { \"General.zoomIn\",\n          { { Qt::CTRL | Qt::Key_Plus, Qt::CTRL | Qt::SHIFT | Qt::Key_Plus },\n            QT_TRANSLATE_NOOP(\"CutterGraphView\", \"Zoom In\"),\n            \"CutterGraphView\" } },\n        { \"General.zoomOut\",\n          { { Qt::CTRL | Qt::Key_Minus },\n            QT_TRANSLATE_NOOP(\"CutterGraphView\", \"Zoom Out\"),\n            \"CutterGraphView\" } },\n        { \"General.zoomReset\",\n          { { Qt::CTRL | Qt::Key_Equal },\n            QT_TRANSLATE_NOOP(\"CutterGraphView\", \"Reset Zoom\"),\n            \"CutterGraphView\" } },\n        { \"General.addComment\",\n          { { Qt::Key_Semicolon },\n            QT_TRANSLATE_NOOP(\"AddressableItemContextMenu\", \"Add comment\"),\n            \"AddressableItemContextMenu\" } },\n        { \"General.showXRefs\",\n          { { Qt::Key_X },\n            QT_TRANSLATE_NOOP(\"AddressableItemContextMenu\", \"Show X-Refs\"),\n            \"AddressableItemContextMenu\" } },\n        { \"General.copyAddress\",\n          { { Qt::CTRL | Qt::SHIFT | Qt::Key_C },\n            QT_TRANSLATE_NOOP(\"AddressableItemContextMenu\", \"Copy address\"),\n            \"AddressableItemContextMenu\" } },\n        { \"General.seekPrev\",\n          { { Qt::Key_Escape },\n            QT_TRANSLATE_NOOP(\"DecompilerWidget\", \"Seek to Previous Address\"),\n            \"DecompilerWidget\" } },\n\n        // Breakpoint\n        { \"Breakpoint.delBreakpoint\",\n          { { Qt::Key_Delete },\n            QT_TRANSLATE_NOOP(\"BreakpointWidget\", \"Delete breakpoint\"),\n            \"BreakpointWidget\" } },\n        { \"Breakpoint.toggleBreakpoint\",\n          { { Qt::Key_Space },\n            QT_TRANSLATE_NOOP(\"BreakpointWidget\", \"Toggle breakpoint\"),\n            \"BreakpointWidget\" } },\n\n        // Console\n        { \"Console.focusConsole\",\n          { { Qt::Key_Period },\n            QT_TRANSLATE_NOOP(\"MainWindow\", \"Focus Console Input\"),\n            \"MainWindow\" } },\n        { \"Console.toggle\",\n          { { Qt::CTRL | Qt::Key_QuoteLeft, Qt::Key_Colon },\n            QT_TRANSLATE_NOOP(\"ConsoleWidget\", \"Toggle Console Window\"),\n            \"ConsoleWidget\" } },\n        { \"Console.clear\",\n          { { Qt::CTRL | Qt::Key_L },\n            QT_TRANSLATE_NOOP(\"ConsoleWidget\", \"Clear Output\"),\n            \"ConsoleWidget\" } },\n        { \"Console.clearRzInputLineEdit\",\n          { { Qt::Key_Escape },\n            QT_TRANSLATE_NOOP(\"ConsoleWidget\", \"Clear Input\"),\n            \"ConsoleWidget\" } },\n        { \"Console.clearDebugee\",\n          { { Qt::Key_Escape },\n            QT_TRANSLATE_NOOP(\"ConsoleWidget\", \"Clear Debugee\"),\n            \"ConsoleWidget\" } },\n        { \"Console.historyUp\",\n          { { Qt::Key_Up },\n            QT_TRANSLATE_NOOP(\"ConsoleWidget\", \"Previous Command\"),\n            \"ConsoleWidget\" } },\n        { \"Console.historyDown\",\n          { { Qt::Key_Down },\n            QT_TRANSLATE_NOOP(\"ConsoleWidget\", \"Next Command\"),\n            \"ConsoleWidget\" } },\n        { \"Console.complete\",\n          { { Qt::Key_Tab },\n            QT_TRANSLATE_NOOP(\"ConsoleWidget\", \"Auto-Complete\"),\n            \"ConsoleWidget\" } },\n\n        // Debug\n        { \"Debug.start\",\n          { { Qt::Key_F9 }, QT_TRANSLATE_NOOP(\"DebugActions\", \"Start debug\"), \"DebugActions\" } },\n        { \"Debug.continue\",\n          { { Qt::Key_F5 }, QT_TRANSLATE_NOOP(\"DebugActions\", \"Continue\"), \"DebugActions\" } },\n        { \"Debug.continueBack\",\n          { { Qt::CTRL | Qt::Key_F5 },\n            QT_TRANSLATE_NOOP(\"DebugActions\", \"Continue backwards\"),\n            \"DebugActions\" } },\n        { \"Debug.step\",\n          { { Qt::Key_F7 }, QT_TRANSLATE_NOOP(\"DebugActions\", \"Step\"), \"DebugActions\" } },\n        { \"Debug.stepOver\",\n          { { Qt::Key_F8 }, QT_TRANSLATE_NOOP(\"DebugActions\", \"Step over\"), \"DebugActions\" } },\n        { \"Debug.stepOut\",\n          { { Qt::CTRL | Qt::Key_F8 },\n            QT_TRANSLATE_NOOP(\"DebugActions\", \"Step out\"),\n            \"DebugActions\" } },\n        { \"Debug.stepBack\",\n          { { Qt::CTRL | Qt::Key_F7 },\n            QT_TRANSLATE_NOOP(\"DebugActions\", \"Step backwards\"),\n            \"DebugActions\" } },\n        { \"Debug.accept\",\n          { { QKeySequence(Qt::CTRL | Qt::Key_Return) },\n            QT_TRANSLATE_NOOP(\"NativeDebugDialog\", \"Accept Dialog\"),\n            \"NativeDebugDialog\" } },\n        { \"Debug.toggleBreakpoint\",\n          { { Qt::Key_F2, Qt::CTRL | Qt::Key_B },\n            QT_TRANSLATE_NOOP(\"DecompilerContextMenu\", \"Add/remove breakpoint\"),\n            \"DecompilerContextMenu\" } },\n        { \"Debug.advancedBreakpoint\",\n          { { Qt::CTRL | Qt::Key_F2 },\n            QT_TRANSLATE_NOOP(\"DecompilerContextMenu\", \"Advanced breakpoint\"),\n            \"DecompilerContextMenu\" } },\n\n        // Decompiler\n        { \"Decompiler.copy\",\n          { QKeySequence::keyBindings(QKeySequence::Copy),\n            QT_TRANSLATE_NOOP(\"DecompilerContextMenu\", \"Copy\"), \"DecompilerContextMenu\" } },\n        { \"Decompiler.copyReferenceAddress\",\n          { { Qt::KeyboardModifier::ControlModifier | Qt::KeyboardModifier::ShiftModifier\n              | Qt::Key_C },\n            QT_TRANSLATE_NOOP(\"DecompilerContextMenu\", \"Copy address of [flag] (<address>)\"),\n            \"DecompilerContextMenu\" } },\n        { \"Decompiler.renameThingHere\",\n          { { Qt::Key_N },\n            QT_TRANSLATE_NOOP(\"DecompilerContextMenu\", \"Rename function at cursor\"),\n            \"DecompilerContextMenu\" } },\n        { \"Decompiler.editFunctionVariables\",\n          { { Qt::Key_Y },\n            QT_TRANSLATE_NOOP(\"DecompilerContextMenu\", \"Edit variable <name of variable>\"),\n            \"DecompilerContextMenu\" } },\n\n        // Disassembly\n        { \"Disassembly.copy\",\n          { QKeySequence::keyBindings(QKeySequence::Copy),\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Copy\"), \"DisassemblyContextMenu\" } },\n        { \"Disassembly.copyInstructionBytes\",\n          { { Qt::CTRL | Qt::ALT | Qt::Key_C },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Copy instruction bytes\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.retypeLocals\",\n          { { Qt::Key_Y },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Re-type Local Variables\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.editFunction\",\n          { { Qt::SHIFT | Qt::Key_P },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Edit function\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.undefineFunction\",\n          { { Qt::Key_U },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Undefine function\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.defineFunction\",\n          { { Qt::Key_P },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Define function here\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.XRefsForVariables\",\n          { { QKeySequence(Qt::SHIFT | Qt::Key_X) },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"X-Refs for local variables\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.rename\",\n          { { Qt::Key_N },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Rename or add flag\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.globalVariable\",\n          { { Qt::Key_G },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Modify or add global variable\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.setToCode\",\n          { { Qt::Key_C },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Code\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.setAsString\",\n          { { Qt::Key_A },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Auto-detect\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.setAsStringAdvanced\",\n          { { Qt::SHIFT | Qt::Key_A },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Advanced\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.setToDataEx\",\n          { { Qt::Key_Asterisk },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Set To Data Advanced\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.setToData\",\n          { { Qt::Key_D },\n            QT_TRANSLATE_NOOP(\"DisassemblyContextMenu\", \"Switch Data\"),\n            \"DisassemblyContextMenu\" } },\n        { \"Disassembly.switchToGraph\",\n          { { Qt::Key_Space },\n            QT_TRANSLATE_NOOP(\"DisassemblyWidget\", \"Switch to Graph\"),\n            \"DisassemblyWidget\" } },\n        { \"Disassembly.moveDown\",\n          { QList<QKeySequence> { Qt::Key_J }\n                    + QKeySequence::keyBindings(QKeySequence::MoveToNextLine),\n            QT_TRANSLATE_NOOP(\"DisassemblyWidget\", \"Move Cursor Down\"), \"DisassemblyWidget\" } },\n        { \"Disassembly.moveUp\",\n          { QList<QKeySequence> { Qt::Key_K }\n                    + QKeySequence::keyBindings(QKeySequence::MoveToPreviousLine),\n            QT_TRANSLATE_NOOP(\"DisassemblyWidget\", \"Move Cursor Up\"), \"DisassemblyWidget\" } },\n        { \"Disassembly.pageDown\",\n          { QKeySequence::keyBindings(QKeySequence::MoveToNextPage),\n            QT_TRANSLATE_NOOP(\"DisassemblyWidget\", \"Move Cursor Down By Page\"),\n            \"DisassemblyWidget\" } },\n        { \"Disassembly.pageUp\",\n          { QKeySequence::keyBindings(QKeySequence::MoveToPreviousPage),\n            QT_TRANSLATE_NOOP(\"DisassemblyWidget\", \"Move Cursor Up By Page\"),\n            \"DisassemblyWidget\" } },\n\n        // Exports\n        { \"Exports.toggle\",\n          { { Qt::SHIFT | Qt::Key_E },\n            QT_TRANSLATE_NOOP(\"ExportsWidget\", \"Toggle Exports Window\"),\n            \"ExportsWidget\" } },\n\n        // Functions\n        { \"Functions.rename\",\n          { { Qt::Key_N }, QT_TRANSLATE_NOOP(\"FunctionsWidget\", \"Rename\"), \"FunctionsWidget\" } },\n\n        // Graph\n        { \"Graph.takeTrue\",\n          { { Qt::Key_T },\n            QT_TRANSLATE_NOOP(\"DisassemblerGraphView\", \"Take True Branch\"),\n            \"DisassemblerGraphView\" } },\n        { \"Graph.takeFalse\",\n          { { Qt::Key_F },\n            QT_TRANSLATE_NOOP(\"DisassemblerGraphView\", \"Take False Branch\"),\n            \"DisassemblerGraphView\" } },\n        { \"Graph.nextInstr\",\n          { { Qt::Key_J },\n            QT_TRANSLATE_NOOP(\"DisassemblerGraphView\", \"Next Instruction\"),\n            \"DisassemblerGraphView\" } },\n        { \"Graph.prevInstr\",\n          { { Qt::Key_K },\n            QT_TRANSLATE_NOOP(\"DisassemblerGraphView\", \"Previous Instruction\"),\n            \"DisassemblerGraphView\" } },\n        { \"Graph.toggle\",\n          { { Qt::SHIFT | Qt::Key_G },\n            QT_TRANSLATE_NOOP(\"GraphWidget\", \"Toggle Graph Window\"),\n            \"GraphWidget\" } },\n        { \"Graph.switchToDisassembly\",\n          { { Qt::Key_Space },\n            QT_TRANSLATE_NOOP(\"GraphWidget\", \"Switch to Disassembly View\"),\n            \"GraphWidget\" } },\n\n        // Hex\n        { \"Hex.copy\",\n          { QKeySequence::keyBindings(QKeySequence::Copy), QT_TRANSLATE_NOOP(\"HexWidget\", \"Copy\"),\n            \"HexWidget\" } },\n        { \"Hex.addFlag\",\n          { { Qt::Key_N }, QT_TRANSLATE_NOOP(\"HexWidget\", \"Add flag at address\"), \"HexWidget\" } },\n        { \"Hex.addMark\",\n          { { Qt::Key_M }, QT_TRANSLATE_NOOP(\"HexWidget\", \"Add mark\"), \"HexWidget\" } },\n\n        // Imports\n        { \"Imports.toggle\",\n          { { Qt::SHIFT | Qt::Key_I },\n            QT_TRANSLATE_NOOP(\"ImportsWidget\", \"Toggle Imports Window\"),\n            \"ImportsWidget\" } },\n\n        // Omnibar\n        { \"Omnibar.clear\",\n          { { QKeySequence(Qt::Key_Escape) },\n            QT_TRANSLATE_NOOP(\"Omnibar\", \"Clear Omnibar\"),\n            \"Omnibar\" } },\n\n        // Graph Overview\n        { \"Overview.zoomIn\",\n          { { Qt::Key_Plus }, QT_TRANSLATE_NOOP(\"OverviewWidget\", \"Zoom In\"), \"OverviewWidget\" } },\n        { \"Overview.zoomOut\",\n          { { Qt::Key_Minus },\n            QT_TRANSLATE_NOOP(\"OverviewWidget\", \"Zoom Out\"),\n            \"OverviewWidget\" } },\n\n        // Strings\n        { \"Strings.toggle\",\n          { { Qt::SHIFT | Qt::Key_F12 },\n            QT_TRANSLATE_NOOP(\"StringsWidget\", \"Toggle Strings Window\"),\n            \"StringsWidget\" } },\n\n        // Search\n        { \"Search.toggle\",\n          { { QKeySequence::keyBindings(QKeySequence::Find) },\n            QT_TRANSLATE_NOOP(\"CutterSearchable\", \"Toggle Search Bar\"),\n            \"CutterSearchable\" } },\n        { \"Search.findNext\",\n          { { Qt::Key_Return },\n            QT_TRANSLATE_NOOP(\"SearchBarWidget\", \"Find Next Match\"),\n            \"SearchBarWidget\" } },\n        { \"Search.findPrev\",\n          { { Qt::SHIFT | Qt::Key_Return },\n            QT_TRANSLATE_NOOP(\"SearchBarWidget\", \"Find Previous Match\"),\n            \"SearchBarWidget\" } },\n        { \"Search.findLast\",\n          { { Qt::CTRL | Qt::Key_Return },\n            QT_TRANSLATE_NOOP(\"SearchBarWidget\", \"Find Last Match\"),\n            \"SearchBarWidget\" } },\n        { \"Search.hide\",\n          { { Qt::Key_Escape },\n            QT_TRANSLATE_NOOP(\"SearchBarWidget\", \"Hide Search Bar\"),\n            \"SearchBarWidget\" } },\n        { \"Search.options\",\n          { { Qt::CTRL | Qt::Key_O },\n            QT_TRANSLATE_NOOP(\"SearchBarWidget\", \"Show Search Bar Options Menu\"),\n            \"SearchBarWidget\" } },\n\n    };\n    return defaultShortcuts;\n}\n"
  },
  {
    "path": "src/shortcuts/DefaultShortcuts.h",
    "content": "#ifndef DEFAULTSHORTCUTS_H\n#define DEFAULTSHORTCUTS_H\n\n#include <QString>\n#include <QKeySequence>\n#include <QHash>\n#include <QPair>\n\nstruct Shortcut\n{\n    QList<QKeySequence> keySequences;\n    const char *text; // untranslated description\n    const char *context;\n};\n\nconst QHash<QString, Shortcut> &getDefaultShortcuts();\n\n#endif // DEFAULTSHORTCUTS_H\n"
  },
  {
    "path": "src/shortcuts/ShortcutManager.cpp",
    "content": "#include \"ShortcutManager.h\"\n#include <QCoreApplication>\n#include <QDebug>\n\nQ_GLOBAL_STATIC(ShortcutManager, uniqueInstance)\n\nShortcutManager *ShortcutManager::getInstance()\n{\n    return uniqueInstance;\n}\n\nShortcutManager::ShortcutManager() {}\n\nQList<QKeySequence> ShortcutManager::getKeySequences(const QString &id)\n{\n    const auto &defaultShortcuts = getDefaultShortcuts();\n\n    if (!defaultShortcuts.contains(id)) {\n        qWarning() << \"Can't find shortcut for\" << id;\n        return {};\n    }\n\n    QList<QKeySequence> ksq = getCustomKeySequences(id);\n    if (ksq.isEmpty()) { // No custom keySequence set, return default\n        ksq = defaultShortcuts.value(id).keySequences;\n    }\n    return ksq;\n}\n\nQKeySequence ShortcutManager::getKeySequence(const QString &id)\n{\n    const QList<QKeySequence> sequences = getKeySequences(id);\n    return sequences.isEmpty() ? QKeySequence() : sequences.first();\n}\n\nShortcut ShortcutManager::getShortcut(const QString &id)\n{\n    const auto &defaultShortcuts = getDefaultShortcuts();\n    if (!defaultShortcuts.contains(id)) {\n        qWarning() << \"Can't find shortcut for\" << id;\n        return {};\n    }\n\n    Shortcut result = defaultShortcuts.value(id);\n\n    QList<QKeySequence> customKeySequences = getKeySequences(id);\n    if (!customKeySequences.isEmpty()) {\n        result.keySequences = customKeySequences;\n    }\n    return result;\n}\n\nQHash<QString, Shortcut> ShortcutManager::getAllShortcuts()\n{\n    const auto &defaultShortcuts = getDefaultShortcuts();\n    QHash<QString, Shortcut> shortcuts;\n    shortcuts.reserve(defaultShortcuts.size());\n\n    for (auto it = defaultShortcuts.cbegin(); it != defaultShortcuts.cend(); ++it) {\n        const QString name = it.key();\n        Shortcut s = getShortcut(name);\n        shortcuts.insert(name, s);\n    }\n    return shortcuts;\n}\n\nQAction *ShortcutManager::makeAction(const QString &id, QObject *parent)\n{\n\n    QAction *action = new QAction(parent);\n    setupAction(*action, id);\n    return action;\n}\n\nvoid ShortcutManager::setupAction(QAction &action, const QString &id)\n{\n    Shortcut s = getShortcut(id);\n    action.setShortcuts(s.keySequences);\n    action.setText(QCoreApplication::translate(s.context, s.text));\n}\n\nQShortcut *ShortcutManager::makeQShortcut(const QString &id, QWidget *parent)\n{\n    QShortcut *shortcut = new QShortcut(parent);\n    QList<QKeySequence> keySequences = getKeySequences(id);\n    if (!keySequences.isEmpty()) {\n#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)\n        shortcut->setKeys(keySequences);\n#else\n        shortcut->setKey(keySequences.first());\n#endif\n    }\n    return shortcut;\n}\n\nbool ShortcutManager::matchesKeySequence(const QString &id, const QKeySequence &keySeq)\n{\n    const auto sequences = getKeySequences(id);\n    for (const QKeySequence &seq : sequences) {\n        if (seq == keySeq) {\n            return true;\n        }\n    }\n    return false;\n}\n\nQList<QKeySequence> ShortcutManager::getCustomKeySequences(const QString &id)\n{\n    return {}; // Custom shortcut support is not implemented yet\n}\n"
  },
  {
    "path": "src/shortcuts/ShortcutManager.h",
    "content": "#ifndef SHORTCUTMANAGER_H\n#define SHORTCUTMANAGER_H\n\n#include <QObject>\n#include <QWidget>\n#include <QKeySequence>\n#include <QAction>\n#include <QShortcut>\n#include \"DefaultShortcuts.h\"\n\nclass ShortcutManager : public QObject\n{\n    Q_OBJECT\n\npublic:\n    static ShortcutManager *getInstance();\n\n    Shortcut getShortcut(const QString &id);\n    QKeySequence getKeySequence(const QString &id);\n    QList<QKeySequence> getKeySequences(const QString &id);\n    QHash<QString, Shortcut> getAllShortcuts();\n\n    QAction *makeAction(const QString &id, QObject *parent);\n    void setupAction(QAction &action, const QString &id);\n\n    QShortcut *makeQShortcut(const QString &id, QWidget *parent);\n\n    /**\n     * @brief Returns whether the given key sequence matches any key sequence assigned to the\n     * specified shortcut ID.\n     */\n    bool matchesKeySequence(const QString &id, const QKeySequence &keySeq);\n\n    /**\n     * @brief placeholder for getting custom shortcuts set by the user.\n     */\n    QList<QKeySequence> getCustomKeySequences(const QString &id);\n\n    ShortcutManager();\n\nprivate:\n    static ShortcutManager *instance;\n};\n\n#define Shortcuts() (ShortcutManager::getInstance())\n\n#endif // SHORTCUTMANAGER_H\n"
  },
  {
    "path": "src/themes/lightstyle/light.qrc",
    "content": "<RCC>\n  <qresource prefix=\"qss_icons\">\n    <file>rc/down_arrow.png</file>\n    <file>rc/down_arrow_disabled.png</file>\n    <file>rc/up_arrow.png</file>\n    <file>rc/up_arrow_disabled.png</file>\n    <file>rc/right_arrow.png</file>\n    <file>rc/right_arrow_disabled.png</file>\n    <file>rc/left_arrow.png</file>\n    <file>rc/left_arrow_disabled.png</file>\n    <file>rc/down_arrow_combo.png</file>\n    <file>rc/up_arrow_combo.png</file>\n  </qresource>\n  <qresource prefix=\"lightstyle\">\n      <file>light.qss</file>\n  </qresource>\n</RCC>\n"
  },
  {
    "path": "src/themes/lightstyle/light.qss",
    "content": "QColorDialog\n{\n  background-color:#fff;\n}\n\nQTextEdit\n{\n  background-color:#fafcfe;\n  color:#00162e;\n}\n\nQWidget\n{\n  color:#00162e;\n  background-color:#fafcfe;\n  selection-background-color:#03a9f4;\n  selection-color:#ffffff;\n  background-clip:border;\n  border-image:none;\n  border:0 transparent #000;\n  outline:0;\n}\n\nQDockWidget\n{\n  background:#fafcfe;\n  border:1px solid #e0e0e0;\n}\n\nQDockWidget::title\n{\n  background-color:#fafcfe;\n  border-top:1px solid #dee8f3;\n  text-align:center;\n}\n\nQPlainTextEdit\n{\n  selection-background-color:#03a9f4;\n  selection-color:#ffffff;\n  color:#00162e;\n  border-color:transparent;\n  border-style:solid;\n  border-width:1px;\n}\n\nQPushButton\n{\n  color:#00162e;\n  background-color:#fff;\n  border-color:#039be5;\n  border-style:solid;\n  border-width:1px;\n  padding:2px;\n}\n\nQPushButton::default\n{\n  color:#00162e;\n  background-color:#fff;\n  border-color:transparent transparent #039be5;\n  border-width:1px;\n  padding:2px;\n}\n\nQToolButton\n{\n  border-top-color:transparent;\n  border-right-color:transparent;\n  border-left-color:transparent;\n  color:#00162e;\n  background-color:transparent;\n  border-style:solid;\n  padding:2px;\n}\n\nQToolButton[popupMode=\"1\"] { /* only for MenuButtonPopup */\n    padding-right: 20px; /* make way for the popup button */\n}\n\nQToolButton:hover\n{\n  border-bottom-width:2px;\n  color:#00162e;\n  padding-bottom:1px;\n  background-color:transparent;\n  border-color:transparent transparent #03a9f4;\n  border-style:solid;\n}\n\nQPushButton:hover\n{\n  border-bottom-width:1px;\n  color:#00162e;\n  padding-bottom:2px;\n  background-color:#fafafa;\n  border:1px solid #039be5;\n  border-color:transparent transparent #03a9f4;\n  border-style:solid;\n}\n\nQPushButton:pressed\n{\n  border-bottom-width:2px;\n  color:#03a9f4;\n  padding-bottom:1px;\n  background-color:#fff;\n  border-color:transparent transparent #03a9f4;\n  border-style:solid;\n}\n\nQPushButton:disabled\n{\n  border-bottom-width:2px;\n  color:#808086;\n  padding-bottom:1px;\n  background-color:#fff;\n  border-color:#e2e3e4;\n  border-style:solid;\n}\n\nQLineEdit\n{\n  border-radius:4px;\n  color:#00162e;\n  background:#fff;\n  selection-background-color:#007ac1;\n  selection-color:#FFF;\n  border-color:#bdbdbd;\n  border-style:inset;\n  border-width:1px;\n  padding:0 8px;\n}\n\nQLineEdit:disabled\n{\n  color: #919191;\n  background-color:#f1f1f1;\n}\n\nQLabel\n{\n  color:#00162e;\n}\n\nQLCDNumber\n{\n  color:#03a9f4;\n}\n\nQProgressBar\n{\n  text-align:center;\n  color:#f0f0f0;\n  border-radius:10px;\n  background-color:#fff;\n  border-color:#bdbdbd;\n  border-style:inset;\n  border-width:1px;\n}\n\nQProgressBar::chunk\n{\n  background-color:#039be5;\n  border-radius:5px;\n}\n\nQToolBar\n{\n  background-color:#03a9f4;\n}\n\nQMenuBar\n{\n  background-color:#007ac1;\n  height:40px;\n  padding-top: 0;\n  padding-bottom: 0;\n}\n\nQMenuBar::item\n{\n  background:transparent;\n  color:#fff;\n  height:40px;\n  max-width:40px;\n  text-align:center;\n  padding-left:10px;\n  padding-right:10px;\n}\n\nQMenuBar::item:selected\n{\n  background:transparent;\n  border-bottom:2px solid #fff;\n  background-color:#1386c9;\n}\n\nQMenuBar::item:pressed\n{\n  border:1px solid #03a9f4;\n  background-color:#03a9f4;\n  color:#fff;\n  margin-bottom:-1px;\n  padding-bottom:1px;\n}\n\nQMenuBar::item:disabled\n{\n  color: #a6a6a6;\n}\n\nQMenu\n{\n  background-color:#fafcfe;\n  border:1px solid #cfcfcf;\n  padding:2px 2px 2px 6px;\n}\n\nQMenu::item\n{\n  background-color:#fafcfe;\n  border-bottom-width:1px;\n  color:#00162e;\n  border-color:transparent;\n  border-style:solid;\n  padding:4px 30px;\n}\n\nQMenu::item:selected\n{\n  border-left-color:#039be5;\n  border-left-width:2px;\n  color:#039be5;\n  background-color:#f4f9ff;\n}\n\nQMenu::item:disabled\n{\n  color: #a6a6a6;\n}\n\nQTabWidget\n{\n  color:#000;\n  background-color:#fff;\n}\n\nQTabWidget::pane\n{\n  background-color:#fff;\n  border-radius:6px;\n  border-color:#bdbdbd;\n  border-style:solid;\n  border-width:1px;\n}\n\nQTabBar::tab\n{\n  border-bottom-width:1px;\n  color:#007ac1;\n  margin-left:3px;\n  background-color:#fff;\n  border-color:transparent;\n  border-style:solid;\n  padding:3px;\n}\n\nQTabBar::tab:selected,QTabBar::tab:last:selected,QTabBar::tab:hover\n{\n  border-bottom-width:2px;\n  color:#007ac1;\n  padding-left:3px;\n  padding-bottom:2px;\n  margin-left:3px;\n  background-color:#fff;\n  border-color:transparent transparent #039be5;\n  border-style:solid;\n}\n\nQCheckBox\n{\n  color:#00162e;\n  padding:2px;\n}\n\nQCheckBox:disabled\n{\n  color:#7e7e80;\n  padding:2px;\n}\n\nQCheckBox:hover\n{\n  border-radius:4px;\n  background-color:#fff;\n  border-color:#bdbdbd;\n  border-style:solid;\n  border-width:1px;\n  padding:1px;\n}\n\nQCheckBox::indicator:checked\n{\n  height:10px;\n  width:10px;\n  color:#00162e;\n  background-color:#039be5;\n  border-color:#039be5;\n  border-style:solid;\n  border-width:1px;\n}\n\nQCheckBox::indicator:unchecked\n{\n  height:10px;\n  width:10px;\n  color:#00162e;\n  background-color:transparent;\n  border-color:#039be5;\n  border-style:solid;\n  border-width:1px;\n}\n\nQCheckBox::indicator:checked:disabled\n{\n  background-color:#d6d6d6;\n}\n\nQRadioButton\n{\n  color:#00162e;\n  background-color:#fff;\n  padding:1px;\n}\n\nQRadioButton::indicator:checked\n{\n  height:10px;\n  width:10px;\n  border-radius:5px;\n  color:#00162e;\n  background-color:#039be5;\n  border-color:#039be5;\n  border-style:solid;\n  border-width:1px;\n}\n\nQRadioButton::indicator:!checked\n{\n  height:10px;\n  width:10px;\n}\n\nQStatusBar\n{\n  color:#027f7f;\n}\n\nQComboBox\n{\n  selection-background-color:#3daee9;\n  border:1px solid #76797C;\n  border-radius:2px;\n  min-width:75px;\n  color:#00162e;\n  background-color:#fff;\n  border-style:solid;\n  padding:3px;\n}\n\nQComboBox:hover\n{\n  border:1px solid #3daee9;\n  color:#00162e;\n}\n\nQComboBox:on\n{\n  padding-top:3px;\n  padding-left:4px;\n  selection-background-color:#4a4a4a;\n}\n\nQComboBox QAbstractItemView\n{\n  background-color:#efefef;\n  border-radius:2px;\n  border:1px solid #76797C;\n  selection-background-color:#18465d;\n}\n\nQComboBox::drop-down\n{\n  subcontrol-origin:padding;\n  subcontrol-position:top right;\n  width:15px;\n  border-left-width:0;\n  border-left-color:#e0e0e0;\n  border-left-style:solid;\n  border-top-right-radius:3px;\n  border-bottom-right-radius:3px;\n}\n\nQAbstractSpinBox::down-arrow,QAbstractSpinBox::down-arrow:disabled,QAbstractSpinBox::down-arrow:off\n{\n  image:url(:/qss_icons/rc/down_arrow.png);\n  width:10px;\n  height:10px;\n}\n\nQToolButton::menu-button {\n    border: solid transparent;\n    /* 16px width + 4px for border = 20px allocated above */\n    width: 16px;\n}\n\nQToolButton::menu-indicator\n{\n  image:url(:/qss_icons/rc/down_arrow.png);\n  top:-2px;\n  left:-2px;\n}\n\nQAbstractSpinBox::up-arrow,QSpinBox::up-arrow,QAbstractSpinBox::up-arrow:disabled,QAbstractSpinBox::up-arrow:off\n{\n  image:url(:/qss_icons/rc/up_arrow_disabled.png);\n  width:10px;\n  height:10px;\n}\n\nQSlider::groove:horizontal\n{\n  height:5px;\n  background:#039be5;\n}\n\nQSlider::groove:vertical\n{\n  width:5px;\n  background:#039be5;\n}\n\nQSlider::handle:horizontal\n{\n  border:1px solid #5c5c5c;\n  width:14px;\n  border-radius:7px;\n  background-color:#039be5;\n  margin:-5px 0;\n}\n\nQSlider::handle:vertical\n{\n  border:1px solid #5c5c5c;\n  height:14px;\n  border-radius:7px;\n  background-color:#039be5;\n  margin:0 -5px;\n}\n\nQTreeView,QListView\n{\n  border:1px solid #76797C;\n  background-color:#fff;\n  color:#00162e;\n}\n\nQListView::item:!selected:hover,QTreeView::item:!selected:hover\n{\n  background:#BBDEFB;\n  outline:0;\n  color:#163f6b;\n}\n\nQListView::item:selected:hover,QTreeView::item:selected:hover\n{\n  background:#2196F3;\n  color:#fff;\n}\n\nQTableView\n{\n  border:1px solid #76797C;\n  gridline-color:#bdbdbd;\n  background-color:#fff;\n  color:#00162e;\n}\n\nQTableView,QHeaderView\n{\n  border-radius:0;\n  background-color:#fff;\n  color:#007ac1;\n}\n\nQTableCornerButton::section\n{\n  background-color:#bdbdbd;\n  border:1px transparent #cdcdce;\n  border-radius:0;\n  color:#007ac1;\n}\n\nQHeaderView::section\n{\n  background-color:#fafcfe;\n  border-radius:0;\n  text-align:center;\n  padding:3px 3px 3px 10px;\n}\n\nQScrollBar:horizontal\n{\n  height:15px;\n  border:1px transparent #9e9e9e;\n  border-radius:4px;\n  background-color:#efefef;\n  margin:3px 15px;\n}\n\nQScrollBar::handle:horizontal\n{\n  background-color:#9e9e9e;\n  min-width:5px;\n  border-radius:4px;\n}\n\nQScrollBar::add-line:horizontal\n{\n  border-image:url(:/qss_icons/rc/right_arrow_disabled.png);\n  width:10px;\n  height:10px;\n  subcontrol-position:right;\n  subcontrol-origin:margin;\n  margin:0 3px;\n}\n\nQScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on\n{\n  border-image:url(:/qss_icons/rc/right_arrow.png);\n  height:10px;\n  width:10px;\n  subcontrol-position:right;\n  subcontrol-origin:margin;\n}\n\nQScrollBar::sub-line:horizontal\n{\n  border-image:url(:/qss_icons/rc/left_arrow_disabled.png);\n  height:10px;\n  width:10px;\n  subcontrol-position:left;\n  subcontrol-origin:margin;\n  margin:0 3px;\n}\n\nQScrollBar::sub-line:horizontal:hover,QScrollBar::sub-line:horizontal:on\n{\n  border-image:url(:/qss_icons/rc/left_arrow.png);\n  height:10px;\n  width:10px;\n  subcontrol-position:left;\n  subcontrol-origin:margin;\n}\n\nQScrollBar:vertical\n{\n  background-color:#efefef;\n  width:15px;\n  border:1px transparent #efefef;\n  border-radius:4px;\n  margin:15px 3px;\n}\n\nQScrollBar::handle:vertical\n{\n  background-color:#9e9e9e;\n  min-height:5px;\n  border-radius:4px;\n}\n\nQScrollBar::sub-line:vertical\n{\n  border-image:url(:/qss_icons/rc/up_arrow_disabled.png);\n  height:10px;\n  width:10px;\n  subcontrol-position:top;\n  subcontrol-origin:margin;\n  margin:3px 0;\n}\n\nQScrollBar::add-line:vertical\n{\n  border-image:url(:/qss_icons/rc/down_arrow_disabled.png);\n  height:10px;\n  width:10px;\n  subcontrol-position:bottom;\n  subcontrol-origin:margin;\n  margin:3px 0;\n}\n\nQScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on\n{\n  border-image:url(:/qss_icons/rc/up_arrow.png);\n  height:10px;\n  width:10px;\n  subcontrol-position:top;\n  subcontrol-origin:margin;\n}\n\nQScrollBar::add-line:vertical:hover,QScrollBar::add-line:vertical:on\n{\n  border-image:url(:/qss_icons/rc/down_arrow.png);\n  height:10px;\n  width:10px;\n  subcontrol-position:bottom;\n  subcontrol-origin:margin;\n}\n\nQLineEdit:hover,QLineEdit:focus\n{\n  border:1px solid #26adfc;\n  color: #007ac1;\n}\n\nQToolTip\n{\n  border-radius:1px;\n  opacity:200;\n  border-style:solid;\n  border-color: white;\n  border-width:2px;\n  padding:5px;\n  background-color: #212121;\n}\n\nQFrame\n{\n  border-radius:2px;\n  border:1px solid #c7dce9;\n}\n\nQFrame[frameShape=\"0\"]\n{\n  border-radius:2px;\n  border:1px transparent #c7dce9;\n}\n\nQFrame[height=\"3\"],QFrame[width=\"3\"]\n{\n  background-color:#c7dce9;\n}\n\nQMainWindow > QTabBar::tab\n{\n  border-bottom-color:#007ac1;\n  border-top-left-radius:0;\n  border-top-right-radius:0;\n  min-width:8px;\n  max-width:200px;\n  margin-bottom:3px;\n  margin-top:3px;\n  background-color:#fafcfe;\n  padding:5px;\n}\n\nQMenu::indicator\n{\n  border:1px solid #009be2;\n  border-radius:2px;\n  margin:3px 3px 3px 4px;\n}\n\nQHeaderView::section::horizontal\n{\n  background-color:#fafcfe;\n  color:#0046af;\n}\n\nQMainWindow,QDialog\n{\n  background-color:#fafcfe;\n}\n\nQSpinBox,QDoubleSpinBox,QTimeEdit,QDateTimeEdit,QDateEdit,QToolBox,QToolBox::tab\n{\n  color:#00162e;\n  background-color:#fff;\n}\n\nQToolBox::tab:selected,QScrollArea\n{\n  color:#FFF;\n  background-color:#fff;\n}\n\nQSlider::add-page:horizontal,QSlider::add-page:vertical\n{\n  background:#FFF;\n}\n\nQSlider::sub-page:horizontal,QSlider::sub-page:vertical\n{\n  background:#039be5;\n}\n\nQTableView::item:pressed,QListView::item:pressed,QTreeView::item:pressed,QTableView::item:selected:active,QTreeView::item:selected:active,QListView::item:selected:active\n{\n  background: #bbe0ff;\n  color:#163f6b;\n}\n\nQScrollBar::up-arrow:horizontal,QScrollBar::down-arrow:horizontal,QScrollBar::add-page:horizontal,QScrollBar::sub-page:horizontal,QScrollBar::up-arrow:vertical,QScrollBar::down-arrow:vertical,QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical\n{\n  background:none;\n}\n\nQMainWindow > QTabBar::tab:selected,QMainWindow > QTabBar::tab:hover\n{\n  background:#03a9f4;\n  color:#FFF;\n}\n\nQMenu::indicator:non-exclusive:unchecked,QMenu::indicator:non-exclusive:unchecked:selected,QMenu::indicator:exclusive:unchecked\n{\n  background:#ebeef1;\n}\n\nQMenu::indicator:non-exclusive:checked,QMenu::indicator:non-exclusive:checked:selected,QMenu::indicator:exclusive:unchecked:selected,QMenu::indicator:exclusive:checked,QMenu::indicator:exclusive:checked:selected\n{\n  background:#03a9f4;\n}\n\nQComboBox:hover,QPushButton:hover,QAbstractSpinBox:hover,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QAbstractView:hover,QTreeView:hover\n{\n  border:1px solid #3daee9;\n}\n\nQTreeView\n{\n  border:1px solid #b7dcf0;\n  selection-background-color:#bbe0ff;\n  selection-color:#163f6b;\n}\n\nQAbstractSpinBox::down-arrow:hover,QToolButton::menu-arrow,QHeaderView::down-arrow,QSpinBox::down-arrow:hover,QHeaderView::down-arrow\n{\n  image:url(:/qss_icons/rc/down_arrow.png);\n}\n\nQToolButton::menu-arrow:open {\n    top: 1px; left: 1px; /* shift it a bit */\n}\n\nQHeaderView::up-arrow,QAbstractSpinBox::up-arrow:hover,QHeaderView::up-arrow\n{\n  image:url(:/qss_icons/rc/up_arrow.png);\n}\n\nQComboBox::down-arrow,QComboBox::down-arrow:on,QComboBox::down-arrow:hover,QComboBox::down-arrow:focus {\n image:url(:/qss_icons/rc/down_arrow_combo.png);\n  width: 14px;\n  height: 14px;\n}\n\nQSpinBox::down-arrow,QSpinBox::down-arrow:on,QSpinBox::down-arrow:hover,QSpinBox::down-arrow:focus {\n  image:url(:/qss_icons/rc/down_arrow_combo.png);\n  border-width: 0px;\n }\n \n QSpinBox::up-arrow,QSpinBox::up-arrow:on,QSpinBox::up-arrow:hover,QSpinBox::up-arrow:focus {\n  image:url(:/qss_icons/rc/up_arrow_combo.png);\n  border-width: 0px;\n }\n\nQLineEdit, QLineEdit[text=\"\"] {\n  color: #00304d;\n}\n\nQMenu#historyMenu, QMenu#forwardHistoryMenu {\n    menu-scrollable: true;\n}\n"
  },
  {
    "path": "src/themes/midnight/midnight.qrc",
    "content": "<RCC>\n  <qresource prefix=\"qss_icons\">\n    <file>rc/up_arrow_disabled.png</file>\n    <file>rc/Hmovetoolbar.png</file>\n    <file>rc/stylesheet-branch-end.png</file>\n    <file>rc/branch_closed-on.png</file>\n    <file>rc/stylesheet-vline.png</file>\n    <file>rc/branch_closed.png</file>\n    <file>rc/branch_open-on.png</file>\n    <file>rc/transparent.png</file>\n    <file>rc/right_arrow_disabled.png</file>\n    <file>rc/sizegrip.png</file>\n    <file>rc/close.png</file>\n    <file>rc/close-hover.png</file>\n    <file>rc/close-pressed.png</file>\n    <file>rc/down_arrow.png</file>\n    <file>rc/Vmovetoolbar.png</file>\n    <file>rc/left_arrow.png</file>\n    <file>rc/stylesheet-branch-more.png</file>\n    <file>rc/up_arrow.png</file>\n    <file>rc/right_arrow.png</file>\n    <file>rc/left_arrow_disabled.png</file>\n    <file>rc/Hsepartoolbar.png</file>\n    <file>rc/branch_open.png</file>\n    <file>rc/Vsepartoolbar.png</file>\n    <file>rc/down_arrow_disabled.png</file>\n    <file>rc/undock.png</file>\n    <file>rc/checkbox_checked_disabled.png</file>\n    <file>rc/checkbox_checked_focus.png</file>\n    <file>rc/checkbox_checked.png</file>\n    <file>rc/checkbox_indeterminate.png</file>\n    <file>rc/checkbox_indeterminate_focus.png</file>\n    <file>rc/checkbox_unchecked_disabled.png</file>\n    <file>rc/checkbox_unchecked_focus.png</file>\n    <file>rc/checkbox_unchecked.png</file>\n    <file>rc/radio_checked_disabled.png</file>\n    <file>rc/radio_checked_focus.png</file>\n    <file>rc/radio_checked.png</file>\n    <file>rc/radio_unchecked_disabled.png</file>\n    <file>rc/radio_unchecked_focus.png</file>\n    <file>rc/radio_unchecked.png</file>\n  </qresource>\n  <qresource prefix=\"midnight\">\n      <file>style.css</file>\n  </qresource>\n</RCC>\n"
  },
  {
    "path": "src/themes/midnight/style.css",
    "content": "QToolTip {\n    border: 1px solid #2a2b2f;\n    background-color: #5A7566;\n    color: white;\n    padding: 0px;                /*remove padding, for fix combobox tooltip.*/\n    opacity: 200;\n}\n\nQWidget {\n    color: #eff0f1;\n    background-color: #1f2022;\n    selection-background-color: #36393c;\n    selection-color: #eff0f1;\n    background-clip: border;\n    border-image: none;\n    border: 0px transparent black;\n    outline: 0;\n}\n\n/*noinspection ALL*/\nQWidget:item:hover {\n    background-color: #4f5256;\n    color: #eff0f1;\n}\n\nQWidget:item:selected {\n    background-color: #4f5256;\n}\n\nQCheckBox {\n    spacing: 5px;\n    outline: none;\n    color: #eff0f1;\n    margin-bottom: 2px;\n}\n\nQCheckBox:disabled {\n    color: #2a2b2f;\n}\n\nQCheckBox::indicator,\nQGroupBox::indicator {\n    width: 18px;\n    height: 18px;\n}\n\nQGroupBox::indicator {\n    margin-left: 2px;\n}\n\nQCheckBox::indicator:unchecked {\n    image: url(:/qss_icons/rc/checkbox_unchecked.png);\n}\n\nQCheckBox::indicator:unchecked:hover,\nQCheckBox::indicator:unchecked:focus,\nQCheckBox::indicator:unchecked:pressed,\nQGroupBox::indicator:unchecked:hover,\nQGroupBox::indicator:unchecked:focus,\nQGroupBox::indicator:unchecked:pressed {\n    border: none;\n    image: url(:/qss_icons/rc/checkbox_unchecked_focus.png);\n}\n\nQCheckBox::indicator:checked {\n    image: url(:/qss_icons/rc/checkbox_checked.png);\n}\n\nQCheckBox::indicator:checked:hover,\nQCheckBox::indicator:checked:focus,\nQCheckBox::indicator:checked:pressed,\nQGroupBox::indicator:checked:hover,\nQGroupBox::indicator:checked:focus,\nQGroupBox::indicator:checked:pressed {\n    border: none;\n    image: url(:/qss_icons/rc/checkbox_checked_focus.png);\n}\n\nQCheckBox::indicator:indeterminate {\n    image: url(:/qss_icons/rc/checkbox_indeterminate.png);\n}\n\nQCheckBox::indicator:indeterminate:focus,\nQCheckBox::indicator:indeterminate:hover,\nQCheckBox::indicator:indeterminate:pressed {\n    image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png);\n}\n\nQCheckBox::indicator:checked:disabled,\nQGroupBox::indicator:checked:disabled {\n    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);\n}\n\nQCheckBox::indicator:unchecked:disabled,\nQGroupBox::indicator:unchecked:disabled {\n    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);\n}\n\nQRadioButton {\n    spacing: 5px;\n    outline: none;\n    color: #eff0f1;\n    margin-bottom: 2px;\n}\n\nQRadioButton:disabled {\n    color: #2a2b2f;\n}\n\nQRadioButton::indicator {\n    width: 21px;\n    height: 21px;\n}\n\nQRadioButton::indicator:unchecked {\n    image: url(:/qss_icons/rc/radio_unchecked.png);\n}\n\nQRadioButton::indicator:unchecked:hover,\nQRadioButton::indicator:unchecked:focus,\nQRadioButton::indicator:unchecked:pressed {\n    border: none;\n    outline: none;\n    image: url(:/qss_icons/rc/radio_unchecked_focus.png);\n}\n\nQRadioButton::indicator:checked {\n    border: none;\n    outline: none;\n    image: url(:/qss_icons/rc/radio_checked.png);\n}\n\nQRadioButton::indicator:checked:hover,\nQRadioButton::indicator:checked:focus,\nQRadioButton::indicator:checked:pressed {\n    border: none;\n    outline: none;\n    image: url(:/qss_icons/rc/radio_checked_focus.png);\n}\n\nQRadioButton::indicator:checked:disabled {\n    outline: none;\n    image: url(:/qss_icons/rc/radio_checked_disabled.png);\n}\n\nQRadioButton::indicator:unchecked:disabled {\n    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);\n}\n\nQMenuBar {\n    background-color: #1f2022;\n    color: #eff0f1;\n}\n\nQMenuBar::item {\n    background: transparent;\n}\n\nQMenuBar::item:selected {\n    background: transparent;\n    border: 1px solid #2a2b2f;\n}\n\nQMenuBar::item:pressed {\n    border: 1px solid #2a2b2f;\n    background-color: #36393c;\n    color: #eff0f1;\n    margin-bottom: -1px;\n    padding-bottom: 1px;\n}\n\nQMenu {\n    border: 1px solid #2a2b2f;\n    color: #eff0f1;\n    margin: 2px;\n}\n\nQMenu::icon {\n    margin: 5px;\n}\n\nQMenu::item {\n    padding: 5px 30px 5px 30px;\n    border: 1px solid transparent;\n    /* reserve space for selection border */\n}\n\nQMenu::item:selected {\n    color: #eff0f1;\n}\n\nQMenu::separator {\n    height: 2px;\n    background: lightblue;\n    margin-left: 10px;\n    margin-right: 5px;\n}\n\nQMenu::indicator {\n    width: 18px;\n    height: 18px;\n}\n\n\n/* non-exclusive indicator = check box style indicator\n   (see QActionGroup::setExclusive) */\n\nQMenu::indicator:non-exclusive:unchecked {\n    image: url(:/qss_icons/rc/checkbox_unchecked.png);\n}\n\nQMenu::indicator:non-exclusive:unchecked:selected {\n    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);\n}\n\nQMenu::indicator:non-exclusive:checked {\n    image: url(:/qss_icons/rc/checkbox_checked.png);\n}\n\nQMenu::indicator:non-exclusive:checked:selected {\n    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);\n}\n\n\n/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */\n\nQMenu::indicator:exclusive:unchecked {\n    image: url(:/qss_icons/rc/radio_unchecked.png);\n}\n\nQMenu::indicator:exclusive:unchecked:selected {\n    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);\n}\n\nQMenu::indicator:exclusive:checked {\n    image: url(:/qss_icons/rc/radio_checked.png);\n}\n\nQMenu::indicator:exclusive:checked:selected {\n    image: url(:/qss_icons/rc/radio_checked_disabled.png);\n}\n\nQMenu::right-arrow {\n    margin: 5px;\n    image: url(:/qss_icons/rc/right_arrow.png)\n}\n\nQWidget:disabled {\n    color: #454545;\n    background-color: #1f2022;\n}\n\nQAbstractItemView {\n    alternate-background-color: #1f2022;\n    color: #eff0f1;\n    border: 1px solid #2a2b2f;\n    border-radius: 2px;\n}\n\nQWidget:focus,\nQMenuBar:focus {\n    border: 1px solid #36393c;\n}\n\nQTabWidget:focus,\nQCheckBox:focus,\nQRadioButton:focus,\nQSlider:focus {\n    border: none;\n}\n\nQLineEdit {\n    background-color: #232629;\n    padding: 5px;\n    border-style: solid;\n    border: 1px solid #2a2b2f;\n    border-radius: 2px;\n    color: #eff0f1;\n}\n\nQAbstractItemView QLineEdit {\n    padding: 0;\n}\n\nQGroupBox {\n    border: 1px solid #2a2b2f;\n    border-radius: 2px;\n    margin-top: 20px;\n}\n\nQGroupBox::title {\n    subcontrol-origin: margin;\n    subcontrol-position: top center;\n    padding-left: 10px;\n    padding-right: 10px;\n    padding-top: 10px;\n}\n\nQAbstractScrollArea {\n    border-radius: 2px;\n    border: 1px solid #2a2b2f;\n    background-color: transparent;\n}\n\nQScrollBar:horizontal {\n    height: 15px;\n    margin: 3px 15px 3px 15px;\n    border: 1px transparent #2a2b2f;\n    border-radius: 4px;\n    background-color: #2a2b2f;\n}\n\nQScrollBar::handle:horizontal {\n    background-color: #605F5F;\n    min-width: 5px;\n    border-radius: 4px;\n}\n\nQScrollBar::add-line:horizontal {\n    margin: 0px 3px 0px 3px;\n    border-image: url(:/qss_icons/rc/right_arrow_disabled.png);\n    width: 10px;\n    height: 10px;\n    subcontrol-position: right;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::sub-line:horizontal {\n    margin: 0px 3px 0px 3px;\n    border-image: url(:/qss_icons/rc/left_arrow_disabled.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: left;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::add-line:horizontal:hover,\nQScrollBar::add-line:horizontal:on {\n    border-image: url(:/qss_icons/rc/right_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: right;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::sub-line:horizontal:hover,\nQScrollBar::sub-line:horizontal:on {\n    border-image: url(:/qss_icons/rc/left_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: left;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::up-arrow:horizontal,\nQScrollBar::down-arrow:horizontal {\n    background: none;\n}\n\nQScrollBar::add-page:horizontal,\nQScrollBar::sub-page:horizontal {\n    background: none;\n}\n\nQScrollBar:vertical {\n    background-color: #2a2b2f;\n    width: 15px;\n    margin: 15px 3px 15px 3px;\n    border: 1px transparent #2a2b2f;\n    border-radius: 4px;\n}\n\nQScrollBar::handle:vertical {\n    background-color: #605F5F;\n    min-height: 5px;\n    border-radius: 4px;\n}\n\nQScrollBar::sub-line:vertical {\n    margin: 3px 0px 3px 0px;\n    border-image: url(:/qss_icons/rc/up_arrow_disabled.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: top;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::add-line:vertical {\n    margin: 3px 0px 3px 0px;\n    border-image: url(:/qss_icons/rc/down_arrow_disabled.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: bottom;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::sub-line:vertical:hover,\nQScrollBar::sub-line:vertical:on {\n    border-image: url(:/qss_icons/rc/up_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: top;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::add-line:vertical:hover,\nQScrollBar::add-line:vertical:on {\n    border-image: url(:/qss_icons/rc/down_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: bottom;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::up-arrow:vertical,\nQScrollBar::down-arrow:vertical {\n    background: none;\n}\n\nQScrollBar::add-page:vertical,\nQScrollBar::sub-page:vertical {\n    background: none;\n}\n\nQTextEdit {\n    background-color: #232629;\n    color: #eff0f1;\n    border: 1px solid #2a2b2f;\n}\n\nQPlainTextEdit {\n    background-color: #232629;\n    ;\n    color: #eff0f1;\n    border-radius: 2px;\n    border: 1px solid #2a2b2f;\n}\n\nQHeaderView::section {\n    background-color: #2a2b2f;\n    color: #eff0f1;\n    padding: 5px;\n    border: 1px solid #2a2b2f;\n}\n\nQSizeGrip {\n    image: url(:/qss_icons/rc/sizegrip.png);\n    width: 12px;\n    height: 12px;\n}\n\nQMainWindow::separator {\n    background-color: #1f2022;\n    color: white;\n    padding-left: 4px;\n    spacing: 2px;\n    /*border: 1px dashed #2a2b2f;*/\n}\n\nQMainWindow::separator:hover {\n    background-color: #787876;\n    color: white;\n    padding-left: 4px;\n    border: 1px solid #2a2b2f;\n    spacing: 2px;\n}\n\nQMenu::separator {\n    height: 1px;\n    background-color: #2a2b2f;\n    color: white;\n    padding-left: 4px;\n    margin-left: 10px;\n    margin-right: 5px;\n}\n\nQFrame {\n    border-radius: 2px;\n    border: 1px solid #2a2b2f;\n}\n\nQFrame[frameShape=\"0\"] {\n    border-radius: 2px;\n    border: 1px transparent #2a2b2f;\n}\n\nQStackedWidget {\n    border: 1px transparent black;\n}\n\nQToolBar {\n    border: 1px transparent #2a2b2f;\n    background: 1px solid #1f2022;\n    font-weight: bold;\n}\n\nQToolBar::handle:horizontal {\n    image: url(:/qss_icons/rc/Hmovetoolbar.png);\n}\n\nQToolBar::handle:vertical {\n    image: url(:/qss_icons/rc/Vmovetoolbar.png);\n}\n\nQToolBar::separator:horizontal {\n    image: url(:/qss_icons/rc/Hsepartoolbar.png);\n}\n\nQToolBar::separator:vertical {\n    image: url(:/qss_icons/rc/Vsepartoolbar.png);\n}\n\nQToolButton#qt_toolbar_ext_button {\n    background: #58595a\n}\n\nQPushButton {\n    color: #eff0f1;\n    background-color: #1f2022;\n    border-width: 1px;\n    border-color: #2a2b2f;\n    border-style: solid;\n    padding: 5px;\n    border-radius: 2px;\n    outline: none;\n}\n\nQPushButton:disabled {\n    background-color: #1f2022;\n    border-width: 1px;\n    border-color: #454545;\n    border-style: solid;\n    padding-top: 5px;\n    padding-bottom: 5px;\n    padding-left: 10px;\n    padding-right: 10px;\n    border-radius: 2px;\n    color: #454545;\n}\n\nQPushButton:focus {\n    background-color: #36393c;\n    color: white;\n}\n\nQPushButton:pressed {\n    background-color: #36393c;\n    padding-top: -15px;\n    padding-bottom: -17px;\n}\n\nQComboBox {\n    selection-background-color: #36393c;\n    border-style: solid;\n    border: 1px solid #2a2b2f;\n    border-radius: 2px;\n    padding: 5px;\n    min-width: 75px;\n}\n\nQPushButton:checked {\n    background-color: #2a2b2f;\n    border-color: #2a2b2f;\n}\n\nQComboBox:hover,\nQPushButton:hover,\nQAbstractSpinBox:hover,\nQLineEdit:hover,\nQTextEdit:hover,\nQPlainTextEdit:hover,\nQAbstractView:hover,\nQTreeView:hover {\n    border: 1px solid #36393c;\n    color: #eff0f1;\n}\n\nQComboBox:on {\n    padding-top: 3px;\n    padding-left: 4px;\n    selection-background-color: #4a4a4a;\n}\n\nQComboBox QAbstractItemView {\n    background-color: #232629;\n    border-radius: 2px;\n    border: 1px solid #2a2b2f;\n    selection-background-color: #4f5256;\n}\n\nQComboBox::drop-down {\n    subcontrol-origin: padding;\n    subcontrol-position: top right;\n    width: 15px;\n    border-left-width: 0px;\n    border-left-color: darkgray;\n    border-left-style: solid;\n    border-top-right-radius: 3px;\n    border-bottom-right-radius: 3px;\n}\n\nQComboBox::down-arrow {\n    image: url(:/qss_icons/rc/down_arrow_disabled.png);\n}\n\nQComboBox::down-arrow:on,\nQComboBox::down-arrow:hover,\nQComboBox::down-arrow:focus {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQAbstractSpinBox {\n    padding: 5px;\n    border: 1px solid #2a2b2f;\n    background-color: #232629;\n    color: #eff0f1;\n    border-radius: 2px;\n    min-width: 75px;\n}\n\nQAbstractSpinBox:up-button {\n    background-color: transparent;\n    subcontrol-origin: border;\n    subcontrol-position: center right;\n}\n\nQAbstractSpinBox:down-button {\n    background-color: transparent;\n    subcontrol-origin: border;\n    subcontrol-position: center left;\n}\n\nQAbstractSpinBox::up-arrow,\nQAbstractSpinBox::up-arrow:disabled,\nQAbstractSpinBox::up-arrow:off {\n    image: url(:/qss_icons/rc/up_arrow_disabled.png);\n    width: 10px;\n    height: 10px;\n}\n\nQAbstractSpinBox::up-arrow:hover {\n    image: url(:/qss_icons/rc/up_arrow.png);\n}\n\nQAbstractSpinBox::down-arrow,\nQAbstractSpinBox::down-arrow:disabled,\nQAbstractSpinBox::down-arrow:off {\n    image: url(:/qss_icons/rc/down_arrow_disabled.png);\n    width: 10px;\n    height: 10px;\n}\n\nQAbstractSpinBox::down-arrow:hover {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQLabel {\n    border: 0px solid black;\n}\n\nQTabWidget {\n    border: 0px transparent black;\n}\n\nQTabWidget::pane {\n    border: 1px solid #2a2b2f;\n    padding: 5px;\n    margin: 0px;\n}\n\nQTabWidget::tab-bar {\n    /* left: 5px; move to the right by 5px */\n}\n\nQTabBar {\n    qproperty-drawBase: 0;\n    border-radius: 3px;\n}\n\nQTabBar:focus {\n    border: 0px transparent black;\n}\n\nQTabBar::close-button {\n    image: url(:/qss_icons/rc/close.png);\n    background: transparent;\n}\n\nQTabBar::close-button:hover {\n    image: url(:/qss_icons/rc/close-hover.png);\n    background: transparent;\n}\n\nQTabBar::close-button:pressed {\n    image: url(:/qss_icons/rc/close-pressed.png);\n    background: transparent;\n}\n\n\n/* TOP TABS */\n\nQTabBar::tab:top {\n    color: #eff0f1;\n    border: 1px solid #2a2b2f;\n    border-bottom: 1px transparent black;\n    background-color: #1f2022;\n    padding: 5px;\n    min-width: 50px;\n    border-top-left-radius: 2px;\n    border-top-right-radius: 2px;\n}\n\nQTabBar::tab:top:selected {\n    color: #eff0f1;\n    background-color: #36393c;\n    border: 1px solid #2a2b2f;\n}\n\nQTabBar::tab:top:!selected:hover {\n    background-color: #36393c;\n}\n\n\n/* BOTTOM TABS */\n\nQTabBar::tab:bottom {\n    color: #eff0f1;\n    border: 1px solid #2a2b2f;\n    border-top: 1px transparent black;\n    background-color: #1f2022;\n    padding: 5px;\n    border-bottom-left-radius: 2px;\n    border-bottom-right-radius: 2px;\n    min-width: 50px;\n}\n\nQTabBar::tab:bottom:selected {\n    color: #eff0f1;\n    background-color: #36393c;\n    border: 1px solid #2a2b2f;\n}\n\nQTabBar::tab:bottom:!selected:hover {\n    background-color: #36393c;\n}\n\n\n/* LEFT TABS */\n\nQTabBar::tab:left {\n    color: #eff0f1;\n    border: 1px solid #2a2b2f;\n    border-left: 1px transparent black;\n    background-color: #1f2022;\n    padding: 5px;\n    border-top-right-radius: 2px;\n    border-bottom-right-radius: 2px;\n    min-height: 50px;\n}\n\nQTabBar::tab:left:selected {\n    color: #eff0f1;\n    background-color: #36393c;\n    border: 1px solid #2a2b2f;\n    border-left: 2px solid #36393c;\n    border-top-right-radius: 2px;\n    border-bottom-right-radius: 2px;\n}\n\nQTabBar::tab:left:!selected:hover {\n    background-color: #36393c;\n}\n\n\n/* RIGHT TABS */\n\nQTabBar::tab:right {\n    color: #eff0f1;\n    border: 1px solid #2a2b2f;\n    border-right: 1px transparent black;\n    background-color: #1f2022;\n    padding: 5px;\n    border-top-left-radius: 2px;\n    border-bottom-left-radius: 2px;\n    min-height: 50px;\n}\n\nQTabBar::tab:right:selected {\n    color: #eff0f1;\n    background-color: #36393c;\n    border: 1px solid #2a2b2f;\n    border-right: 2px solid #36393c;\n    border-top-left-radius: 2px;\n    border-bottom-left-radius: 2px;\n}\n\nQTabBar::tab:right:!selected:hover {\n    background-color: #36393c;\n}\n\nQTabBar QToolButton::right-arrow:enabled {\n    image: url(:/qss_icons/rc/right_arrow.png);\n}\n\nQTabBar QToolButton::left-arrow:enabled {\n    image: url(:/qss_icons/rc/left_arrow.png);\n}\n\nQTabBar QToolButton::right-arrow:disabled {\n    image: url(:/qss_icons/rc/right_arrow_disabled.png);\n}\n\nQTabBar QToolButton::left-arrow:disabled {\n    image: url(:/qss_icons/rc/left_arrow_disabled.png);\n}\n\nQDockWidget {\n    background: #1f2022;\n    border: 1px solid #2a2b2f;\n    titlebar-close-icon: url(:/qss_icons/rc/close.png);\n    titlebar-normal-icon: url(:/qss_icons/rc/undock.png);\n}\n\nQDockWidget::close-button,\nQDockWidget::float-button {\n    border: 1px solid transparent;\n    border-radius: 2px;\n    background: transparent;\n}\n\nQDockWidget::close-button:hover,\nQDockWidget::float-button:hover {\n    background: rgba(255, 255, 255, 10);\n}\n\nQDockWidget::close-button:pressed,\nQDockWidget::float-button:pressed {\n    padding: 1px -1px -1px 1px;\n    background: rgba(255, 255, 255, 10);\n}\n\nQTreeView,\nQListView {\n    border: 1px solid #2a2b2f;\n    background-color: #232629;\n}\n\nQTreeView:branch:selected,\nQTreeView:branch:hover {\n    background: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:has-siblings:!adjoins-item {\n    border-image: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:has-siblings:adjoins-item {\n    border-image: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:!has-children:!has-siblings:adjoins-item {\n    border-image: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:has-children:!has-siblings:closed,\nQTreeView::branch:closed:has-children:has-siblings {\n    image: url(:/qss_icons/rc/branch_closed.png);\n}\n\nQTreeView::branch:open:has-children:!has-siblings,\nQTreeView::branch:open:has-children:has-siblings {\n    image: url(:/qss_icons/rc/branch_open.png);\n}\n\nQTreeView::branch:has-children:!has-siblings:closed:hover,\nQTreeView::branch:closed:has-children:has-siblings:hover {\n    image: url(:/qss_icons/rc/branch_closed-on.png);\n}\n\nQTreeView::branch:open:has-children:!has-siblings:hover,\nQTreeView::branch:open:has-children:has-siblings:hover {\n    image: url(:/qss_icons/rc/branch_open-on.png);\n}\n\nQListView::item:!selected:hover,\nQTreeView::item:!selected:hover {\n    background: #4f5256;\n    outline: 0;\n    color: #eff0f1\n}\n\nQListView::item:selected:hover,\nQTreeView::item:selected:hover {\n    background: #36393c;\n    color: #eff0f1;\n}\n\nQTreeView::indicator:checked,\nQListView::indicator:checked {\n    image: url(:/qss_icons/rc/checkbox_checked.png);\n}\n\nQTreeView::indicator:unchecked,\nQListView::indicator:unchecked {\n    image: url(:/qss_icons/rc/checkbox_unchecked.png);\n}\n\nQTreeView::indicator:checked:hover,\nQTreeView::indicator:checked:focus,\nQTreeView::indicator:checked:pressed,\nQListView::indicator:checked:hover,\nQListView::indicator:checked:focus,\nQListView::indicator:checked:pressed {\n    image: url(:/qss_icons/rc/checkbox_checked_focus.png);\n}\n\nQTreeView::indicator:unchecked:hover,\nQTreeView::indicator:unchecked:focus,\nQTreeView::indicator:unchecked:pressed,\nQListView::indicator:unchecked:hover,\nQListView::indicator:unchecked:focus,\nQListView::indicator:unchecked:pressed {\n    image: url(:/qss_icons/rc/checkbox_unchecked_focus.png);\n}\n\nQSlider::groove:horizontal {\n    border: 1px solid #2a2b2f;\n    height: 4px;\n    background: #2a2b2f;\n    margin: 0px;\n    border-radius: 2px;\n}\n\nQSlider::handle:horizontal {\n    background: #232629;\n    border: 1px solid #2a2b2f;\n    width: 16px;\n    height: 16px;\n    margin: -8px 0;\n    border-radius: 9px;\n}\n\nQSlider::groove:vertical {\n    border: 1px solid #2a2b2f;\n    width: 4px;\n    background: #2a2b2f;\n    margin: 0px;\n    border-radius: 3px;\n}\n\nQSlider::handle:vertical {\n    background: #232629;\n    border: 1px solid #2a2b2f;\n    width: 16px;\n    height: 16px;\n    margin: 0 -8px;\n    border-radius: 9px;\n}\n\nQToolButton {\n    background-color: transparent;\n    border: 1px transparent #2a2b2f;\n    border-radius: 2px;\n    margin: 3px;\n    padding: 5px;\n}\n\nQToolButton[popupMode=\"1\"] {\n    /* only for MenuButtonPopup */\n    padding-right: 20px;\n    /* make way for the popup button */\n    border: 1px #2a2b2f;\n    border-radius: 5px;\n}\n\nQToolButton[popupMode=\"2\"] {\n    /* only for InstantPopup */\n    padding-right: 10px;\n    /* make way for the popup button */\n    border: 1px #2a2b2f;\n}\n\nQToolButton:hover {\n    background-color: transparent;\n    border: 1px solid #36393c;\n}\n\nQToolButton::menu-button:hover {\n    border-top-right-radius: 6px;\n    border-bottom-right-radius: 6px;\n    width: 16px;\n    background-color: transparent;\n    border: 1px solid #36393c;\n}\n\nQToolButton:checked,\nQToolButton:pressed,\nQToolButton::menu-button:pressed {\n    background-color: #36393c;\n    border: 1px solid #36393c;\n}\n\n\n/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */\n\nQToolButton::menu-indicator {\n    image: url(:/qss_icons/rc/down_arrow.png);\n    top: -7px;\n    left: -2px;\n    /* shift it a bit */\n}\n\n\n/* the subcontrols below are used only in the MenuButtonPopup mode */\n\nQToolButton::menu-button {\n    border: 1px transparent #2a2b2f;\n    border-top-right-radius: 6px;\n    border-bottom-right-radius: 6px;\n    /* 16px width + 4px for border = 20px allocated above */\n    width: 16px;\n    outline: none;\n}\n\nQToolButton::menu-arrow {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQToolButton::menu-arrow:open {\n    border: 1px solid #2a2b2f;\n}\n\nQPushButton::menu-indicator {\n    subcontrol-origin: padding;\n    subcontrol-position: bottom right;\n    left: 8px;\n}\n\nQTableView {\n    border: 1px solid #2a2b2f;\n    gridline-color: #1f2022;\n    background-color: #232629;\n}\n\nQTableView,\nQHeaderView {\n    border-radius: 0px;\n}\n\nQTableView::item:pressed,\nQListView::item:pressed,\nQTreeView::item:pressed {\n    background: #4f5256;\n    color: #eff0f1;\n}\n\nQTableView::item:selected:active,\nQTreeView::item:selected:active,\nQListView::item:selected:active {\n    background: #36393c;\n    color: #eff0f1;\n}\n\nQHeaderView {\n    background-color: #1f2022;\n    border: 1px transparent;\n    border-radius: 0px;\n    margin: 0px;\n    padding: 0px;\n}\n\nQHeaderView::section {\n    background-color: #2b2f32;\n    color: #eff0f1;\n    padding: 5px;\n    border: 1px solid #2a2b2f;\n    border-radius: 0px;\n    text-align: center;\n}\n\nQHeaderView::section::vertical::first,\nQHeaderView::section::vertical::only-one {\n    border-top: 1px solid #2a2b2f;\n}\n\nQHeaderView::section::vertical {\n    border-top: transparent;\n}\n\nQHeaderView::section::horizontal::first,\nQHeaderView::section::horizontal::only-one {\n    border-left: 1px solid #2a2b2f;\n}\n\nQHeaderView::section::horizontal {\n    border-left: transparent;\n}\n\nQHeaderView::section:checked {\n    color: white;\n    background-color: #334e5e;\n}\n\n\n/* style the sort indicator */\n\nQHeaderView::down-arrow {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQHeaderView::up-arrow {\n    image: url(:/qss_icons/rc/up_arrow.png);\n}\n\nQTableCornerButton::section {\n    background-color: #1f2022;\n    border: 1px transparent #2a2b2f;\n    border-radius: 0px;\n}\n\nQToolBox {\n    padding: 5px;\n    border: 1px transparent black;\n}\n\nQToolBox::tab {\n    color: #eff0f1;\n    background-color: #1f2022;\n    border: 1px solid #2a2b2f;\n    border-bottom: 1px transparent #1f2022;\n    border-top-left-radius: 5px;\n    border-top-right-radius: 5px;\n}\n\nQToolBox::tab:selected {\n    /* italicize selected tabs */\n    font: italic;\n    background-color: #1f2022;\n    border-color: #36393c;\n}\n\nQStatusBar::item {\n    border: 0px transparent dark;\n}\n\nQFrame[height=\"3\"],\nQFrame[width=\"3\"] {\n    background-color: #2a2b2f;\n}\n\nQSplitter::handle {\n    background-color: #1f2022;\n    /*border: 1px dashed #2a2b2f;*/\n}\n\nQSplitter::handle:hover {\n    background-color: #787876;\n    /*border: 1px solid #2a2b2f;*/\n}\n\nQSplitter::handle:horizontal {\n    width: 4px;\n}\n\nQSplitter::handle:vertical {\n    height: 4px;\n}\n\nQProgressBar {\n    border: 1px solid #2a2b2f;\n    border-radius: 5px;\n    text-align: center;\n}\n\nQProgressBar::chunk {\n    background-color: #05B8CC;\n}\n\nQDateEdit {\n    selection-background-color: #36393c;\n    border-style: solid;\n    border: 1px solid #3375A3;\n    border-radius: 2px;\n    padding: 1px;\n    min-width: 75px;\n}\n\nQDateEdit:on {\n    padding-top: 3px;\n    padding-left: 4px;\n    selection-background-color: #4a4a4a;\n}\n\nQDateEdit QAbstractItemView {\n    background-color: #232629;\n    border-radius: 2px;\n    border: 1px solid #3375A3;\n    selection-background-color: #36393c;\n}\n\nQDateEdit::drop-down {\n    subcontrol-origin: padding;\n    subcontrol-position: top right;\n    width: 15px;\n    border-left-width: 0px;\n    border-left-color: darkgray;\n    border-left-style: solid;\n    border-top-right-radius: 3px;\n    border-bottom-right-radius: 3px;\n}\n\nQDateEdit::down-arrow {\n    image: url(:/qss_icons/rc/down_arrow_disabled.png);\n}\n\nQDateEdit::down-arrow:on,\nQDateEdit::down-arrow:hover,\nQDateEdit::down-arrow:focus {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}"
  },
  {
    "path": "src/themes/native/native-macos-dark.qss",
    "content": "/*\nCocoa has no notion of Dock Widgets so Qt has to craft them itself,\nbut the default appearance is awful!\nSo let's try to make them as native as possible.\n*/\nQDockWidget::title {\n    text-align: center;\n    background: palette(window);\n}\n\nQDockWidget {\n    titlebar-close-icon: url(:/native/macos-close-dark.svg);\n    titlebar-normal-icon: url(:/native/macos-float-dark.svg);\n}\n\nQDockWidget::close-button,\nQDockWidget::float-button {\n    border: 1px solid transparent;\n    border-radius: 10px;\n    background: transparent;\n}\n\nQDockWidget::close-button:hover,\nQDockWidget::float-button:hover {\n    background: rgba(255, 255, 255, 10);\n}\n\nQDockWidget::close-button:pressed,\nQDockWidget::float-button:pressed {\n    background: rgba(255, 255, 255, 10);\n}\n"
  },
  {
    "path": "src/themes/native/native-macos-light.qss",
    "content": "/*\nCocoa has no notion of Dock Widgets so Qt has to craft them itself,\nbut the default appearance is awful!\nSo let's try to make them as native as possible.\n*/\nQDockWidget::title {\n    text-align: center;\n}\n\nQDockWidget {\n    titlebar-close-icon: url(:/native/macos-close-light.svg);\n    titlebar-normal-icon: url(:/native/macos-float-light.svg);\n}\n\nQDockWidget::close-button,\nQDockWidget::float-button {\n    border: 1px solid transparent;\n    border-radius: 10px;\n    background: transparent;\n}\n\nQDockWidget::close-button:hover,\nQDockWidget::float-button:hover {\n    background: rgba(0, 0, 0, 10);\n}\n\nQDockWidget::close-button:pressed,\nQDockWidget::float-button:pressed {\n    background: rgba(0, 0, 0, 10);\n}\n"
  },
  {
    "path": "src/themes/native/native.qrc",
    "content": "<RCC>\n  <qresource prefix=\"native\">\n      <file>native.qss</file>\n      <file>native-macos-dark.qss</file>\n      <file>macos-close-dark.svg</file>\n      <file>macos-float-dark.svg</file>\n      <file>native-macos-light.qss</file>\n      <file>macos-close-light.svg</file>\n      <file>macos-float-light.svg</file>\n  </qresource>\n</RCC>\n"
  },
  {
    "path": "src/themes/native/native.qss",
    "content": " /* QMainWindow */\n\nQHeaderView::section {\n     padding: 3px;\n     padding-left: 10px;\n/*     border: 2px solid white;*/\n}\n\nQDockWidget::title {\n    text-align: center;\n}\n\nQToolTip {\n\tbackground-color: #444;\n\tborder: 3px solid #444;\n\tcolor: rgb(232, 232, 232);\n\tfont: 11pt \"Monaco\";\n}\n\n/* CutterTreeView */\n\nCutterTreeView::item\n{\n   padding-top: 1px;\n   padding-bottom: 1px;\n}\n\nQMenu#historyMenu, QMenu#forwardHistoryMenu {\n    menu-scrollable: true;\n}\n"
  },
  {
    "path": "src/themes/qdarkstyle/.gitignore",
    "content": "/*.pyc\n"
  },
  {
    "path": "src/themes/qdarkstyle/dark.qrc",
    "content": "<RCC>\n  <qresource prefix=\"qss_icons\">\n    <file>rc/up_arrow_disabled.png</file>\n    <file>rc/Hmovetoolbar.png</file>\n    <file>rc/stylesheet-branch-end.png</file>\n    <file>rc/branch_closed-on.png</file>\n    <file>rc/stylesheet-vline.png</file>\n    <file>rc/branch_closed.png</file>\n    <file>rc/branch_open-on.png</file>\n    <file>rc/transparent.png</file>\n    <file>rc/right_arrow_disabled.png</file>\n    <file>rc/sizegrip.png</file>\n    <file>rc/close.png</file>\n    <file>rc/close-hover.png</file>\n    <file>rc/close-pressed.png</file>\n    <file>rc/down_arrow.png</file>\n    <file>rc/Vmovetoolbar.png</file>\n    <file>rc/left_arrow.png</file>\n    <file>rc/stylesheet-branch-more.png</file>\n    <file>rc/up_arrow.png</file>\n    <file>rc/right_arrow.png</file>\n    <file>rc/left_arrow_disabled.png</file>\n    <file>rc/Hsepartoolbar.png</file>\n    <file>rc/branch_open.png</file>\n    <file>rc/Vsepartoolbar.png</file>\n    <file>rc/down_arrow_disabled.png</file>\n    <file>rc/undock.png</file>\n    <file>rc/checkbox_checked_disabled.png</file>\n    <file>rc/checkbox_checked_focus.png</file>\n    <file>rc/checkbox_checked.png</file>\n    <file>rc/checkbox_indeterminate.png</file>\n    <file>rc/checkbox_indeterminate_focus.png</file>\n    <file>rc/checkbox_unchecked_disabled.png</file>\n    <file>rc/checkbox_unchecked_focus.png</file>\n    <file>rc/checkbox_unchecked.png</file>\n    <file>rc/radio_checked_disabled.png</file>\n    <file>rc/radio_checked_focus.png</file>\n    <file>rc/radio_checked.png</file>\n    <file>rc/radio_unchecked_disabled.png</file>\n    <file>rc/radio_unchecked_focus.png</file>\n    <file>rc/radio_unchecked.png</file>\n  </qresource>\n  <qresource prefix=\"qdarkstyle\">\n      <file>style.qss</file>\n  </qresource>\n</RCC>\n"
  },
  {
    "path": "src/themes/qdarkstyle/style.qss",
    "content": "QToolTip {\n    border: 1px solid #76797C;\n    background-color: #5A7566;\n    color: white;\n    padding: 0px;                /*remove padding, for fix combobox tooltip.*/\n    opacity: 200;\n}\n\nQWidget {\n    color: #eff0f1;\n    background-color: #31363b;\n    selection-background-color: #3daee9;\n    selection-color: #eff0f1;\n    background-clip: border;\n    border-image: none;\n    border: 0px transparent black;\n    outline: 0;\n}\n\nQWidget:item:hover {\n    background-color: #18465d;\n    color: #eff0f1;\n}\n\nQWidget:item:selected {\n    background-color: #18465d;\n}\n\nQCheckBox {\n    spacing: 5px;\n    outline: none;\n    color: #eff0f1;\n    margin-bottom: 2px;\n}\n\nQCheckBox:disabled {\n    color: #76797C;\n}\n\nQCheckBox::indicator,\nQGroupBox::indicator {\n    width: 18px;\n    height: 18px;\n}\n\nQGroupBox::indicator {\n    margin-left: 2px;\n}\n\nQCheckBox::indicator:unchecked {\n    image: url(:/qss_icons/rc/checkbox_unchecked.png);\n}\n\nQCheckBox::indicator:unchecked:hover,\nQCheckBox::indicator:unchecked:focus,\nQCheckBox::indicator:unchecked:pressed,\nQGroupBox::indicator:unchecked:hover,\nQGroupBox::indicator:unchecked:focus,\nQGroupBox::indicator:unchecked:pressed {\n    border: none;\n    image: url(:/qss_icons/rc/checkbox_unchecked_focus.png);\n}\n\nQCheckBox::indicator:checked {\n    image: url(:/qss_icons/rc/checkbox_checked.png);\n}\n\nQCheckBox::indicator:checked:hover,\nQCheckBox::indicator:checked:focus,\nQCheckBox::indicator:checked:pressed,\nQGroupBox::indicator:checked:hover,\nQGroupBox::indicator:checked:focus,\nQGroupBox::indicator:checked:pressed {\n    border: none;\n    image: url(:/qss_icons/rc/checkbox_checked_focus.png);\n}\n\nQCheckBox::indicator:indeterminate {\n    image: url(:/qss_icons/rc/checkbox_indeterminate.png);\n}\n\nQCheckBox::indicator:indeterminate:focus,\nQCheckBox::indicator:indeterminate:hover,\nQCheckBox::indicator:indeterminate:pressed {\n    image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png);\n}\n\nQCheckBox::indicator:checked:disabled,\nQGroupBox::indicator:checked:disabled {\n    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);\n}\n\nQCheckBox::indicator:unchecked:disabled,\nQGroupBox::indicator:unchecked:disabled {\n    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);\n}\n\nQRadioButton {\n    spacing: 5px;\n    outline: none;\n    color: #eff0f1;\n    margin-bottom: 2px;\n}\n\nQRadioButton:disabled {\n    color: #76797C;\n}\n\nQRadioButton::indicator {\n    width: 21px;\n    height: 21px;\n}\n\nQRadioButton::indicator:unchecked {\n    image: url(:/qss_icons/rc/radio_unchecked.png);\n}\n\nQRadioButton::indicator:unchecked:hover,\nQRadioButton::indicator:unchecked:focus,\nQRadioButton::indicator:unchecked:pressed {\n    border: none;\n    outline: none;\n    image: url(:/qss_icons/rc/radio_unchecked_focus.png);\n}\n\nQRadioButton::indicator:checked {\n    border: none;\n    outline: none;\n    image: url(:/qss_icons/rc/radio_checked.png);\n}\n\nQRadioButton::indicator:checked:hover,\nQRadioButton::indicator:checked:focus,\nQRadioButton::indicator:checked:pressed {\n    border: none;\n    outline: none;\n    image: url(:/qss_icons/rc/radio_checked_focus.png);\n}\n\nQRadioButton::indicator:checked:disabled {\n    outline: none;\n    image: url(:/qss_icons/rc/radio_checked_disabled.png);\n}\n\nQRadioButton::indicator:unchecked:disabled {\n    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);\n}\n\nQMenuBar {\n    background-color: #31363b;\n    color: #eff0f1;\n}\n\nQMenuBar::item {\n    background: transparent;\n}\n\nQMenuBar::item:selected {\n    background: transparent;\n    border: 1px solid #76797C;\n}\n\nQMenuBar::item:pressed {\n    border: 1px solid #76797C;\n    background-color: #3daee9;\n    color: #eff0f1;\n    margin-bottom: -1px;\n    padding-bottom: 1px;\n}\n\nQMenu {\n    border: 1px solid #76797C;\n    color: #eff0f1;\n    margin: 2px;\n}\n\nQMenu::icon {\n    margin: 5px;\n}\n\nQMenu::item {\n    padding: 5px 30px 5px 30px;\n    border: 1px solid transparent;\n    /* reserve space for selection border */\n}\n\nQMenu::item:selected {\n    color: #eff0f1;\n}\n\nQMenu::separator {\n    height: 2px;\n    background: lightblue;\n    margin-left: 10px;\n    margin-right: 5px;\n}\n\nQMenu::indicator {\n    width: 18px;\n    height: 18px;\n}\n\n\n/* non-exclusive indicator = check box style indicator\n   (see QActionGroup::setExclusive) */\n\nQMenu::indicator:non-exclusive:unchecked {\n    image: url(:/qss_icons/rc/checkbox_unchecked.png);\n}\n\nQMenu::indicator:non-exclusive:unchecked:selected {\n    image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png);\n}\n\nQMenu::indicator:non-exclusive:checked {\n    image: url(:/qss_icons/rc/checkbox_checked.png);\n}\n\nQMenu::indicator:non-exclusive:checked:selected {\n    image: url(:/qss_icons/rc/checkbox_checked_disabled.png);\n}\n\n\n/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */\n\nQMenu::indicator:exclusive:unchecked {\n    image: url(:/qss_icons/rc/radio_unchecked.png);\n}\n\nQMenu::indicator:exclusive:unchecked:selected {\n    image: url(:/qss_icons/rc/radio_unchecked_disabled.png);\n}\n\nQMenu::indicator:exclusive:checked {\n    image: url(:/qss_icons/rc/radio_checked.png);\n}\n\nQMenu::indicator:exclusive:checked:selected {\n    image: url(:/qss_icons/rc/radio_checked_disabled.png);\n}\n\nQMenu::right-arrow {\n    margin: 5px;\n    image: url(:/qss_icons/rc/right_arrow.png)\n}\n\nQWidget:disabled {\n    color: #454545;\n    background-color: #31363b;\n}\n\nQAbstractItemView {\n    alternate-background-color: #31363b;\n    color: #eff0f1;\n    border: 1px solid #3A3939;\n    border-radius: 2px;\n}\n\nQWidget:focus,\nQMenuBar:focus {\n    border: 1px solid #3daee9;\n}\n\nQTabWidget:focus,\nQCheckBox:focus,\nQRadioButton:focus,\nQSlider:focus {\n    border: none;\n}\n\nQLineEdit {\n    background-color: #232629;\n    padding: 5px;\n    border-style: solid;\n    border: 1px solid #76797C;\n    border-radius: 2px;\n    color: #eff0f1;\n}\n\nQAbstractItemView QLineEdit {\n    padding: 0;\n}\n\nQGroupBox {\n    border: 1px solid #76797C;\n    border-radius: 2px;\n    margin-top: 20px;\n}\n\nQGroupBox::title {\n    subcontrol-origin: margin;\n    subcontrol-position: top center;\n    padding-left: 10px;\n    padding-right: 10px;\n    padding-top: 10px;\n}\n\nQAbstractScrollArea {\n    border-radius: 2px;\n    border: 1px solid #76797C;\n    background-color: transparent;\n}\n\nQScrollBar:horizontal {\n    height: 15px;\n    margin: 3px 15px 3px 15px;\n    border: 1px transparent #2A2929;\n    border-radius: 4px;\n    background-color: #2A2929;\n}\n\nQScrollBar::handle:horizontal {\n    background-color: #605F5F;\n    min-width: 5px;\n    border-radius: 4px;\n}\n\nQScrollBar::add-line:horizontal {\n    margin: 0px 3px 0px 3px;\n    border-image: url(:/qss_icons/rc/right_arrow_disabled.png);\n    width: 10px;\n    height: 10px;\n    subcontrol-position: right;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::sub-line:horizontal {\n    margin: 0px 3px 0px 3px;\n    border-image: url(:/qss_icons/rc/left_arrow_disabled.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: left;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::add-line:horizontal:hover,\nQScrollBar::add-line:horizontal:on {\n    border-image: url(:/qss_icons/rc/right_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: right;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::sub-line:horizontal:hover,\nQScrollBar::sub-line:horizontal:on {\n    border-image: url(:/qss_icons/rc/left_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: left;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::up-arrow:horizontal,\nQScrollBar::down-arrow:horizontal {\n    background: none;\n}\n\nQScrollBar::add-page:horizontal,\nQScrollBar::sub-page:horizontal {\n    background: none;\n}\n\nQScrollBar:vertical {\n    background-color: #2A2929;\n    width: 15px;\n    margin: 15px 3px 15px 3px;\n    border: 1px transparent #2A2929;\n    border-radius: 4px;\n}\n\nQScrollBar::handle:vertical {\n    background-color: #605F5F;\n    min-height: 5px;\n    border-radius: 4px;\n}\n\nQScrollBar::sub-line:vertical {\n    margin: 3px 0px 3px 0px;\n    border-image: url(:/qss_icons/rc/up_arrow_disabled.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: top;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::add-line:vertical {\n    margin: 3px 0px 3px 0px;\n    border-image: url(:/qss_icons/rc/down_arrow_disabled.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: bottom;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::sub-line:vertical:hover,\nQScrollBar::sub-line:vertical:on {\n    border-image: url(:/qss_icons/rc/up_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: top;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::add-line:vertical:hover,\nQScrollBar::add-line:vertical:on {\n    border-image: url(:/qss_icons/rc/down_arrow.png);\n    height: 10px;\n    width: 10px;\n    subcontrol-position: bottom;\n    subcontrol-origin: margin;\n}\n\nQScrollBar::up-arrow:vertical,\nQScrollBar::down-arrow:vertical {\n    background: none;\n}\n\nQScrollBar::add-page:vertical,\nQScrollBar::sub-page:vertical {\n    background: none;\n}\n\nQTextEdit {\n    background-color: #232629;\n    color: #eff0f1;\n    border: 1px solid #76797C;\n}\n\nQPlainTextEdit {\n    background-color: #232629;\n    ;\n    color: #eff0f1;\n    border-radius: 2px;\n    border: 1px solid #76797C;\n}\n\nQHeaderView::section {\n    background-color: #76797C;\n    color: #eff0f1;\n    padding: 5px;\n    border: 1px solid #76797C;\n}\n\nQSizeGrip {\n    image: url(:/qss_icons/rc/sizegrip.png);\n    width: 12px;\n    height: 12px;\n}\n\nQMainWindow::separator {\n    background-color: #31363b;\n    color: white;\n    padding-left: 4px;\n    spacing: 2px;\n    /*border: 1px dashed #76797C;*/\n}\n\nQMainWindow::separator:hover {\n    background-color: #787876;\n    color: white;\n    padding-left: 4px;\n    border: 1px solid #76797C;\n    spacing: 2px;\n}\n\nQMenu::separator {\n    height: 1px;\n    background-color: #76797C;\n    color: white;\n    padding-left: 4px;\n    margin-left: 10px;\n    margin-right: 5px;\n}\n\nQFrame {\n    border-radius: 2px;\n    border: 1px solid #76797C;\n}\n\nQFrame[frameShape=\"0\"] {\n    border-radius: 2px;\n    border: 1px transparent #76797C;\n}\n\nQStackedWidget {\n    border: 1px transparent black;\n}\n\nQToolBar {\n    border: 1px transparent #393838;\n    background: 1px solid #31363b;\n    font-weight: bold;\n}\n\nQToolBar::handle:horizontal {\n    image: url(:/qss_icons/rc/Hmovetoolbar.png);\n}\n\nQToolBar::handle:vertical {\n    image: url(:/qss_icons/rc/Vmovetoolbar.png);\n}\n\nQToolBar::separator:horizontal {\n    image: url(:/qss_icons/rc/Hsepartoolbar.png);\n}\n\nQToolBar::separator:vertical {\n    image: url(:/qss_icons/rc/Vsepartoolbar.png);\n}\n\nQToolButton#qt_toolbar_ext_button {\n    background: #58595a\n}\n\nQPushButton {\n    color: #eff0f1;\n    background-color: #31363b;\n    border-width: 1px;\n    border-color: #76797C;\n    border-style: solid;\n    padding: 5px;\n    border-radius: 2px;\n    outline: none;\n}\n\nQPushButton:disabled {\n    background-color: #31363b;\n    border-width: 1px;\n    border-color: #454545;\n    border-style: solid;\n    padding-top: 5px;\n    padding-bottom: 5px;\n    padding-left: 10px;\n    padding-right: 10px;\n    border-radius: 2px;\n    color: #454545;\n}\n\nQPushButton:focus {\n    background-color: #3daee9;\n    color: white;\n}\n\nQPushButton:pressed {\n    background-color: #3daee9;\n    padding-top: -15px;\n    padding-bottom: -17px;\n}\n\nQComboBox {\n    selection-background-color: #3daee9;\n    border-style: solid;\n    border: 1px solid #76797C;\n    border-radius: 2px;\n    padding: 5px;\n    min-width: 75px;\n}\n\nQPushButton:checked {\n    background-color: #76797C;\n    border-color: #6A6969;\n}\n\nQComboBox:hover,\nQPushButton:hover,\nQAbstractSpinBox:hover,\nQLineEdit:hover,\nQTextEdit:hover,\nQPlainTextEdit:hover,\nQAbstractView:hover,\nQTreeView:hover {\n    border: 1px solid #3daee9;\n    color: #eff0f1;\n}\n\nQComboBox:on {\n    padding-top: 3px;\n    padding-left: 4px;\n    selection-background-color: #4a4a4a;\n}\n\nQComboBox QAbstractItemView {\n    background-color: #232629;\n    border-radius: 2px;\n    border: 1px solid #76797C;\n    selection-background-color: #18465d;\n}\n\nQComboBox::drop-down {\n    subcontrol-origin: padding;\n    subcontrol-position: top right;\n    width: 15px;\n    border-left-width: 0px;\n    border-left-color: darkgray;\n    border-left-style: solid;\n    border-top-right-radius: 3px;\n    border-bottom-right-radius: 3px;\n}\n\nQComboBox::down-arrow {\n    image: url(:/qss_icons/rc/down_arrow_disabled.png);\n}\n\nQComboBox::down-arrow:on,\nQComboBox::down-arrow:hover,\nQComboBox::down-arrow:focus {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQAbstractSpinBox {\n    padding: 5px;\n    border: 1px solid #76797C;\n    background-color: #232629;\n    color: #eff0f1;\n    border-radius: 2px;\n    min-width: 75px;\n}\n\nQAbstractSpinBox:up-button {\n    background-color: transparent;\n    subcontrol-origin: border;\n    subcontrol-position: center right;\n}\n\nQAbstractSpinBox:down-button {\n    background-color: transparent;\n    subcontrol-origin: border;\n    subcontrol-position: center left;\n}\n\nQAbstractSpinBox::up-arrow,\nQAbstractSpinBox::up-arrow:disabled,\nQAbstractSpinBox::up-arrow:off {\n    image: url(:/qss_icons/rc/up_arrow_disabled.png);\n    width: 10px;\n    height: 10px;\n}\n\nQAbstractSpinBox::up-arrow:hover {\n    image: url(:/qss_icons/rc/up_arrow.png);\n}\n\nQAbstractSpinBox::down-arrow,\nQAbstractSpinBox::down-arrow:disabled,\nQAbstractSpinBox::down-arrow:off {\n    image: url(:/qss_icons/rc/down_arrow_disabled.png);\n    width: 10px;\n    height: 10px;\n}\n\nQAbstractSpinBox::down-arrow:hover {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQLabel {\n    border: 0px solid black;\n}\n\nQTabWidget {\n    border: 0px transparent black;\n}\n\nQTabWidget::pane {\n    border: 1px solid #76797C;\n    padding: 5px;\n    margin: 0px;\n}\n\nQTabWidget::tab-bar {\n    /* left: 5px; move to the right by 5px */\n}\n\nQTabBar {\n    qproperty-drawBase: 0;\n    border-radius: 3px;\n}\n\nQTabBar:focus {\n    border: 0px transparent black;\n}\n\nQTabBar::close-button {\n    image: url(:/qss_icons/rc/close.png);\n    background: transparent;\n}\n\nQTabBar::close-button:hover {\n    image: url(:/qss_icons/rc/close-hover.png);\n    background: transparent;\n}\n\nQTabBar::close-button:pressed {\n    image: url(:/qss_icons/rc/close-pressed.png);\n    background: transparent;\n}\n\n\n/* TOP TABS */\n\nQTabBar::tab:top {\n    color: #eff0f1;\n    border: 1px solid #76797C;\n    border-bottom: 1px transparent black;\n    background-color: #31363b;\n    padding: 5px;\n    min-width: 50px;\n    border-top-left-radius: 2px;\n    border-top-right-radius: 2px;\n}\n\nQTabBar::tab:top:selected {\n    color: #eff0f1;\n    background-color: #54575B;\n    border: 1px solid #76797C;\n    border-bottom: 2px solid #3daee9;\n    border-top-left-radius: 2px;\n    border-top-right-radius: 2px;\n}\n\nQTabBar::tab:top:!selected:hover {\n    background-color: #3daee9;\n}\n\n\n/* BOTTOM TABS */\n\nQTabBar::tab:bottom {\n    color: #eff0f1;\n    border: 1px solid #76797C;\n    border-top: 1px transparent black;\n    background-color: #31363b;\n    padding: 5px;\n    border-bottom-left-radius: 2px;\n    border-bottom-right-radius: 2px;\n    min-width: 50px;\n}\n\nQTabBar::tab:bottom:selected {\n    color: #eff0f1;\n    background-color: #54575B;\n    border: 1px solid #76797C;\n    border-top: 2px solid #3daee9;\n    border-bottom-left-radius: 2px;\n    border-bottom-right-radius: 2px;\n}\n\nQTabBar::tab:bottom:!selected:hover {\n    background-color: #3daee9;\n}\n\n\n/* LEFT TABS */\n\nQTabBar::tab:left {\n    color: #eff0f1;\n    border: 1px solid #76797C;\n    border-left: 1px transparent black;\n    background-color: #31363b;\n    padding: 5px;\n    border-top-right-radius: 2px;\n    border-bottom-right-radius: 2px;\n    min-height: 50px;\n}\n\nQTabBar::tab:left:selected {\n    color: #eff0f1;\n    background-color: #54575B;\n    border: 1px solid #76797C;\n    border-left: 2px solid #3daee9;\n    border-top-right-radius: 2px;\n    border-bottom-right-radius: 2px;\n}\n\nQTabBar::tab:left:!selected:hover {\n    background-color: #3daee9;\n}\n\n\n/* RIGHT TABS */\n\nQTabBar::tab:right {\n    color: #eff0f1;\n    border: 1px solid #76797C;\n    border-right: 1px transparent black;\n    background-color: #31363b;\n    padding: 5px;\n    border-top-left-radius: 2px;\n    border-bottom-left-radius: 2px;\n    min-height: 50px;\n}\n\nQTabBar::tab:right:selected {\n    color: #eff0f1;\n    background-color: #54575B;\n    border: 1px solid #76797C;\n    border-right: 2px solid #3daee9;\n    border-top-left-radius: 2px;\n    border-bottom-left-radius: 2px;\n}\n\nQTabBar::tab:right:!selected:hover {\n    background-color: #3daee9;\n}\n\nQTabBar QToolButton::right-arrow:enabled {\n    image: url(:/qss_icons/rc/right_arrow.png);\n}\n\nQTabBar QToolButton::left-arrow:enabled {\n    image: url(:/qss_icons/rc/left_arrow.png);\n}\n\nQTabBar QToolButton::right-arrow:disabled {\n    image: url(:/qss_icons/rc/right_arrow_disabled.png);\n}\n\nQTabBar QToolButton::left-arrow:disabled {\n    image: url(:/qss_icons/rc/left_arrow_disabled.png);\n}\n\nQDockWidget {\n    background: #31363b;\n    border: 1px solid #403F3F;\n    titlebar-close-icon: url(:/qss_icons/rc/close.png);\n    titlebar-normal-icon: url(:/qss_icons/rc/undock.png);\n}\n\nQDockWidget::close-button,\nQDockWidget::float-button {\n    border: 1px solid transparent;\n    border-radius: 2px;\n    background: transparent;\n}\n\nQDockWidget::close-button:hover,\nQDockWidget::float-button:hover {\n    background: rgba(255, 255, 255, 10);\n}\n\nQDockWidget::close-button:pressed,\nQDockWidget::float-button:pressed {\n    padding: 1px -1px -1px 1px;\n    background: rgba(255, 255, 255, 10);\n}\n\nQTreeView,\nQListView {\n    border: 1px solid #76797C;\n    background-color: #232629;\n}\n\nQTreeView:branch:selected,\nQTreeView:branch:hover {\n    background: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:has-siblings:!adjoins-item {\n    border-image: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:has-siblings:adjoins-item {\n    border-image: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:!has-children:!has-siblings:adjoins-item {\n    border-image: url(:/qss_icons/rc/transparent.png);\n}\n\nQTreeView::branch:has-children:!has-siblings:closed,\nQTreeView::branch:closed:has-children:has-siblings {\n    image: url(:/qss_icons/rc/branch_closed.png);\n}\n\nQTreeView::branch:open:has-children:!has-siblings,\nQTreeView::branch:open:has-children:has-siblings {\n    image: url(:/qss_icons/rc/branch_open.png);\n}\n\nQTreeView::branch:has-children:!has-siblings:closed:hover,\nQTreeView::branch:closed:has-children:has-siblings:hover {\n    image: url(:/qss_icons/rc/branch_closed-on.png);\n}\n\nQTreeView::branch:open:has-children:!has-siblings:hover,\nQTreeView::branch:open:has-children:has-siblings:hover {\n    image: url(:/qss_icons/rc/branch_open-on.png);\n}\n\nQListView::item:!selected:hover,\nQTreeView::item:!selected:hover {\n    background: #18465d;\n    outline: 0;\n    color: #eff0f1\n}\n\nQListView::item:selected:hover,\nQTreeView::item:selected:hover {\n    background: #287399;\n    color: #eff0f1;\n}\n\nQTreeView::indicator:checked,\nQListView::indicator:checked {\n    image: url(:/qss_icons/rc/checkbox_checked.png);\n}\n\nQTreeView::indicator:unchecked,\nQListView::indicator:unchecked {\n    image: url(:/qss_icons/rc/checkbox_unchecked.png);\n}\n\nQTreeView::indicator:checked:hover,\nQTreeView::indicator:checked:focus,\nQTreeView::indicator:checked:pressed,\nQListView::indicator:checked:hover,\nQListView::indicator:checked:focus,\nQListView::indicator:checked:pressed {\n    image: url(:/qss_icons/rc/checkbox_checked_focus.png);\n}\n\nQTreeView::indicator:unchecked:hover,\nQTreeView::indicator:unchecked:focus,\nQTreeView::indicator:unchecked:pressed,\nQListView::indicator:unchecked:hover,\nQListView::indicator:unchecked:focus,\nQListView::indicator:unchecked:pressed {\n    image: url(:/qss_icons/rc/checkbox_unchecked_focus.png);\n}\n\nQSlider::groove:horizontal {\n    border: 1px solid #565a5e;\n    height: 4px;\n    background: #565a5e;\n    margin: 0px;\n    border-radius: 2px;\n}\n\nQSlider::handle:horizontal {\n    background: #232629;\n    border: 1px solid #565a5e;\n    width: 16px;\n    height: 16px;\n    margin: -8px 0;\n    border-radius: 9px;\n}\n\nQSlider::groove:vertical {\n    border: 1px solid #565a5e;\n    width: 4px;\n    background: #565a5e;\n    margin: 0px;\n    border-radius: 3px;\n}\n\nQSlider::handle:vertical {\n    background: #232629;\n    border: 1px solid #565a5e;\n    width: 16px;\n    height: 16px;\n    margin: 0 -8px;\n    border-radius: 9px;\n}\n\nQToolButton {\n    background-color: transparent;\n    border: 1px transparent #76797C;\n    border-radius: 2px;\n    margin: 3px;\n    padding: 5px;\n}\n\nQToolButton[popupMode=\"1\"] {\n    /* only for MenuButtonPopup */\n    padding-right: 20px;\n    /* make way for the popup button */\n    border: 1px #76797C;\n    border-radius: 5px;\n}\n\nQToolButton[popupMode=\"2\"] {\n    /* only for InstantPopup */\n    padding-right: 10px;\n    /* make way for the popup button */\n    border: 1px #76797C;\n}\n\nQToolButton:hover {\n    background-color: transparent;\n    border: 1px solid #3daee9;\n}\n\nQToolButton::menu-button:hover {\n    border-top-right-radius: 6px;\n    border-bottom-right-radius: 6px;\n    width: 16px;\n    background-color: transparent;\n    border: 1px solid #3daee9;\n}\n\nQToolButton:checked,\nQToolButton:pressed,\nQToolButton::menu-button:pressed {\n    background-color: #3daee9;\n    border: 1px solid #3daee9;\n}\n\n\n/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */\n\nQToolButton::menu-indicator {\n    image: url(:/qss_icons/rc/down_arrow.png);\n    top: -2px;\n    left: -2px;\n    /* shift it a bit */\n}\n\n\n/* the subcontrols below are used only in the MenuButtonPopup mode */\n\nQToolButton::menu-button {\n    border: 1px transparent #76797C;\n    border-top-right-radius: 6px;\n    border-bottom-right-radius: 6px;\n    /* 16px width + 4px for border = 20px allocated above */\n    width: 16px;\n    outline: none;\n}\n\nQToolButton::menu-arrow {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQToolButton::menu-arrow:open {\n    border: 1px solid #76797C;\n}\n\nQPushButton::menu-indicator {\n    subcontrol-origin: padding;\n    subcontrol-position: bottom right;\n    left: 8px;\n}\n\nQTableView {\n    border: 1px solid #76797C;\n    gridline-color: #31363b;\n    background-color: #232629;\n}\n\nQTableView,\nQHeaderView {\n    border-radius: 0px;\n}\n\nQTableView::item:pressed,\nQListView::item:pressed,\nQTreeView::item:pressed {\n    background: #18465d;\n    color: #eff0f1;\n}\n\nQTableView::item:selected:active,\nQTreeView::item:selected:active,\nQListView::item:selected:active {\n    background: #287399;\n    color: #eff0f1;\n}\n\nQHeaderView {\n    background-color: #31363b;\n    border: 1px transparent;\n    border-radius: 0px;\n    margin: 0px;\n    padding: 0px;\n}\n\nQHeaderView::section {\n    background-color: #31363b;\n    color: #eff0f1;\n    padding: 5px;\n    border: 1px solid #76797C;\n    border-radius: 0px;\n    text-align: center;\n}\n\nQHeaderView::section::vertical::first,\nQHeaderView::section::vertical::only-one {\n    border-top: 1px solid #76797C;\n}\n\nQHeaderView::section::vertical {\n    border-top: transparent;\n}\n\nQHeaderView::section::horizontal::first,\nQHeaderView::section::horizontal::only-one {\n    border-left: 1px solid #76797C;\n}\n\nQHeaderView::section::horizontal {\n    border-left: transparent;\n}\n\nQHeaderView::section:checked {\n    color: white;\n    background-color: #334e5e;\n}\n\n\n/* style the sort indicator */\n\nQHeaderView::down-arrow {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQHeaderView::up-arrow {\n    image: url(:/qss_icons/rc/up_arrow.png);\n}\n\nQTableCornerButton::section {\n    background-color: #31363b;\n    border: 1px transparent #76797C;\n    border-radius: 0px;\n}\n\nQToolBox {\n    padding: 5px;\n    border: 1px transparent black;\n}\n\nQToolBox::tab {\n    color: #eff0f1;\n    background-color: #31363b;\n    border: 1px solid #76797C;\n    border-bottom: 1px transparent #31363b;\n    border-top-left-radius: 5px;\n    border-top-right-radius: 5px;\n}\n\nQToolBox::tab:selected {\n    /* italicize selected tabs */\n    font: italic;\n    background-color: #31363b;\n    border-color: #3daee9;\n}\n\nQStatusBar::item {\n    border: 0px transparent dark;\n}\n\nQFrame[height=\"3\"],\nQFrame[width=\"3\"] {\n    background-color: #76797C;\n}\n\nQSplitter::handle {\n    background-color: #31363b;\n    /*border: 1px dashed #76797C;*/\n}\n\nQSplitter::handle:hover {\n    background-color: #787876;\n    /*border: 1px solid #76797C;*/\n}\n\nQSplitter::handle:horizontal {\n    width: 4px;\n}\n\nQSplitter::handle:vertical {\n    height: 4px;\n}\n\nQProgressBar {\n    border: 1px solid #76797C;\n    border-radius: 5px;\n    text-align: center;\n}\n\nQProgressBar::chunk {\n    background-color: #05B8CC;\n}\n\nQDateEdit {\n    selection-background-color: #3daee9;\n    border-style: solid;\n    border: 1px solid #3375A3;\n    border-radius: 2px;\n    padding: 1px;\n    min-width: 75px;\n}\n\nQDateEdit:on {\n    padding-top: 3px;\n    padding-left: 4px;\n    selection-background-color: #4a4a4a;\n}\n\nQDateEdit QAbstractItemView {\n    background-color: #232629;\n    border-radius: 2px;\n    border: 1px solid #3375A3;\n    selection-background-color: #3daee9;\n}\n\nQDateEdit::drop-down {\n    subcontrol-origin: padding;\n    subcontrol-position: top right;\n    width: 15px;\n    border-left-width: 0px;\n    border-left-color: darkgray;\n    border-left-style: solid;\n    border-top-right-radius: 3px;\n    border-bottom-right-radius: 3px;\n}\n\nQDateEdit::down-arrow {\n    image: url(:/qss_icons/rc/down_arrow_disabled.png);\n}\n\nQDateEdit::down-arrow:on,\nQDateEdit::down-arrow:hover,\nQDateEdit::down-arrow:focus {\n    image: url(:/qss_icons/rc/down_arrow.png);\n}\n\nQMenu#historyMenu, QMenu#forwardHistoryMenu {\n    menu-scrollable: true;\n}\n"
  },
  {
    "path": "src/tools/basefind/BaseFindDialog.cpp",
    "content": "#include \"BaseFindDialog.h\"\n#include \"ui_BaseFindDialog.h\"\n\n#include \"BaseFindSearchDialog.h\"\n\n#include <core/Cutter.h>\n#include <rz_th.h>\n\nBaseFindDialog::BaseFindDialog(QWidget *parent) : QDialog(parent), ui(new Ui::BaseFindDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    // Fill in N-thread Combo\n    RzThreadNCores n_cores = rz_th_physical_core_number();\n    ui->nCoresCombo->clear();\n    for (size_t i = n_cores; i > 0; i--) {\n        if (n_cores == i) {\n            ui->nCoresCombo->addItem(\"All Cores\");\n            continue;\n        }\n        ui->nCoresCombo->addItem(QString::number(i));\n    }\n\n    ui->startAddressEdit->setText(Core()->getConfig(\"basefind.search.start\"));\n    ui->endAddressEdit->setText(Core()->getConfig(\"basefind.search.end\"));\n    ui->alignmentEdit->setText(Core()->getConfig(\"basefind.alignment\"));\n    ui->minStrLenEdit->setValue(Core()->getConfigut64(\"basefind.min.string\"));\n    ui->minScoreEdit->setValue(Core()->getConfigut64(\"basefind.min.score\"));\n\n    size_t selected_n_cores = Core()->getConfigut64(\"basefind.max.threads\");\n    if (selected_n_cores < n_cores && selected_n_cores > 0) {\n        ui->nCoresCombo->setCurrentIndex(n_cores - selected_n_cores);\n    }\n}\n\nBaseFindDialog::~BaseFindDialog() {}\n\nRzThreadNCores BaseFindDialog::getNCores() const\n{\n    RzThreadNCores n_cores = rz_th_physical_core_number();\n    return static_cast<RzThreadNCores>(n_cores - ui->nCoresCombo->currentIndex());\n}\n\nut32 BaseFindDialog::getPointerSize() const\n{\n    auto index = ui->pointerSizeCombo->currentIndex();\n    QString value = ui->pointerSizeCombo->itemText(index);\n    return value.toULong(nullptr, 0);\n}\n\nRVA BaseFindDialog::getStartAddress() const\n{\n    QString value = ui->startAddressEdit->text();\n    return value.toULongLong(nullptr, 0);\n}\n\nRVA BaseFindDialog::getEndAddress() const\n{\n    QString value = ui->endAddressEdit->text();\n    return value.toULongLong(nullptr, 0);\n}\n\nRVA BaseFindDialog::getAlignment() const\n{\n    QString value = ui->alignmentEdit->text();\n    return value.toULongLong(nullptr, 0);\n}\n\nut32 BaseFindDialog::getMinStrLen() const\n{\n    return ui->minStrLenEdit->value();\n}\n\nut32 BaseFindDialog::getMinScore() const\n{\n    return ui->minScoreEdit->value();\n}\n\nvoid BaseFindDialog::on_buttonBox_accepted()\n{\n    RzBaseFindOpt options = {};\n    options.max_threads = getNCores();\n    options.pointer_size = getPointerSize();\n    options.start_address = getStartAddress();\n    options.end_address = getEndAddress();\n    options.alignment = getAlignment();\n    options.min_score = getMinScore();\n    options.min_string_len = getMinStrLen();\n    options.callback = nullptr;\n    options.user = nullptr;\n\n    BaseFindSearchDialog *bfs = new BaseFindSearchDialog(parentWidget());\n    bfs->show(&options);\n}\n\nvoid BaseFindDialog::on_buttonBox_rejected() {}\n"
  },
  {
    "path": "src/tools/basefind/BaseFindDialog.h",
    "content": "#ifndef BASEFIND_DIALOG_H\n#define BASEFIND_DIALOG_H\n\n#include <QDialog>\n#include <QListWidgetItem>\n#include <memory>\n\n#include <core/Cutter.h>\n\nnamespace Ui {\nclass BaseFindDialog;\n}\n\nclass BaseFindDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit BaseFindDialog(QWidget *parent = nullptr);\n    ~BaseFindDialog();\n\n    RzThreadNCores getNCores() const;\n    ut32 getPointerSize() const;\n    RVA getStartAddress() const;\n    RVA getEndAddress() const;\n    RVA getAlignment() const;\n    ut32 getMinStrLen() const;\n    ut32 getMinScore() const;\n\nprivate slots:\n    void on_buttonBox_accepted();\n    void on_buttonBox_rejected();\n\nprivate:\n    std::unique_ptr<Ui::BaseFindDialog> ui;\n};\n\n#endif // BASEFIND_DIALOG_H\n"
  },
  {
    "path": "src/tools/basefind/BaseFindDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BaseFindDialog</class>\n <widget class=\"QDialog\" name=\"BaseFindDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>373</width>\n    <height>348</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>BaseFind</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetMinimumSize</enum>\n   </property>\n   <item>\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"nCoresLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Cores:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"nCoresCombo\"/>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"pointerSizeLabel\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>Pointer Size:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"pointerSizeCombo\">\n       <item>\n        <property name=\"text\">\n         <string>32</string>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>64</string>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"startAddressLabel\">\n       <property name=\"text\">\n        <string>Start Address:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"startAddressEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>382</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"endAddressLabel\">\n       <property name=\"text\">\n        <string>End Address:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"endAddressEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>382</width>\n         <height>16777215</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"0\">\n      <widget class=\"QLabel\" name=\"alignmentLabel\">\n       <property name=\"text\">\n        <string>Alignment:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"alignmentEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>382</width>\n         <height>16777215</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"0\">\n      <widget class=\"QLabel\" name=\"minStrLenLabel\">\n       <property name=\"text\">\n        <string>Min String Length:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"5\" column=\"1\">\n      <widget class=\"QSpinBox\" name=\"minStrLenEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>382</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"minimum\">\n        <number>4</number>\n       </property>\n       <property name=\"maximum\">\n        <number>999999999</number>\n       </property>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"0\">\n      <widget class=\"QLabel\" name=\"minScoreLabel\">\n       <property name=\"text\">\n        <string>Min Score:</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"6\" column=\"1\">\n      <widget class=\"QSpinBox\" name=\"minScoreEdit\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>382</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"minimum\">\n        <number>1</number>\n       </property>\n       <property name=\"maximum\">\n        <number>999999999</number>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n  <action name=\"actionRemoveItem\">\n   <property name=\"text\">\n    <string>Remove item</string>\n   </property>\n  </action>\n  <action name=\"actionRemoveAll\">\n   <property name=\"text\">\n    <string>Remove all</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Remove all</string>\n   </property>\n  </action>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>BaseFindDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>234</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>BaseFindDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>240</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>254</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/tools/basefind/BaseFindResultsDialog.cpp",
    "content": "#include \"BaseFindResultsDialog.h\"\n#include \"ui_BaseFindResultsDialog.h\"\n\n#include <QClipboard>\n#include <QMessageBox>\n\n#include <core/Cutter.h>\n#include <CutterApplication.h>\n\nBaseFindResultsModel::BaseFindResultsModel(QList<BasefindResultDescription> list, QObject *parent)\n    : QAbstractListModel(parent), list(list)\n{\n}\n\nint BaseFindResultsModel::rowCount(const QModelIndex &) const\n{\n    return list.count();\n}\n\nint BaseFindResultsModel::columnCount(const QModelIndex &) const\n{\n    return BaseFindResultsModel::ColumnCount;\n}\n\nQVariant BaseFindResultsModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= list.count())\n        return QVariant();\n\n    const BasefindResultDescription &entry = list.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case ScoreColumn:\n            return QString::asprintf(\"%u\", entry.score);\n        case CandidateColumn:\n            return QString::asprintf(\"%#010llx\", entry.candidate);\n        default:\n            return QVariant();\n        }\n\n    case Qt::ToolTipRole: {\n        return QString::asprintf(\"%#010llx\", entry.candidate);\n    }\n\n    default:\n        return QVariant();\n    }\n}\n\nQVariant BaseFindResultsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case ScoreColumn:\n            return tr(\"Score\");\n        case CandidateColumn:\n            return tr(\"Address\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nBaseFindResultsDialog::BaseFindResultsDialog(QList<BasefindResultDescription> results,\n                                             QWidget *parent)\n    : QDialog(parent), ui(new Ui::BaseFindResultsDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n\n    model = new BaseFindResultsModel(results, this);\n    ui->tableView->setModel(model);\n    ui->tableView->sortByColumn(BaseFindResultsModel::ScoreColumn, Qt::AscendingOrder);\n    ui->tableView->verticalHeader()->hide();\n    ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);\n    ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    blockMenu = new QMenu(this);\n    actionCopyCandidate = new QAction(tr(\"Copy %1\"), this);\n    actionSetLoadAddr = new QAction(tr(\"Reopen Cutter with base address as %1\"), this);\n    actionSetMapAddr = new QAction(tr(\"Reopen Cutter with map address as %1\"), this);\n\n    connect(ui->tableView, &QWidget::customContextMenuRequested, this,\n            &BaseFindResultsDialog::showItemContextMenu);\n    connect(actionCopyCandidate, &QAction::triggered, this,\n            &BaseFindResultsDialog::onActionCopyLine);\n    connect(actionSetLoadAddr, &QAction::triggered, this,\n            &BaseFindResultsDialog::onActionSetLoadAddr);\n    connect(actionSetMapAddr, &QAction::triggered, this,\n            &BaseFindResultsDialog::onActionSetMapAddr);\n\n    blockMenu->addAction(actionSetLoadAddr);\n    blockMenu->addAction(actionSetMapAddr);\n    blockMenu->addAction(actionCopyCandidate);\n    addActions(blockMenu->actions());\n}\n\nvoid BaseFindResultsDialog::showItemContextMenu(const QPoint &pt)\n{\n    auto index = ui->tableView->currentIndex();\n    if (index.isValid()) {\n        const BasefindResultDescription &entry = model->list.at(index.row());\n        candidate = entry.candidate;\n        auto addr = QString::asprintf(\"%#010llx\", candidate);\n        actionCopyCandidate->setText(tr(\"Copy %1\").arg(addr));\n        actionSetLoadAddr->setText(tr(\"Reopen Cutter with base address as %1\").arg(addr));\n        actionSetMapAddr->setText(tr(\"Reopen Cutter with map address as %1\").arg(addr));\n        blockMenu->exec(this->mapToGlobal(pt));\n    }\n}\n\nvoid BaseFindResultsDialog::onActionCopyLine()\n{\n    auto clipboard = QApplication::clipboard();\n    clipboard->setText(QString::asprintf(\"%#010llx\", candidate));\n}\n\nvoid BaseFindResultsDialog::onActionSetLoadAddr()\n{\n    auto cutter = static_cast<CutterApplication *>(qApp);\n    auto options = cutter->getInitialOptions();\n    auto oldValue = options.binLoadAddr;\n\n    // override options to generate correct args\n    options.binLoadAddr = candidate;\n    cutter->setInitialOptions(options);\n    auto args = cutter->getArgs();\n\n    // revert back options\n    options.binLoadAddr = oldValue;\n    cutter->setInitialOptions(options);\n\n    cutter->launchNewInstance(args);\n}\n\nvoid BaseFindResultsDialog::onActionSetMapAddr()\n{\n    auto cutter = static_cast<CutterApplication *>(qApp);\n    auto options = cutter->getInitialOptions();\n    auto oldValue = options.mapAddr;\n\n    // override options to generate correct args\n    options.mapAddr = candidate;\n    cutter->setInitialOptions(options);\n    auto args = cutter->getArgs();\n\n    // revert back options\n    options.mapAddr = oldValue;\n    cutter->setInitialOptions(options);\n\n    cutter->launchNewInstance(args);\n}\n\nBaseFindResultsDialog::~BaseFindResultsDialog() {}\n\nvoid BaseFindResultsDialog::on_buttonBox_rejected() {}\n"
  },
  {
    "path": "src/tools/basefind/BaseFindResultsDialog.h",
    "content": "#ifndef BASEFIND_RESULTS_DIALOG_H\n#define BASEFIND_RESULTS_DIALOG_H\n\n#include <QDialog>\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n#include <memory>\n\n#include <core/Cutter.h>\n\nclass BaseFindResultsDialog;\n\nnamespace Ui {\nclass BaseFindResultsDialog;\n}\n\nclass BaseFindResultsModel : public QAbstractListModel\n{\n    Q_OBJECT\n\n    friend BaseFindResultsDialog;\n\npublic:\n    enum Column { ScoreColumn = 0, CandidateColumn, ColumnCount };\n\n    BaseFindResultsModel(QList<BasefindResultDescription> list, QObject *parent = nullptr);\n\n    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;\n    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const;\n\nprivate:\n    QList<BasefindResultDescription> list;\n};\n\nclass BaseFindResultsDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit BaseFindResultsDialog(QList<BasefindResultDescription> results,\n                                   QWidget *parent = nullptr);\n    ~BaseFindResultsDialog();\n\npublic slots:\n    void showItemContextMenu(const QPoint &pt);\n\nprivate slots:\n    void on_buttonBox_rejected();\n\nprivate:\n    void onActionCopyLine();\n    void onActionSetLoadAddr();\n    void onActionSetMapAddr();\n\n    std::unique_ptr<Ui::BaseFindResultsDialog> ui;\n    BaseFindResultsModel *model;\n    QMenu *blockMenu;\n    QAction *actionCopyCandidate;\n    QAction *actionSetLoadAddr;\n    QAction *actionSetMapAddr;\n    RVA candidate;\n};\n\n#endif // BASEFIND_RESULTS_DIALOG_H\n"
  },
  {
    "path": "src/tools/basefind/BaseFindResultsDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BaseFindResultsDialog</class>\n <widget class=\"QDialog\" name=\"BaseFindResultsDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>582</width>\n    <height>382</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>BaseFind Results</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetMinimumSize</enum>\n   </property>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"mainVerticalLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <item>\n      <widget class=\"QTableView\" name=\"tableView\"/>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Close</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>BaseFindResultsDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>240</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>254</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/tools/basefind/BaseFindSearchDialog.cpp",
    "content": "#include \"BaseFindSearchDialog.h\"\n#include \"ui_BaseFindSearchDialog.h\"\n\n#include \"BaseFindResultsDialog.h\"\n\n#include <QLabel>\n#include <QFormLayout>\n\n#include <core/Cutter.h>\n#include <rz_th.h>\n\nBaseFindSearchDialog::BaseFindSearchDialog(QWidget *parent)\n    : QDialog(parent), basefind(new Basefind(Core())), ui(new Ui::BaseFindSearchDialog)\n{\n    ui->setupUi(this);\n    setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));\n}\n\nBaseFindSearchDialog::~BaseFindSearchDialog() {}\n\nvoid BaseFindSearchDialog::show(RzBaseFindOpt *opts)\n{\n    RzThreadNCores n_cores = rz_th_physical_core_number();\n    if (opts->max_threads > n_cores || opts->max_threads < 1) {\n        opts->max_threads = n_cores;\n    }\n\n    QFormLayout *layout = new QFormLayout();\n    ui->scrollAreaWidgetContents->setLayout(layout);\n    for (ut32 i = 0; i < opts->max_threads; ++i) {\n        QString label = QString::asprintf(\"Core %u\", i);\n        QProgressBar *pbar = new QProgressBar(nullptr);\n        layout->addRow(label, pbar);\n        pbar->setRange(0, 100);\n        bars.push_back(pbar);\n    }\n\n    if (!basefind->setOptions(opts)) {\n        return;\n    }\n\n    connect(this, &BaseFindSearchDialog::cancelSearch, basefind.get(), &Basefind::cancel);\n    connect(basefind.get(), &Basefind::progress, this, &BaseFindSearchDialog::onProgress);\n    connect(basefind.get(), &Basefind::complete, this, &BaseFindSearchDialog::onCompletion);\n\n    basefind->start();\n    this->QDialog::show();\n}\n\nvoid BaseFindSearchDialog::onProgress(BasefindCoreStatusDescription status)\n{\n    bars[status.index]->setValue(status.percentage);\n}\n\nvoid BaseFindSearchDialog::onCompletion()\n{\n    auto results = basefind->results();\n    BaseFindResultsDialog *table = new BaseFindResultsDialog(results, parentWidget());\n    table->show();\n    this->close();\n}\n\nvoid BaseFindSearchDialog::on_buttonBox_rejected()\n{\n    emit cancelSearch();\n}\n"
  },
  {
    "path": "src/tools/basefind/BaseFindSearchDialog.h",
    "content": "#ifndef BASEFIND_SEARCH_DIALOG_H\n#define BASEFIND_SEARCH_DIALOG_H\n\n#include <QDialog>\n#include <QListWidgetItem>\n#include <QProgressBar>\n#include <memory>\n\n#include <core/Cutter.h>\n\nnamespace Ui {\nclass BaseFindSearchDialog;\n}\n\nclass BaseFindSearchDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit BaseFindSearchDialog(QWidget *parent = nullptr);\n    ~BaseFindSearchDialog();\n\n    void show(RzBaseFindOpt *opts);\n\npublic slots:\n    void onProgress(BasefindCoreStatusDescription status);\n    void onCompletion();\n\nsignals:\n    void cancelSearch();\n\nprivate slots:\n    void on_buttonBox_rejected();\n\nprivate:\n    std::vector<QProgressBar *> bars;\n    std::unique_ptr<Basefind> basefind;\n    std::unique_ptr<Ui::BaseFindSearchDialog> ui;\n};\n\n#endif // BASEFIND_SEARCH_DIALOG_H\n"
  },
  {
    "path": "src/tools/basefind/BaseFindSearchDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BaseFindSearchDialog</class>\n <widget class=\"QDialog\" name=\"BaseFindSearchDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>582</width>\n    <height>382</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Searching for base address</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetMinimumSize</enum>\n   </property>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"mainVerticalLayout\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <item>\n      <widget class=\"QScrollArea\" name=\"scrollArea\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"sizeAdjustPolicy\">\n        <enum>QAbstractScrollArea::AdjustToContents</enum>\n       </property>\n       <property name=\"widgetResizable\">\n        <bool>true</bool>\n       </property>\n       <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n        <property name=\"geometry\">\n         <rect>\n          <x>0</x>\n          <y>0</y>\n          <width>564</width>\n          <height>320</height>\n         </rect>\n        </property>\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n       </widget>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Cancel</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n  <action name=\"actionRemoveItem\">\n   <property name=\"text\">\n    <string>Remove item</string>\n   </property>\n  </action>\n  <action name=\"actionRemoveAll\">\n   <property name=\"text\">\n    <string>Remove all</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Remove all</string>\n   </property>\n  </action>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>BaseFindSearchDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>240</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>254</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/widgets/AddressRangeScrollBar.cpp",
    "content": "#include \"AddressRangeScrollBar.h\"\n#include \"Cutter.h\"\n\n#include <QWheelEvent>\n\n#include <algorithm>\n#include <cstring>\n\nAddressRangeScrollBar::AddressRangeScrollBar(QWidget *parent) : QScrollBar(parent)\n{\n    connect(Core(), &CutterCore::refreshAll, this, &AddressRangeScrollBar::refreshRange);\n    connect(this, &AddressRangeScrollBar::actionTriggered, this, [this](int action) {\n        switch (action) {\n        // Due to the way the QScrollBar::actionTriggered signal works,\n        // setting the slider pos to its current value here\n        // prevents it from moving, allowing us to basically\n        // override behavior for specific actions\n        // See https://doc.qt.io/qt-6/qabstractslider.html#actionTriggered\n        // for more info.\n        case QAbstractSlider::SliderSingleStepAdd:\n            setSliderPosition(value());\n            if (value() == maximum())\n                return;\n            emit scrolled(-singleStep());\n            return;\n        case QAbstractSlider::SliderPageStepAdd:\n            setSliderPosition(value());\n            if (value() == maximum())\n                return;\n            emit scrolled(-pageStep());\n            return;\n        case QAbstractSlider::SliderSingleStepSub:\n            setSliderPosition(value());\n            if (value() == minimum())\n                return;\n            emit scrolled(singleStep());\n            return;\n        case QAbstractSlider::SliderPageStepSub:\n            setSliderPosition(value());\n            if (value() == minimum())\n                return;\n            emit scrolled(pageStep());\n            return;\n        default:\n            return;\n        }\n    });\n}\n\nvoid AddressRangeScrollBar::refreshRange()\n{\n    beginOffset = RVA_MAX;\n    endOffset = 0;\n    if (!Core()->currentlyEmulating && Core()->currentlyDebugging) {\n        QString currentlyOpenFile = Core()->getConfig(\"file.path\");\n        QList<MemoryMapDescription> memoryMaps = Core()->getMemoryMap();\n        for (const MemoryMapDescription &map : memoryMaps) {\n            if (map.fileName == currentlyOpenFile) {\n                if (map.addrStart < beginOffset) {\n                    beginOffset = map.addrStart;\n                }\n                if (map.addrEnd > endOffset) {\n                    endOffset = map.addrEnd;\n                }\n            }\n        }\n    } else {\n        RzCoreLocked core(Core());\n        RzPVector *mapsPtr = rz_io_maps(core->io);\n        if (!mapsPtr) {\n            emit hideScrollBar();\n            return;\n        }\n        CutterPVector<RzIOMap> maps { mapsPtr };\n        for (const RzIOMap *const map : maps) {\n            // Skip the ESIL memory stack region\n            if (Core()->currentlyEmulating && std::strncmp(rz_str_get(map->name), \"mem.\", 4) == 0) {\n                continue;\n            }\n            ut64 b = rz_itv_begin(map->itv);\n            ut64 e = rz_itv_end(map->itv);\n            if (b < beginOffset) {\n                beginOffset = b;\n            }\n            if (e > endOffset) {\n                endOffset = e;\n            }\n        }\n    }\n    if (endOffset) {\n        --endOffset;\n    }\n    if (endOffset == 0) {\n        beginOffset = 0;\n    }\n    setMinimum(0);\n    // Increasing this value increases scroll bar accuracy for small files but\n    // decreases it for large files\n    // Sufficiently below 2^32 to avoid causing problems in calculations done by QScrollbar,\n    // otherwise as high as possible to maximize range in which address map 1:1 to scrollbar pos.\n    const int rangeMax = 512 * 1024 * 1024;\n    if (rangeSize() > rangeMax) {\n        setMaximum(rangeMax);\n    } else {\n        setMaximum(rangeSize());\n    }\n    if (rangeSize()) {\n        emit showScrollBar();\n        return;\n    }\n    emit hideScrollBar();\n    return;\n}\n\nvoid AddressRangeScrollBar::setPosition(RVA address)\n{\n    const QSignalBlocker blocker(this);\n    if (!maximum() || !rangeSize()) {\n        emit hideScrollBar();\n        return;\n    }\n    int scrollBarPos = 0;\n    if (address < beginOffset) {\n        setValue(scrollBarPos);\n        emit showScrollBar();\n        return;\n    }\n    if (address > endOffset) {\n        setValue(maximum());\n        emit showScrollBar();\n        return;\n    }\n    auto offset = address - beginOffset;\n    if ((RVA_MAX / maximum()) < rangeSize()) {\n        // Fallback formula for large files\n        uint64_t smallBox = rangeSize() / maximum();\n        uint64_t extra = rangeSize() % maximum();\n        auto bigBoxRange = (smallBox + 1) * extra;\n        if (offset < bigBoxRange) {\n            scrollBarPos = offset / (smallBox + 1);\n        } else {\n            scrollBarPos = extra + (offset - bigBoxRange) / smallBox;\n        }\n    } else {\n        scrollBarPos = (maximum() * offset) / rangeSize();\n    }\n    if (address != beginOffset && scrollBarPos == 0) {\n        scrollBarPos = 1;\n    }\n    setValue(scrollBarPos);\n    emit showScrollBar();\n    return;\n}\n\nRVA AddressRangeScrollBar::address()\n{\n    if (!maximum() || !rangeSize()) {\n        return beginOffset;\n    }\n    // Fallback formula for large files\n    if ((RVA_MAX / maximum()) < rangeSize()) {\n        return value() * (rangeSize() / maximum()) + std::min<RVA>(value(), rangeSize() % maximum())\n                + beginOffset;\n    }\n    return (value() * rangeSize()) / maximum() + beginOffset;\n}\n\nRVA AddressRangeScrollBar::clampAddressToRange(RVA address)\n{\n    if (address > endOffset) {\n        return endOffset;\n    }\n    if (address < beginOffset) {\n        return beginOffset;\n    }\n    return address;\n}\n\nRVA AddressRangeScrollBar::rangeSize()\n{\n    return endOffset - beginOffset;\n}\n\nvoid AddressRangeScrollBar::showTransientScrollBar()\n{\n    const Qt::ScrollPhase phases[] = { Qt::ScrollBegin, Qt::ScrollEnd };\n    for (const auto &phase : phases) {\n#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)\n        QWheelEvent event(QPointF(0, 0), QPointF(0, 0), QPoint(0, 0), QPoint(0, 0), Qt::NoButton,\n                          Qt::NoModifier, phase, false);\n#else\n        QWheelEvent event(QPointF(0, 0), QPointF(0, 0), QPoint(0, 0), QPoint(0, 0), 0, Qt::Vertical,\n                          Qt::NoButton, Qt::NoModifier, phase);\n#endif\n        QScrollBar::wheelEvent(&event);\n    }\n}\n\nvoid AddressRangeScrollBar::wheelEvent(QWheelEvent *event)\n{\n    accumScrollWheelDeltaY += event->angleDelta().y();\n    // Delta is reported in 1/8 of a degree\n    // eg. 120 units * 1/8 = 15 degrees\n    // Typical scroll speed is 1 line per 5 degrees\n    const int lineDelta = 5 * 8;\n    if (accumScrollWheelDeltaY >= lineDelta || accumScrollWheelDeltaY <= -lineDelta) {\n        int lineCount = accumScrollWheelDeltaY / lineDelta;\n        accumScrollWheelDeltaY -= lineDelta * lineCount;\n        if ((lineCount < 0 && value() == maximum()) || (lineCount > 0 && value() == minimum())) {\n            return;\n        }\n        emit scrolled(lineCount);\n    }\n}\n"
  },
  {
    "path": "src/widgets/AddressRangeScrollBar.h",
    "content": "#ifndef ADDRESS_RANGE_SCROLLBAR_H\n#define ADDRESS_RANGE_SCROLLBAR_H\n\n#include <QScrollBar>\n#include \"CutterCommon.h\"\n\nclass AddressRangeScrollBar : public QScrollBar\n{\n    Q_OBJECT\npublic:\n    AddressRangeScrollBar(QWidget *parent = nullptr);\n    void refreshRange();\n    void setPosition(RVA address);\n    RVA address();\n\n    RVA clampAddressToRange(RVA address);\n    RVA rangeSize();\n\n    /**\n     * @brief Sends fake wheelEvent to the parent QScrollBar\n     *\n     * This allows external widgets (like side panels or text edits) to notify the\n     * scrollbar of wheel activity. It is needed for systems (like MacOS) which have the option to\n     * enable transient scrollbars meaning the ability to show the scrollbar only while scrolling\n     * and hiding it later (when using Cutter's \"Native\" theme)\n     */\n    void showTransientScrollBar();\nsignals:\n    void hideScrollBar();\n    void showScrollBar();\n    void scrolled(int lines);\n\nprotected:\n    void wheelEvent(QWheelEvent *event) override;\n\nprivate:\n    RVA beginOffset = 0, endOffset = RVA_INVALID;\n    int accumScrollWheelDeltaY = 0;\n};\n\n#endif // ADDRESS_RANGE_SCROLLBAR_H\n"
  },
  {
    "path": "src/widgets/AddressableDockWidget.cpp",
    "content": "#include \"AddressableDockWidget.h\"\n#include \"common/CutterSeekable.h\"\n#include \"MainWindow.h\"\n#include <QAction>\n#include <QEvent>\n#include <QMenu>\n#include <QContextMenuEvent>\n\nAddressableDockWidget::AddressableDockWidget(MainWindow *parent)\n    : CutterDockWidget(parent),\n      seekable(new CutterSeekable(this)),\n      syncAction(tr(\"Sync/unsync offset\"), this)\n{\n    connect(seekable, &CutterSeekable::syncChanged, this,\n            &AddressableDockWidget::updateWindowTitle);\n    connect(&syncAction, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);\n\n    dockMenu = new QMenu(this);\n    dockMenu->addAction(&syncAction);\n\n    setContextMenuPolicy(Qt::ContextMenuPolicy::DefaultContextMenu);\n}\n\nQVariantMap AddressableDockWidget::serializeViewProprties()\n{\n    auto result = CutterDockWidget::serializeViewProprties();\n    result[\"synchronized\"] = seekable->isSynchronized();\n    return result;\n}\n\nvoid AddressableDockWidget::deserializeViewProperties(const QVariantMap &properties)\n{\n    QVariant synchronized = properties.value(\"synchronized\", true);\n    seekable->setSynchronization(synchronized.toBool());\n}\n\nvoid AddressableDockWidget::updateWindowTitle()\n{\n    QString name = getWindowTitle();\n    QString id = getDockNumber();\n    if (!id.isEmpty()) {\n        name += \" \" + id;\n    }\n    if (!seekable->isSynchronized()) {\n        name += CutterSeekable::tr(\" (unsynced)\");\n    }\n    setWindowTitle(name);\n}\n\nvoid AddressableDockWidget::contextMenuEvent(QContextMenuEvent *event)\n{\n    event->accept();\n    dockMenu->exec(mapToGlobal(event->pos()));\n}\n\nCutterSeekable *AddressableDockWidget::getSeekable() const\n{\n    return seekable;\n}\n"
  },
  {
    "path": "src/widgets/AddressableDockWidget.h",
    "content": "#ifndef ADDRESSABLE_DOCK_WIDGET_H\n#define ADDRESSABLE_DOCK_WIDGET_H\n\n#include \"CutterDockWidget.h\"\n#include \"core/Cutter.h\"\n\n#include <QAction>\n\nclass CutterSeekable;\n\nclass AddressableDockWidget : public CutterDockWidget\n{\n    Q_OBJECT\npublic:\n    AddressableDockWidget(MainWindow *parent);\n    ~AddressableDockWidget() override {}\n\n    CutterSeekable *getSeekable() const;\n\n    QVariantMap serializeViewProprties() override;\n    void deserializeViewProperties(const QVariantMap &properties) override;\npublic slots:\n    void updateWindowTitle();\n\nprotected:\n    CutterSeekable *seekable = nullptr;\n    QAction syncAction;\n    QMenu *dockMenu = nullptr;\n\n    virtual QString getWindowTitle() const = 0;\n    void contextMenuEvent(QContextMenuEvent *event) override;\n};\n\n#endif // ADDRESSABLE_DOCK_WIDGET_H\n"
  },
  {
    "path": "src/widgets/AddressableItemList.h",
    "content": "#ifndef ADDRESSABLE_ITEM_LIST_H\n#define ADDRESSABLE_ITEM_LIST_H\n\n#include <memory>\n#include <QAbstractItemModel>\n#include <QSortFilterProxyModel>\n#include <QAbstractItemView>\n#include <QMenu>\n\n#include \"core/Cutter.h\"\n#include \"common/AddressableItemModel.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeWidget.h\"\n#include \"menus/AddressableItemContextMenu.h\"\n#include \"CutterTreeView.h\"\n\nclass MainWindow;\n\ntemplate<class BaseListWidget = CutterTreeView>\nclass AddressableItemList : public BaseListWidget\n{\n    static_assert(std::is_base_of<QAbstractItemView, BaseListWidget>::value,\n                  \"ParentModel needs to inherit from QAbstractItemModel\");\n\npublic:\n    explicit AddressableItemList(QWidget *parent = nullptr) : BaseListWidget(parent)\n    {\n        this->connect(this, &QWidget::customContextMenuRequested, this,\n                      &AddressableItemList<BaseListWidget>::showItemContextMenu);\n        this->setContextMenuPolicy(Qt::CustomContextMenu);\n        this->connect(this, &QAbstractItemView::activated, this,\n                      &AddressableItemList<BaseListWidget>::onItemActivated);\n    }\n\n    void setModel(AddressableItemModelI *addressableItemModel)\n    {\n        this->addressableModel = addressableItemModel;\n\n        BaseListWidget::setModel(this->addressableModel->asItemModel());\n\n        this->connect(this->selectionModel(), &QItemSelectionModel::currentChanged, this,\n                      &AddressableItemList<BaseListWidget>::onSelectedItemChanged);\n    }\n    void setMainWindow(MainWindow *mainWindow)\n    {\n        this->mainWindow = mainWindow;\n        setItemContextMenu(new AddressableItemContextMenu(this, mainWindow));\n        this->addActions(this->getItemContextMenu()->actions());\n    }\n\n    AddressableItemContextMenu *getItemContextMenu() { return itemContextMenu; }\n    void setItemContextMenu(AddressableItemContextMenu *menu)\n    {\n        if (itemContextMenu != menu && itemContextMenu) {\n            itemContextMenu->deleteLater();\n        }\n        itemContextMenu = menu;\n    }\n\n    /**\n     * If this is set to true, the context menu will also be shown if no item\n     * is currently selected.\n     */\n    void setShowItemContextMenuWithoutAddress(bool val) { showItemContextMenuWithoutAddress = val; }\n\nprotected:\n    virtual void showItemContextMenu(const QPoint &pt)\n    {\n        if (!itemContextMenu) {\n            return;\n        }\n        auto index = this->currentIndex();\n        if (index.isValid()) {\n            auto offset = addressableModel->address(index);\n            auto name = addressableModel->name(index);\n            itemContextMenu->setTarget(offset, name);\n        } else {\n            if (!showItemContextMenuWithoutAddress) {\n                return;\n            }\n            itemContextMenu->clearTarget();\n        }\n        itemContextMenu->exec(this->mapToGlobal(pt));\n    }\n\n    virtual void onItemActivated(const QModelIndex &index)\n    {\n        if (!index.isValid())\n            return;\n\n        auto offset = addressableModel->address(index);\n        Core()->seekAndShow(offset);\n    }\n    virtual void onSelectedItemChanged(const QModelIndex &index) { updateMenuFromItem(index); }\n    void updateMenuFromItem(const QModelIndex &index)\n    {\n        if (index.isValid()) {\n            auto offset = addressableModel->address(index);\n            auto name = addressableModel->name(index);\n            itemContextMenu->setTarget(offset, name);\n        } else {\n            itemContextMenu->clearTarget();\n        }\n    }\n\nprivate:\n    bool showItemContextMenuWithoutAddress = false;\n    AddressableItemModelI *addressableModel = nullptr;\n    AddressableItemContextMenu *itemContextMenu = nullptr;\n    MainWindow *mainWindow = nullptr;\n};\n\n#endif // ADDRESSABLE_ITEM_LIST_H\n"
  },
  {
    "path": "src/widgets/BacktraceWidget.cpp",
    "content": "#include \"BacktraceWidget.h\"\n#include \"ui_BacktraceWidget.h\"\n#include \"common/JsonModel.h\"\n#include \"QHeaderView\"\n\n#include \"core/MainWindow.h\"\n\nBacktraceWidget::BacktraceWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::BacktraceWidget)\n{\n    ui->setupUi(this);\n\n    // setup backtrace model\n    QString PC = Core()->getRegisterName(\"PC\");\n    QString SP = Core()->getRegisterName(\"SP\");\n    modelBacktrace->setHorizontalHeaderItem(0, new QStandardItem(tr(\"Function\")));\n    modelBacktrace->setHorizontalHeaderItem(1, new QStandardItem(SP));\n    modelBacktrace->setHorizontalHeaderItem(2, new QStandardItem(PC));\n    modelBacktrace->setHorizontalHeaderItem(3, new QStandardItem(tr(\"Description\")));\n    modelBacktrace->setHorizontalHeaderItem(4, new QStandardItem(tr(\"Frame Size\")));\n    viewBacktrace->setFont(Config()->getFont());\n    viewBacktrace->setModel(modelBacktrace);\n    viewBacktrace->verticalHeader()->setVisible(false);\n    viewBacktrace->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);\n    ui->verticalLayout->addWidget(viewBacktrace);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { updateContents(); });\n\n    connect(Core(), &CutterCore::refreshAll, this, &BacktraceWidget::updateContents);\n    connect(Core(), &CutterCore::registersChanged, this, &BacktraceWidget::updateContents);\n    connect(Config(), &Configuration::fontsUpdated, this, &BacktraceWidget::fontsUpdatedSlot);\n}\n\nBacktraceWidget::~BacktraceWidget() {}\n\nvoid BacktraceWidget::updateContents()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr) || Core()->isDebugTaskInProgress()) {\n        return;\n    }\n\n    setBacktraceGrid();\n}\n\nvoid BacktraceWidget::setBacktraceGrid()\n{\n    auto core = Core()->lock();\n    RzList *list = rz_core_debug_backtraces(core);\n    int i = 0;\n    RzListIter *iter;\n    RzBacktrace *bt;\n    CutterRzListForeach (list, iter, RzBacktrace, bt) {\n        QString funcName = bt->fcn ? bt->fcn->name : \"\";\n        QString pc = RzAddressString(bt->frame ? bt->frame->addr : 0);\n        QString sp = RzAddressString(bt->frame ? bt->frame->sp : 0);\n        QString frameSize = QString::number(bt->frame ? bt->frame->size : 0);\n        QString desc = bt->desc;\n\n        modelBacktrace->setItem(i, 0, new QStandardItem(funcName));\n        modelBacktrace->setItem(i, 1, new QStandardItem(sp));\n        modelBacktrace->setItem(i, 2, new QStandardItem(pc));\n        modelBacktrace->setItem(i, 3, new QStandardItem(desc));\n        modelBacktrace->setItem(i, 4, new QStandardItem(frameSize));\n        ++i;\n    }\n    rz_list_free(list);\n\n    // Remove irrelevant old rows\n    if (modelBacktrace->rowCount() > i) {\n        modelBacktrace->removeRows(i, modelBacktrace->rowCount() - i);\n    }\n\n    viewBacktrace->setModel(modelBacktrace);\n    viewBacktrace->resizeColumnsToContents();\n}\n\nvoid BacktraceWidget::fontsUpdatedSlot()\n{\n    viewBacktrace->setFont(Config()->getFont());\n}\n"
  },
  {
    "path": "src/widgets/BacktraceWidget.h",
    "content": "#pragma once\n\n#include <QJsonObject>\n#include <memory>\n#include <QStandardItem>\n#include <QTableView>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n\nclass MainWindow;\n\nnamespace Ui {\nclass BacktraceWidget;\n}\n\nclass BacktraceWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit BacktraceWidget(MainWindow *main);\n    ~BacktraceWidget();\n\nprivate slots:\n    void updateContents();\n    void setBacktraceGrid();\n    void fontsUpdatedSlot();\n\nprivate:\n    std::unique_ptr<Ui::BacktraceWidget> ui;\n    QStandardItemModel *modelBacktrace = new QStandardItemModel(1, 5, this);\n    QTableView *viewBacktrace = new QTableView(this);\n    RefreshDeferrer *refreshDeferrer;\n};\n"
  },
  {
    "path": "src/widgets/BacktraceWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BacktraceWidget</class>\n <widget class=\"QDockWidget\" name=\"BacktraceWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>463</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Backtrace</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n      <property name=\"spacing\">\n       <number>10</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n     </layout>\n  </widget>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/BoolToggleDelegate.cpp",
    "content": "#include \"BoolToggleDelegate.h\"\n#include <QEvent>\n\nBoolTogggleDelegate::BoolTogggleDelegate(QObject *parent) : QStyledItemDelegate(parent) {}\n\nQWidget *BoolTogggleDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,\n                                           const QModelIndex &index) const\n{\n    if (index.data(Qt::EditRole).type() == QVariant::Bool) {\n        return nullptr;\n    }\n    return QStyledItemDelegate::createEditor(parent, option, index);\n}\n\nbool BoolTogggleDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,\n                                      const QStyleOptionViewItem &option, const QModelIndex &index)\n{\n    if (model->flags(index).testFlag(Qt::ItemFlag::ItemIsEditable)) {\n        if (event->type() == QEvent::MouseButtonDblClick) {\n            auto data = index.data(Qt::EditRole);\n            if (data.type() == QVariant::Bool) {\n                model->setData(index, !data.toBool());\n                return true;\n            }\n        }\n    }\n    return QStyledItemDelegate::editorEvent(event, model, option, index);\n}\n"
  },
  {
    "path": "src/widgets/BoolToggleDelegate.h",
    "content": "#ifndef BOOLTOGGGLEDELEGATE_H\n#define BOOLTOGGGLEDELEGATE_H\n\n#include \"core/CutterCommon.h\"\n\n#include <QStyledItemDelegate>\n\nclass CUTTER_EXPORT BoolTogggleDelegate : public QStyledItemDelegate\n{\npublic:\n    BoolTogggleDelegate(QObject *parent = nullptr);\n\n    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,\n                          const QModelIndex &index) const override;\n\n    bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,\n                     const QModelIndex &index) override;\n};\n\n#endif // BOOLTOGGGLEDELEGATE_H\n"
  },
  {
    "path": "src/widgets/BreakpointWidget.cpp",
    "content": "#include \"BreakpointWidget.h\"\n#include \"ui_BreakpointWidget.h\"\n#include \"dialogs/BreakpointsDialog.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"widgets/BoolToggleDelegate.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include <QMenu>\n#include <QStyledItemDelegate>\n#include <QCheckBox>\n\nBreakpointModel::BreakpointModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent)\n{\n}\n\nvoid BreakpointModel::refresh()\n{\n    beginResetModel();\n    breakpoints = Core()->getBreakpoints();\n    endResetModel();\n}\n\nint BreakpointModel::rowCount(const QModelIndex &) const\n{\n    return breakpoints.count();\n}\n\nint BreakpointModel::columnCount(const QModelIndex &) const\n{\n    return BreakpointModel::ColumnCount;\n}\n\nstatic QString formatHwBreakpoint(int permission)\n{\n    char data[] = \"rwx\";\n    if ((permission & (RZ_PERM_R | RZ_PERM_RW)) == 0) {\n        data[0] = '-';\n    }\n    if ((permission & (RZ_PERM_W | RZ_PERM_RW)) == 0) {\n        data[1] = '-';\n    }\n    if ((permission & RZ_PERM_X) == 0) {\n        data[2] = '-';\n    }\n    return data;\n}\n\nQVariant BreakpointModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= breakpoints.count())\n        return QVariant();\n\n    const BreakpointDescription &breakpoint = breakpoints.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case AddrColumn:\n            return RzAddressString(breakpoint.addr);\n        case NameColumn:\n            return breakpoint.name;\n        case TypeColumn:\n\n            if (breakpoint.hw) {\n                return tr(\"HW %1\").arg(formatHwBreakpoint(breakpoint.permission));\n            } else {\n                return tr(\"SW\");\n            }\n        case TraceColumn:\n            return breakpoint.trace;\n        case EnabledColumn:\n            return breakpoint.enabled;\n        case CommentColumn:\n            return Core()->getCommentAt(breakpoint.addr);\n        default:\n            return QVariant();\n        }\n    case Qt::EditRole:\n        switch (index.column()) {\n        case AddrColumn:\n            return breakpoint.addr;\n        case TraceColumn:\n            return breakpoint.trace;\n        case EnabledColumn:\n            return breakpoint.enabled;\n        default:\n            return data(index, Qt::DisplayRole);\n        }\n    case BreakpointDescriptionRole:\n        return QVariant::fromValue(breakpoint);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant BreakpointModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case AddrColumn:\n            return tr(\"Offset\");\n        case NameColumn:\n            return tr(\"Name\");\n        case TypeColumn:\n            return tr(\"Type\");\n        case TraceColumn:\n            return tr(\"Tracing\");\n        case EnabledColumn:\n            return tr(\"Enabled\");\n        case CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nbool BreakpointModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n    if (index.row() >= breakpoints.count())\n        return false;\n\n    BreakpointDescription &breakpoint = breakpoints[index.row()];\n\n    switch (role) {\n    case Qt::EditRole:\n        switch (index.column()) {\n        case TraceColumn:\n            breakpoint.trace = value.toBool();\n            Core()->setBreakpointTrace(breakpoint.index, breakpoint.trace);\n            emit dataChanged(index, index, { role, Qt::DisplayRole });\n            return true;\n        case EnabledColumn:\n            breakpoint.enabled = value.toBool();\n            if (breakpoint.enabled) {\n                Core()->enableBreakpoint(breakpoint.addr);\n            } else {\n                Core()->disableBreakpoint(breakpoint.addr);\n            }\n            emit dataChanged(index, index, { role, Qt::DisplayRole });\n            return true;\n        default:\n            return false;\n        }\n\n    default:\n        return false;\n    }\n}\n\nQt::ItemFlags BreakpointModel::flags(const QModelIndex &index) const\n{\n    switch (index.column()) {\n    case TraceColumn:\n        return AddressableItemModel::flags(index) | Qt::ItemFlag::ItemIsEditable;\n    case EnabledColumn:\n        return AddressableItemModel::flags(index) | Qt::ItemFlag::ItemIsEditable;\n    default:\n        return AddressableItemModel::flags(index);\n    }\n}\n\nRVA BreakpointModel::address(const QModelIndex &index) const\n{\n    if (index.row() < breakpoints.count()) {\n        return breakpoints.at(index.row()).addr;\n    }\n    return RVA_INVALID;\n}\n\nBreakpointProxyModel::BreakpointProxyModel(BreakpointModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    // Use numeric values instead of numbers converted to strings if available\n    this->setSortRole(Qt::EditRole);\n}\n\nBreakpointWidget::BreakpointWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::BreakpointWidget)\n{\n    ui->setupUi(this);\n\n    ui->breakpointTreeView->setMainWindow(mainWindow);\n    breakpointModel = new BreakpointModel(this);\n    breakpointProxyModel = new BreakpointProxyModel(breakpointModel, this);\n    ui->breakpointTreeView->setModel(breakpointProxyModel);\n    ui->breakpointTreeView->sortByColumn(BreakpointModel::AddrColumn, Qt::AscendingOrder);\n    ui->breakpointTreeView->setItemDelegate(new BoolTogggleDelegate(this));\n\n    refreshDeferrer = createRefreshDeferrer([this]() { refreshBreakpoint(); });\n\n    setScrollMode();\n\n    actionDelBreakpoint = Shortcuts()->makeAction(\"Breakpoint.delBreakpoint\", this);\n    actionDelBreakpoint->setShortcutContext(Qt::WidgetShortcut);\n    connect(actionDelBreakpoint, &QAction::triggered, this, &BreakpointWidget::delBreakpoint);\n    ui->breakpointTreeView->addAction(actionDelBreakpoint);\n\n    actionToggleBreakpoint = Shortcuts()->makeAction(\"Breakpoint.toggleBreakpoint\", this);\n    actionToggleBreakpoint->setShortcutContext(Qt::WidgetShortcut);\n    connect(actionToggleBreakpoint, &QAction::triggered, this, &BreakpointWidget::toggleBreakpoint);\n    ui->breakpointTreeView->addAction(actionToggleBreakpoint);\n\n    actionEditBreakpoint = new QAction(tr(\"Edit\"), this);\n    connect(actionEditBreakpoint, &QAction::triggered, this, &BreakpointWidget::editBreakpoint);\n\n    auto contextMenu = ui->breakpointTreeView->getItemContextMenu();\n    contextMenu->addAction(actionEditBreakpoint);\n    contextMenu->addAction(actionToggleBreakpoint);\n    contextMenu->addAction(actionDelBreakpoint);\n\n    connect(Core(), &CutterCore::refreshAll, this, &BreakpointWidget::refreshBreakpoint);\n    connect(Core(), &CutterCore::breakpointsChanged, this, &BreakpointWidget::refreshBreakpoint);\n    connect(Core(), &CutterCore::codeRebased, this, &BreakpointWidget::refreshBreakpoint);\n    connect(Core(), &CutterCore::refreshCodeViews, this, &BreakpointWidget::refreshBreakpoint);\n    connect(Core(), &CutterCore::commentsChanged, this, [this]() {\n        qhelpers::emitColumnChanged(breakpointModel, BreakpointModel::CommentColumn);\n    });\n    connect(ui->addBreakpoint, &QAbstractButton::clicked, this,\n            &BreakpointWidget::addBreakpointDialog);\n    connect(ui->delBreakpoint, &QAbstractButton::clicked, this, &BreakpointWidget::delBreakpoint);\n    connect(ui->delAllBreakpoints, &QAbstractButton::clicked, Core(),\n            &CutterCore::delAllBreakpoints);\n}\n\nBreakpointWidget::~BreakpointWidget() = default;\n\nvoid BreakpointWidget::refreshBreakpoint()\n{\n    if (editing || !refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    breakpointModel->refresh();\n\n    ui->breakpointTreeView->resizeColumnToContents(0);\n    ui->breakpointTreeView->resizeColumnToContents(1);\n    ui->breakpointTreeView->resizeColumnToContents(2);\n}\n\nvoid BreakpointWidget::setScrollMode()\n{\n    qhelpers::setVerticalScrollMode(ui->breakpointTreeView);\n}\n\nvoid BreakpointWidget::addBreakpointDialog()\n{\n    BreakpointsDialog::createNewBreakpoint(RVA_INVALID, this);\n}\n\nQVector<RVA> BreakpointWidget::getSelectedAddresses() const\n{\n    auto selection = ui->breakpointTreeView->selectionModel()->selectedRows();\n    QVector<RVA> breakpointAddressese(selection.count());\n    int index = 0;\n    for (auto row : selection) {\n        breakpointAddressese[index++] = breakpointProxyModel->address(row);\n    }\n    return breakpointAddressese;\n}\n\nvoid BreakpointWidget::delBreakpoint()\n{\n    auto breakpointsToRemove = getSelectedAddresses();\n    for (auto address : breakpointsToRemove) {\n        Core()->delBreakpoint(address);\n    }\n}\n\nvoid BreakpointWidget::toggleBreakpoint()\n{\n    auto selection = ui->breakpointTreeView->selectionModel()->selectedRows();\n    editing = true;\n    for (auto row : selection) {\n        auto cell = breakpointProxyModel->index(row.row(), BreakpointModel::EnabledColumn);\n        breakpointProxyModel->setData(cell, !cell.data(Qt::EditRole).toBool());\n    }\n    editing = false;\n}\n\nvoid BreakpointWidget::editBreakpoint()\n{\n    auto index = ui->breakpointTreeView->currentIndex();\n    if (index.isValid()) {\n        auto data = breakpointProxyModel->data(index, BreakpointModel::BreakpointDescriptionRole);\n        if (!data.isNull()) {\n            auto breakpoint = data.value<BreakpointDescription>();\n            BreakpointsDialog::editBreakpoint(breakpoint, this);\n        }\n    }\n}\n"
  },
  {
    "path": "src/widgets/BreakpointWidget.h",
    "content": "#pragma once\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"AddressableItemModel.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidget;\n\nnamespace Ui {\nclass BreakpointWidget;\n}\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass BreakpointWidget;\n\nclass BreakpointModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend BreakpointWidget;\n\nprivate:\n    QList<BreakpointDescription> breakpoints;\n\npublic:\n    enum Column {\n        AddrColumn = 0,\n        NameColumn,\n        TypeColumn,\n        TraceColumn,\n        EnabledColumn,\n        CommentColumn,\n        ColumnCount\n    };\n    enum Role { BreakpointDescriptionRole = Qt::UserRole };\n\n    BreakpointModel(QObject *parent = nullptr);\n\n    void refresh();\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;\n    Qt::ItemFlags flags(const QModelIndex &index) const override;\n\n    RVA address(const QModelIndex &index) const override;\n};\n\nclass BreakpointProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    BreakpointProxyModel(BreakpointModel *sourceModel, QObject *parent = nullptr);\n};\n\nclass BreakpointWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit BreakpointWidget(MainWindow *main);\n    ~BreakpointWidget();\n\nprivate slots:\n    void delBreakpoint();\n    void toggleBreakpoint();\n    void editBreakpoint();\n    void addBreakpointDialog();\n    void refreshBreakpoint();\n\nprivate:\n    std::unique_ptr<Ui::BreakpointWidget> ui;\n\n    BreakpointModel *breakpointModel;\n    BreakpointProxyModel *breakpointProxyModel;\n    QAction *actionDelBreakpoint = nullptr;\n    QAction *actionToggleBreakpoint = nullptr;\n    QAction *actionEditBreakpoint = nullptr;\n\n    void setScrollMode();\n    QVector<RVA> getSelectedAddresses() const;\n\n    RefreshDeferrer *refreshDeferrer;\n    bool editing = false;\n};\n"
  },
  {
    "path": "src/widgets/BreakpointWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BreakpointWidget</class>\n <widget class=\"QDockWidget\" name=\"BreakpointWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Breakpoints</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"AddressableItemList&lt;&gt;\" name=\"breakpointTreeView\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"hLayout\">\n       <item>\n       <widget class=\"QToolButton\" name=\"addBreakpoint\">\n        <property name=\"text\">\n         <string>Add new breakpoint</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QToolButton\" name=\"delBreakpoint\">\n        <property name=\"text\">\n         <string>Delete breakpoint</string>\n        </property>\n       </widget>\n      </item>\n      <item alignment=\"Qt::AlignLeft\">\n       <widget class=\"QToolButton\" name=\"delAllBreakpoints\">\n        <property name=\"text\">\n         <string>Delete all breakpoints</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>AddressableItemList&lt;&gt;</class>\n   <extends>QTreeView</extends>\n   <header>widgets/AddressableItemList.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/CallGraph.cpp",
    "content": "#include \"CallGraph.h\"\n\n#include \"MainWindow.h\"\n\n#include <QJsonValue>\n#include <QJsonArray>\n#include <QJsonObject>\n\nCallGraphWidget::CallGraphWidget(MainWindow *main, bool global)\n    : MemoryDockWidget(MemoryWidgetType::CallGraph, main),\n      graphView(new CallGraphView(this, main, global)),\n      global(global)\n{\n    setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());\n    this->setWindowTitle(getWindowTitle());\n    connect(seekable, &CutterSeekable::seekableSeekChanged, this, &CallGraphWidget::onSeekChanged);\n\n    setWidget(graphView);\n}\n\nCallGraphWidget::~CallGraphWidget() {}\n\nQString CallGraphWidget::getWindowTitle() const\n{\n    return global ? tr(\"Global Callgraph\") : tr(\"Callgraph\");\n}\n\nQString CallGraphWidget::getWidgetType() const\n{\n    return global ? tr(\"GlobalCallgraph\") : tr(\"Callgraph\");\n}\n\nvoid CallGraphWidget::onSeekChanged(RVA address)\n{\n    if (auto function = Core()->functionIn(address)) {\n        graphView->showAddress(function->addr);\n    }\n}\n\nCallGraphView::CallGraphView(CutterDockWidget *parent, MainWindow *main, bool global)\n    : SimpleTextGraphView(parent, main), global(global), refreshDeferrer(nullptr, this)\n{\n    enableAddresses(true);\n    addressableItemContextMenu.toggleBreakpointAction(true);\n    refreshDeferrer.registerFor(parent);\n    connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this, &CallGraphView::refreshView);\n    connect(Core(), &CutterCore::refreshAll, this, &SimpleTextGraphView::refreshView);\n    connect(Core(), &CutterCore::functionRenamed, this, &CallGraphView::refreshView);\n}\n\nvoid CallGraphView::showExportDialog()\n{\n    QString defaultName;\n    if (global) {\n        defaultName = \"global_callgraph\";\n    } else {\n        defaultName = QString(\"callgraph_%1\").arg(RzAddressString(address));\n    }\n    showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_FUNCALL, global ? RVA_INVALID : address);\n}\n\nvoid CallGraphView::showAddress(RVA address)\n{\n    if (global) {\n        selectBlockWithId(address);\n        showBlock(blocks[address]);\n    } else if (address != this->address) {\n        this->address = address;\n        refreshView();\n    }\n}\n\nvoid CallGraphView::refreshView()\n{\n    if (!refreshDeferrer.attemptRefresh(nullptr)) {\n        return;\n    }\n    SimpleTextGraphView::refreshView();\n}\n\nstatic inline bool isBetween(ut64 a, ut64 x, ut64 b)\n{\n    return (a == UT64_MAX || a <= x) && (b == UT64_MAX || x <= b);\n}\n\nvoid CallGraphView::loadCurrentGraph()\n{\n    blockContent.clear();\n    blocks.clear();\n\n    const ut64 from = Core()->getConfigi(\"graph.from\");\n    const ut64 to = Core()->getConfigi(\"graph.to\");\n    const bool usenames = Core()->getConfigb(\"graph.json.usenames\");\n\n    auto edges = std::unordered_set<ut64> {};\n    auto addFunction = [&](RzAnalysisFunction *fcn) {\n        GraphLayout::GraphBlock block;\n        block.entry = fcn->addr;\n\n        auto xrefs = fromOwned(rz_analysis_function_get_xrefs_from(fcn));\n        auto calls = std::unordered_set<ut64>();\n        for (const auto &xref : CutterRzList<RzAnalysisXRef>(xrefs.get())) {\n            const auto x = xref->to;\n            if (!(xref->type == RZ_ANALYSIS_XREF_TYPE_CALL && calls.find(x) == calls.end())) {\n                continue;\n            }\n            calls.insert(x);\n            block.edges.emplace_back(x);\n            edges.insert(x);\n        }\n\n        QString name = usenames ? fcn->name : RzAddressString(fcn->addr);\n        addBlock(std::move(block), name, fcn->addr);\n    };\n\n    auto core = Core()->lock();\n    if (global) {\n        for (const auto &fcn : CutterRzList<RzAnalysisFunction>(core->analysis->fcns)) {\n            if (!isBetween(from, fcn->addr, to)) {\n                continue;\n            }\n            addFunction(fcn);\n        }\n    } else {\n        const auto &fcn = Core()->functionIn(address);\n        if (fcn) {\n            addFunction(fcn);\n        }\n    }\n\n    for (const auto &x : edges) {\n        if (blockContent.find(x) != blockContent.end()) {\n            continue;\n        }\n        GraphLayout::GraphBlock block;\n        block.entry = x;\n        QString flagName = Core()->flagAt(x);\n        QString name = usenames\n                ? (!flagName.isEmpty() ? flagName : QString(\"unk.%0\").arg(RzAddressString(x)))\n                : RzAddressString(x);\n        addBlock(std::move(block), name, x);\n    }\n    if (blockContent.empty() && !global) {\n        const auto name = RzAddressString(address);\n        addBlock({}, name, address);\n    }\n\n    computeGraphPlacement();\n}\n\nvoid CallGraphView::restoreCurrentBlock()\n{\n    if (!global && lastLoadedAddress != address) {\n        selectedBlock = NO_BLOCK_SELECTED;\n        lastLoadedAddress = address;\n        center();\n    } else {\n        SimpleTextGraphView::restoreCurrentBlock();\n    }\n}\n"
  },
  {
    "path": "src/widgets/CallGraph.h",
    "content": "#ifndef CALL_GRAPH_WIDGET_H\n#define CALL_GRAPH_WIDGET_H\n\n#include \"core/Cutter.h\"\n#include \"MemoryDockWidget.h\"\n#include \"widgets/SimpleTextGraphView.h\"\n#include \"common/RefreshDeferrer.h\"\n\nclass MainWindow;\n/**\n * @brief Graphview displaying either global or function callgraph.\n */\nclass CallGraphView : public SimpleTextGraphView\n{\n    Q_OBJECT\npublic:\n    CallGraphView(CutterDockWidget *parent, MainWindow *main, bool global);\n    void showExportDialog() override;\n    void showAddress(RVA address);\n    void refreshView() override;\n\nprotected:\n    bool global; ///< is this a global or function callgraph\n    RVA address = RVA_INVALID; ///< function address if this is not a global callgraph\n    void loadCurrentGraph() override;\n    void restoreCurrentBlock() override;\n\nprivate:\n    RefreshDeferrer refreshDeferrer;\n    RVA lastLoadedAddress = RVA_INVALID;\n};\n\nclass CallGraphWidget : public MemoryDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit CallGraphWidget(MainWindow *main, bool global);\n    ~CallGraphWidget();\n\n    QString getWidgetType() const;\n\nprotected:\n    QString getWindowTitle() const override;\n\nprivate:\n    CallGraphView *graphView;\n    bool global;\n\n    void onSeekChanged(RVA address);\n};\n\n#endif // CALL_GRAPH_WIDGET_H\n"
  },
  {
    "path": "src/widgets/ClassesWidget.cpp",
    "content": "#include \"ClassesWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"common/Helpers.h\"\n#include \"common/SvgIconEngine.h\"\n#include \"dialogs/EditMethodDialog.h\"\n\n#include <QList>\n#include <QMenu>\n#include <QMouseEvent>\n#include <QInputDialog>\n#include <QShortcut>\n#include <QComboBox>\n\nQVariant ClassesModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case NAME:\n            return tr(\"Name\");\n        case REAL_NAME:\n            return tr(\"Real Name\");\n        case TYPE:\n            return tr(\"Type\");\n        case OFFSET:\n            return tr(\"Offset\");\n        case VTABLE:\n            return tr(\"VTable\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA ClassesModel::address(const QModelIndex &index) const\n{\n    QVariant v = data(index, OffsetRole);\n    return v.isValid() ? v.toULongLong() : RVA_INVALID;\n}\n\nQString ClassesModel::name(const QModelIndex &index) const\n{\n    return data(index, NameRole).toString();\n}\n\nBinClassesModel::BinClassesModel(QObject *parent) : ClassesModel(parent) {}\n\nvoid BinClassesModel::setClasses(const QList<BinClassDescription> &classes)\n{\n    beginResetModel();\n    this->classes = classes;\n    endResetModel();\n}\n\nQModelIndex BinClassesModel::index(int row, int column, const QModelIndex &parent) const\n{\n    if (!parent.isValid()) {\n        return createIndex(row, column, (quintptr)0); // root function nodes have id = 0\n    }\n\n    return createIndex(row, column,\n                       (quintptr)parent.row() + 1); // sub-nodes have id = class index + 1\n}\n\nQModelIndex BinClassesModel::parent(const QModelIndex &index) const\n{\n    if (!index.isValid()) {\n        return {};\n    }\n\n    if (index.internalId() == 0) { // root function node\n        return {};\n    } else { // sub-node\n        return this->index((int)(index.internalId() - 1), 0);\n    }\n}\n\nint BinClassesModel::rowCount(const QModelIndex &parent) const\n{\n    if (!parent.isValid()) { // root\n        return classes.count();\n    }\n\n    if (parent.internalId() == 0) { // methods/fields\n        const BinClassDescription *cls = &classes.at(parent.row());\n        return cls->baseClasses.length() + cls->methods.length() + cls->fields.length();\n    }\n\n    return 0; // below methods/fields\n}\n\nint BinClassesModel::columnCount(const QModelIndex &) const\n{\n    return Columns::COUNT;\n}\n\nQVariant BinClassesModel::data(const QModelIndex &index, int role) const\n{\n    const BinClassDescription *cls;\n    const BinClassMethodDescription *meth = nullptr;\n    const BinClassFieldDescription *field = nullptr;\n    const BinClassBaseClassDescription *base = nullptr;\n    if (index.internalId() == 0) { // class row\n        if (index.row() >= classes.count()) {\n            return QVariant();\n        }\n\n        cls = &classes.at(index.row());\n    } else { // method/field/base row\n        cls = &classes.at(static_cast<int>(index.internalId() - 1));\n\n        if (index.row()\n            >= cls->baseClasses.length() + cls->methods.length() + cls->fields.length()) {\n            return QVariant();\n        }\n\n        if (index.row() < cls->baseClasses.length()) {\n            base = &cls->baseClasses[index.row()];\n        } else if (index.row() - cls->baseClasses.length() < cls->methods.length()) {\n            meth = &cls->methods[index.row() - cls->baseClasses.length()];\n        } else {\n            field = &cls->fields[index.row() - cls->baseClasses.length() - cls->methods.length()];\n        }\n    }\n\n    if (meth) {\n        switch (role) {\n        case Qt::DisplayRole:\n            switch (index.column()) {\n            case NAME:\n                return meth->name;\n            case TYPE:\n                return tr(\"method\");\n            case OFFSET:\n                return meth->addr == RVA_INVALID ? QString() : RzAddressString(meth->addr);\n            case VTABLE:\n                return meth->vtableOffset < 0 ? QString() : QString(\"+%1\").arg(meth->vtableOffset);\n            default:\n                return QVariant();\n            }\n        case OffsetRole:\n            return QVariant::fromValue(meth->addr);\n        case NameRole:\n            return meth->name;\n        case TypeRole:\n            return QVariant::fromValue(RowType::Method);\n        default:\n            return QVariant();\n        }\n    } else if (field) {\n        switch (role) {\n        case Qt::DisplayRole:\n            switch (index.column()) {\n            case NAME:\n                return field->name;\n            case TYPE:\n                return tr(\"field\");\n            case OFFSET:\n                return field->addr == RVA_INVALID ? QString() : RzAddressString(field->addr);\n            default:\n                return QVariant();\n            }\n        case OffsetRole:\n            return QVariant::fromValue(field->addr);\n        case NameRole:\n            return field->name;\n        case TypeRole:\n            return QVariant::fromValue(RowType::Field);\n        default:\n            return QVariant();\n        }\n    } else if (base) {\n        switch (role) {\n        case Qt::DisplayRole:\n            switch (index.column()) {\n            case NAME:\n                return base->name;\n            case TYPE:\n                return tr(\"base class\");\n            case OFFSET:\n                return QString(\"+%1\").arg(base->offset);\n            default:\n                return QVariant();\n            }\n        case NameRole:\n            return base->name;\n        case TypeRole:\n            return QVariant::fromValue(RowType::Base);\n        default:\n            return QVariant();\n        }\n    } else {\n        switch (role) {\n        case Qt::DisplayRole:\n            switch (index.column()) {\n            case NAME:\n                return cls->name;\n            case TYPE:\n                return tr(\"class\");\n            case OFFSET:\n                return cls->addr == RVA_INVALID ? QString() : RzAddressString(cls->addr);\n            case VTABLE:\n                return cls->vtableAddr == RVA_INVALID ? QString()\n                                                      : RzAddressString(cls->vtableAddr);\n            default:\n                return QVariant();\n            }\n        case OffsetRole:\n            return QVariant::fromValue(cls->addr);\n        case NameRole:\n            return cls->name;\n        case TypeRole:\n            return QVariant::fromValue(RowType::Class);\n        default:\n            return QVariant();\n        }\n    }\n}\n\nAnalysisClassesModel::AnalysisClassesModel(CutterDockWidget *parent)\n    : ClassesModel(parent), attrs(new QMap<QString, QVector<Attribute>>)\n{\n    // Just use a simple refresh deferrer. If an event was triggered in the background, simply\n    // refresh everything later.\n    refreshDeferrer = parent->createRefreshDeferrer([this]() { this->refreshAll(); });\n\n    connect(Core(), &CutterCore::refreshAll, this, &AnalysisClassesModel::refreshAll);\n    connect(Core(), &CutterCore::codeRebased, this, &AnalysisClassesModel::refreshAll);\n    connect(Core(), &CutterCore::classNew, this, &AnalysisClassesModel::classNew);\n    connect(Core(), &CutterCore::classDeleted, this, &AnalysisClassesModel::classDeleted);\n    connect(Core(), &CutterCore::classRenamed, this, &AnalysisClassesModel::classRenamed);\n    connect(Core(), &CutterCore::classAttrsChanged, this, &AnalysisClassesModel::classAttrsChanged);\n\n    refreshAll();\n}\n\nvoid AnalysisClassesModel::refreshAll()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    beginResetModel();\n    attrs->clear();\n    classes = Core()->getAllAnalysisClasses(true); // must be sorted\n    endResetModel();\n}\n\nvoid AnalysisClassesModel::classNew(const QString &cls)\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    // find the destination position using binary search and add the row\n    auto it = std::lower_bound(classes.begin(), classes.end(), cls);\n    int index = it - classes.begin();\n    beginInsertRows(QModelIndex(), index, index);\n    classes.insert(it, cls);\n    endInsertRows();\n}\n\nvoid AnalysisClassesModel::classDeleted(const QString &cls)\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    // find the position using binary search and remove the row\n    auto it = std::lower_bound(classes.begin(), classes.end(), cls);\n    if (it == classes.end() || *it != cls) {\n        return;\n    }\n    int index = it - classes.begin();\n    beginRemoveRows(QModelIndex(), index, index);\n    classes.erase(it);\n    endRemoveRows();\n}\n\nvoid AnalysisClassesModel::classRenamed(const QString &oldName, const QString &newName)\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    auto oldIt = std::lower_bound(classes.begin(), classes.end(), oldName);\n    if (oldIt == classes.end() || *oldIt != oldName) {\n        return;\n    }\n    auto newIt = std::lower_bound(classes.begin(), classes.end(), newName);\n    int oldRow = oldIt - classes.begin();\n    int newRow = newIt - classes.begin();\n    // oldRow == newRow means the name stayed the same.\n    // oldRow == newRow - 1 means the name changed, but the row stays the same.\n    if (oldRow != newRow && oldRow != newRow - 1) {\n        beginMoveRows(QModelIndex(), oldRow, oldRow, QModelIndex(), newRow);\n        classes.erase(oldIt);\n        // iterators are invalid now, so we calculate the new position from the rows.\n        if (oldRow < newRow) {\n            // if we move down, we need to account for the removed old element above.\n            newRow--;\n        }\n        classes.insert(newRow, newName);\n        endMoveRows();\n    } else if (oldRow == newRow - 1) { // class name changed, but not the row\n        newRow--;\n        classes[newRow] = newName;\n    }\n    emit dataChanged(index(newRow, 0), index(newRow, 0));\n}\n\nvoid AnalysisClassesModel::classAttrsChanged(const QString &cls)\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    auto it = std::lower_bound(classes.begin(), classes.end(), cls);\n    if (it == classes.end() || *it != cls) {\n        return;\n    }\n    QPersistentModelIndex persistentIndex = QPersistentModelIndex(index(it - classes.begin(), 0));\n    layoutAboutToBeChanged({ persistentIndex });\n    attrs->remove(cls);\n    layoutChanged({ persistentIndex });\n}\n\nconst QVector<AnalysisClassesModel::Attribute> &\nAnalysisClassesModel::getAttrs(const QString &cls) const\n{\n    auto it = attrs->find(cls);\n    if (it != attrs->end()) {\n        return it.value();\n    }\n\n    QVector<AnalysisClassesModel::Attribute> clsAttrs;\n    QList<AnalysisBaseClassDescription> bases = Core()->getAnalysisClassBaseClasses(cls);\n    QList<AnalysisMethodDescription> meths = Core()->getAnalysisClassMethods(cls);\n    QList<AnalysisVTableDescription> vtables = Core()->getAnalysisClassVTables(cls);\n    clsAttrs.reserve(bases.size() + meths.size() + vtables.size());\n\n    for (const AnalysisBaseClassDescription &base : bases) {\n        clsAttrs.push_back(Attribute(Attribute::Type::Base, QVariant::fromValue(base)));\n    }\n\n    for (const AnalysisVTableDescription &vtable : vtables) {\n        clsAttrs.push_back(Attribute(Attribute::Type::VTable, QVariant::fromValue(vtable)));\n    }\n\n    for (const AnalysisMethodDescription &meth : meths) {\n        clsAttrs.push_back(Attribute(Attribute::Type::Method, QVariant::fromValue(meth)));\n    }\n\n    return attrs->insert(cls, clsAttrs).value();\n}\n\nQModelIndex AnalysisClassesModel::index(int row, int column, const QModelIndex &parent) const\n{\n    if (!parent.isValid()) {\n        return createIndex(row, column, (quintptr)0); // root function nodes have id = 0\n    }\n\n    return createIndex(row, column,\n                       (quintptr)parent.row() + 1); // sub-nodes have id = class index + 1\n}\n\nQModelIndex AnalysisClassesModel::parent(const QModelIndex &index) const\n{\n    if (!index.isValid()) {\n        return {};\n    }\n\n    if (index.internalId() == 0) { // root function node\n        return {};\n    } else { // sub-node\n        return this->index((int)(index.internalId() - 1), 0);\n    }\n}\n\nint AnalysisClassesModel::rowCount(const QModelIndex &parent) const\n{\n    if (!parent.isValid()) { // root\n        return classes.count();\n    }\n\n    if (parent.internalId() == 0) { // methods/fields\n        return getAttrs(classes[parent.row()]).size();\n    }\n\n    return 0; // below methods/fields\n}\n\nbool AnalysisClassesModel::hasChildren(const QModelIndex &parent) const\n{\n    return !parent.isValid() || !parent.parent().isValid();\n}\n\nint AnalysisClassesModel::columnCount(const QModelIndex &) const\n{\n    return Columns::COUNT;\n}\n\nQVariant AnalysisClassesModel::data(const QModelIndex &index, int role) const\n{\n    if (index.internalId() == 0) { // class row\n        if (index.row() >= classes.count()) {\n            return QVariant();\n        }\n\n        QString cls = classes.at(index.row());\n        switch (role) {\n        case Qt::DisplayRole:\n            switch (index.column()) {\n            case NAME:\n                return cls;\n            case TYPE:\n                return tr(\"class\");\n            default:\n                return QVariant();\n            }\n        case TypeRole:\n            return QVariant::fromValue(RowType::Class);\n        case NameRole:\n            return cls;\n        default:\n            return QVariant();\n        }\n    } else { // method/field/base row\n        QString cls = classes.at(static_cast<int>(index.internalId() - 1));\n        const Attribute &attr = getAttrs(cls)[index.row()];\n\n        switch (attr.type) {\n        case Attribute::Type::Base: {\n            AnalysisBaseClassDescription base = attr.data.value<AnalysisBaseClassDescription>();\n            switch (role) {\n            case Qt::DisplayRole:\n                switch (index.column()) {\n                case NAME:\n                    return base.className;\n                case TYPE:\n                    return tr(\"base\");\n                case OFFSET:\n                    return QString(\"+%1\").arg(base.offset);\n                default:\n                    return QVariant();\n                }\n            case Qt::DecorationRole:\n                if (index.column() == NAME) {\n                    return QIcon(new SvgIconEngine(QString(\":/img/icons/home.svg\"),\n                                                   QPalette::WindowText));\n                }\n                return QVariant();\n            case VTableRole:\n                return -1;\n            case NameRole:\n                return base.className;\n            case TypeRole:\n                return QVariant::fromValue(RowType::Base);\n            default:\n                return QVariant();\n            }\n            break;\n        }\n        case Attribute::Type::Method: {\n            AnalysisMethodDescription meth = attr.data.value<AnalysisMethodDescription>();\n            switch (role) {\n            case Qt::DisplayRole:\n                switch (index.column()) {\n                case NAME:\n                    return meth.name;\n                case REAL_NAME:\n                    return meth.realName;\n                case TYPE:\n                    return tr(\"method\");\n                case OFFSET:\n                    return meth.addr == RVA_INVALID ? QString() : RzAddressString(meth.addr);\n                case VTABLE:\n                    return meth.vtableOffset < 0 ? QString()\n                                                 : QString(\"+%1\").arg(meth.vtableOffset);\n                default:\n                    return QVariant();\n                }\n            case Qt::DecorationRole:\n                if (index.column() == NAME) {\n                    return QIcon(new SvgIconEngine(QString(\":/img/icons/fork.svg\"),\n                                                   QPalette::WindowText));\n                }\n                return QVariant();\n            case VTableRole:\n                return QVariant::fromValue(meth.vtableOffset);\n            case OffsetRole:\n                return QVariant::fromValue(meth.addr);\n            case NameRole:\n                return meth.name;\n            case RealNameRole:\n                return meth.realName;\n            case TypeRole:\n                return QVariant::fromValue(RowType::Method);\n            default:\n                return QVariant();\n            }\n            break;\n        }\n        case Attribute::Type::VTable: {\n            AnalysisVTableDescription vtable = attr.data.value<AnalysisVTableDescription>();\n            switch (role) {\n            case Qt::DisplayRole:\n                switch (index.column()) {\n                case NAME:\n                    return \"vtable\";\n                case TYPE:\n                    return tr(\"vtable\");\n                case OFFSET:\n                    return RzAddressString(vtable.addr);\n                default:\n                    return QVariant();\n                }\n            case Qt::DecorationRole:\n                if (index.column() == NAME) {\n                    return QIcon(new SvgIconEngine(QString(\":/img/icons/list.svg\"),\n                                                   QPalette::WindowText));\n                }\n                return QVariant();\n            case OffsetRole:\n                return QVariant::fromValue(vtable.addr);\n            case TypeRole:\n                return QVariant::fromValue(RowType::VTable);\n            default:\n                return QVariant();\n            }\n            break;\n        }\n        }\n    }\n    return QVariant();\n}\n\nClassesSortFilterProxyModel::ClassesSortFilterProxyModel(QObject *parent)\n    : AddressableFilterProxyModel(nullptr, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool ClassesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    if (parent.isValid())\n        return true;\n\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    return qhelpers::filterStringContains(index.data(ClassesModel::NameRole).toString(), this);\n}\n\nbool ClassesSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    switch (left.column()) {\n    case ClassesModel::OFFSET: {\n        RVA left_offset = left.data(ClassesModel::OffsetRole).toULongLong();\n        RVA right_offset = right.data(ClassesModel::OffsetRole).toULongLong();\n        if (left_offset != right_offset) {\n            return left_offset < right_offset;\n        }\n    }\n    // fallthrough\n    case ClassesModel::TYPE: {\n        auto left_type = left.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();\n        auto right_type = right.data(ClassesModel::TypeRole).value<ClassesModel::RowType>();\n        if (left_type != right_type) {\n            return left_type < right_type;\n        }\n    }\n    // fallthrough\n    case ClassesModel::VTABLE: {\n        auto left_vtable = left.data(ClassesModel::VTableRole).toLongLong();\n        auto right_vtable = right.data(ClassesModel::VTableRole).toLongLong();\n        if (left_vtable != right_vtable) {\n            return left_vtable < right_vtable;\n        }\n    }\n    // fallthrough\n    case ClassesModel::NAME:\n    default:\n        QString left_name = left.data(ClassesModel::NameRole).toString();\n        QString right_name = right.data(ClassesModel::NameRole).toString();\n        return QString::compare(left_name, right_name, Qt::CaseInsensitive) < 0;\n    }\n}\n\nbool ClassesSortFilterProxyModel::hasChildren(const QModelIndex &parent) const\n{\n    return !parent.isValid() || !parent.parent().isValid();\n}\n\nClassesWidget::ClassesWidget(MainWindow *main)\n    : ListDockWidget(main),\n      seekToVTableAction(tr(\"Seek to VTable\"), this),\n      editMethodAction(tr(\"Edit Method\"), this),\n      addMethodAction(tr(\"Add Method\"), this),\n      newClassAction(tr(\"Create new Class\"), this),\n      renameClassAction(tr(\"Rename Class\"), this),\n      deleteClassAction(tr(\"Delete Class\"), this)\n{\n    setWindowTitle(tr(\"Classes\"));\n    setObjectName(\"ClassesWidget\");\n\n    ui->treeView->setIconSize(QSize(10, 10));\n\n    proxy_model = new ClassesSortFilterProxyModel(this);\n    setModels(proxy_model);\n\n    classSourceCombo = new QComboBox(this);\n    // User an intermediate single-child layout to contain the combo box, otherwise\n    // when the combo box is inserted directly, the entire vertical layout gets a\n    // weird horizontal padding on macOS.\n    QBoxLayout *comboLayout = new QBoxLayout(QBoxLayout::Direction::LeftToRight, nullptr);\n    comboLayout->addWidget(classSourceCombo);\n    ui->verticalLayout->insertLayout(ui->verticalLayout->indexOf(ui->quickFilterView), comboLayout);\n    classSourceCombo->addItem(tr(\"Binary Info (Fixed)\"));\n    classSourceCombo->addItem(tr(\"Analysis (Editable)\"));\n    classSourceCombo->setCurrentIndex(1);\n\n    connect<void (QComboBox::*)(int)>(classSourceCombo, &QComboBox::currentIndexChanged, this,\n                                      &ClassesWidget::refreshClasses);\n\n    connect(&seekToVTableAction, &QAction::triggered, this,\n            &ClassesWidget::seekToVTableActionTriggered);\n    connect(&editMethodAction, &QAction::triggered, this,\n            &ClassesWidget::editMethodActionTriggered);\n    connect(&addMethodAction, &QAction::triggered, this, &ClassesWidget::addMethodActionTriggered);\n    connect(&newClassAction, &QAction::triggered, this, &ClassesWidget::newClassActionTriggered);\n    connect(&renameClassAction, &QAction::triggered, this,\n            &ClassesWidget::renameClassActionTriggered);\n    connect(&deleteClassAction, &QAction::triggered, this,\n            &ClassesWidget::deleteClassActionTriggered);\n\n    // Build context menu like this:\n    //   class-related actions\n    //   -- classesMethodsSeparator\n    //   method-related actions\n    //   -- separator\n    //   default actions from AddressableItemList\n    auto contextMenu = ui->treeView->getItemContextMenu();\n    contextMenu->insertSeparator(contextMenu->actions().first());\n    contextMenu->insertActions(contextMenu->actions().first(),\n                               { &addMethodAction, &editMethodAction, &seekToVTableAction });\n    classesMethodsSeparator = contextMenu->insertSeparator(contextMenu->actions().first());\n    contextMenu->insertActions(classesMethodsSeparator,\n                               { &newClassAction, &renameClassAction, &deleteClassAction });\n    connect(contextMenu, &QMenu::aboutToShow, this, &ClassesWidget::updateActions);\n    ui->treeView->setShowItemContextMenuWithoutAddress(true);\n\n    refreshClasses();\n}\n\nClassesWidget::~ClassesWidget() {}\n\nClassesWidget::Source ClassesWidget::getSource()\n{\n    switch (classSourceCombo->currentIndex()) {\n    case 0:\n        return Source::BIN;\n    default:\n        return Source::ANALYSIS;\n    }\n}\n\nvoid ClassesWidget::refreshClasses()\n{\n    switch (getSource()) {\n    case Source::BIN:\n        if (!bin_model) {\n            proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(nullptr));\n            delete analysis_model;\n            analysis_model = nullptr;\n            bin_model = new BinClassesModel(this);\n            proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(bin_model));\n        }\n        bin_model->setClasses(Core()->getAllClassesFromBin());\n        break;\n    case Source::ANALYSIS:\n        if (!analysis_model) {\n            proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(nullptr));\n            delete bin_model;\n            bin_model = nullptr;\n            analysis_model = new AnalysisClassesModel(this);\n            proxy_model->setSourceModel(static_cast<AddressableItemModelI *>(analysis_model));\n        }\n        break;\n    }\n\n    qhelpers::adjustColumns(ui->treeView, 3, 0);\n\n    ui->treeView->setColumnWidth(0, 200);\n}\n\nvoid ClassesWidget::updateActions()\n{\n    bool isAnalysis = !!analysis_model;\n    newClassAction.setVisible(isAnalysis);\n    addMethodAction.setVisible(isAnalysis);\n\n    bool rowIsAnalysisClass = false;\n    bool rowIsAnalysisMethod = false;\n    QModelIndex index = ui->treeView->selectionModel()->currentIndex();\n    if (isAnalysis && index.isValid()) {\n        auto type = static_cast<ClassesModel::RowType>(index.data(ClassesModel::TypeRole).toInt());\n        rowIsAnalysisClass = type == ClassesModel::RowType::Class;\n        rowIsAnalysisMethod = type == ClassesModel::RowType::Method;\n    }\n\n    renameClassAction.setVisible(rowIsAnalysisClass);\n    deleteClassAction.setVisible(rowIsAnalysisClass);\n\n    classesMethodsSeparator->setVisible(rowIsAnalysisClass || rowIsAnalysisMethod);\n\n    editMethodAction.setVisible(rowIsAnalysisMethod);\n    bool rowHasVTable = false;\n    if (rowIsAnalysisMethod) {\n        QString className = index.parent().data(ClassesModel::NameRole).toString();\n        QString methodName = index.data(ClassesModel::NameRole).toString();\n        AnalysisMethodDescription desc;\n        if (Core()->getAnalysisMethod(className, methodName, &desc)) {\n            if (desc.vtableOffset >= 0) {\n                rowHasVTable = true;\n            }\n        }\n    }\n    seekToVTableAction.setVisible(rowHasVTable);\n}\n\nvoid ClassesWidget::seekToVTableActionTriggered()\n{\n    QModelIndex index = ui->treeView->selectionModel()->currentIndex();\n    QString className = index.parent().data(ClassesModel::NameRole).toString();\n\n    QList<AnalysisVTableDescription> vtables = Core()->getAnalysisClassVTables(className);\n    if (vtables.isEmpty()) {\n        QMessageBox::warning(this, tr(\"Missing VTable in class\"),\n                             tr(\"The class %1 does not have any VTable!\").arg(className));\n        return;\n    }\n\n    QString methodName = index.data(ClassesModel::NameRole).toString();\n    AnalysisMethodDescription desc;\n    if (!Core()->getAnalysisMethod(className, methodName, &desc) || desc.vtableOffset < 0) {\n        return;\n    }\n\n    Core()->seekAndShow(vtables[0].addr + desc.vtableOffset);\n}\n\nvoid ClassesWidget::addMethodActionTriggered()\n{\n    QModelIndex index = ui->treeView->selectionModel()->currentIndex();\n    if (!index.isValid()) {\n        return;\n    }\n\n    QString className;\n    if (index.data(ClassesModel::TypeRole).toInt()\n        == static_cast<int>(ClassesModel::RowType::Class)) {\n        className = index.data(ClassesModel::NameRole).toString();\n    } else {\n        className = index.parent().data(ClassesModel::NameRole).toString();\n    }\n\n    EditMethodDialog::newMethod(className, QString(), this);\n}\n\nvoid ClassesWidget::editMethodActionTriggered()\n{\n    QModelIndex index = ui->treeView->selectionModel()->currentIndex();\n    if (!index.isValid()\n        || index.data(ClassesModel::TypeRole).toInt()\n                != static_cast<int>(ClassesModel::RowType::Method)) {\n        return;\n    }\n    QString className = index.parent().data(ClassesModel::NameRole).toString();\n    QString methName = index.data(ClassesModel::NameRole).toString();\n    EditMethodDialog::editMethod(className, methName, this);\n}\n\nvoid ClassesWidget::newClassActionTriggered()\n{\n    bool ok;\n    QString name = QInputDialog::getText(this, tr(\"Create new Class\"), tr(\"Class Name:\"),\n                                         QLineEdit::Normal, QString(), &ok);\n    if (ok && !name.isEmpty()) {\n        Core()->createNewClass(name);\n    }\n}\n\nvoid ClassesWidget::deleteClassActionTriggered()\n{\n    QModelIndex index = ui->treeView->selectionModel()->currentIndex();\n    if (!index.isValid()\n        || index.data(ClassesModel::TypeRole).toInt()\n                != static_cast<int>(ClassesModel::RowType::Class)) {\n        return;\n    }\n    QString className = index.data(ClassesModel::NameRole).toString();\n    if (QMessageBox::question(this, tr(\"Delete Class\"),\n                              tr(\"Are you sure you want to delete the class %1?\").arg(className))\n        != QMessageBox::StandardButton::Yes) {\n        return;\n    }\n    Core()->deleteClass(className);\n}\n\nvoid ClassesWidget::renameClassActionTriggered()\n{\n    QModelIndex index = ui->treeView->selectionModel()->currentIndex();\n    if (!index.isValid()\n        || index.data(ClassesModel::TypeRole).toInt()\n                != static_cast<int>(ClassesModel::RowType::Class)) {\n        return;\n    }\n    QString oldName = index.data(ClassesModel::NameRole).toString();\n    bool ok;\n    QString newName = QInputDialog::getText(this, tr(\"Rename Class %1\").arg(oldName),\n                                            tr(\"Class name:\"), QLineEdit::Normal, oldName, &ok);\n    if (ok && !newName.isEmpty()) {\n        Core()->renameClass(oldName, newName);\n    }\n}\n"
  },
  {
    "path": "src/widgets/ClassesWidget.h",
    "content": "#ifndef CLASSESWIDGET_H\n#define CLASSESWIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"widgets/ListDockWidget.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nnamespace Ui {\nclass ClassesWidget;\n}\n\nclass QTreeWidget;\nclass QTreeWidgetItem;\nclass MainWindow;\nclass ClassesWidget;\n\n/**\n * @brief Common abstract base class for Bin and Anal classes models\n */\nclass ClassesModel : public AddressableItemModel<>\n{\n    Q_OBJECT\npublic:\n    enum Columns { NAME = 0, REAL_NAME, TYPE, OFFSET, VTABLE, COUNT };\n\n    /**\n     * @brief values for TypeRole data\n     */\n    enum class RowType { Class = 0, Base, VTable, Method, Field };\n\n    /**\n     * @brief Offset role of data for QModelIndex\n     *\n     * will contain values of type RVA\n     */\n    static const int OffsetRole = Qt::UserRole;\n\n    /**\n     * @brief Name role of data for QModelIndex\n     *\n     * will contain values of QString, used for sorting,\n     * as well as identifying classes and methods\n     */\n    static const int NameRole = Qt::UserRole + 1;\n\n    /**\n     * @brief Type role of data for QModelIndex\n     *\n     * will contain values of RowType\n     */\n    static const int TypeRole = Qt::UserRole + 2;\n\n    /**\n     * @brief VTable role of data for QModelIndex\n     *\n     * will contain values of type long long for sorting\n     * by vtable offset\n     */\n    static const int VTableRole = Qt::UserRole + 3;\n\n    /**\n     * @brief Real Name role of data for QModelIndex\n     *\n     * will contain values of QString, used for sorting,\n     * as well as identifying classes and methods\n     */\n    static const int RealNameRole = Qt::UserRole + 4;\n\n    explicit ClassesModel(QObject *parent = nullptr) : AddressableItemModel(parent) {}\n\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n};\n\nQ_DECLARE_METATYPE(ClassesModel::RowType)\n\nclass BinClassesModel : public ClassesModel\n{\n    Q_OBJECT\n\nprivate:\n    QList<BinClassDescription> classes;\n\n    QModelIndex index(int row, int column,\n                      const QModelIndex &parent = QModelIndex()) const override;\n\n    QModelIndex parent(const QModelIndex &index) const override;\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n    QVariant data(const QModelIndex &index, int role) const override;\n\npublic:\n    explicit BinClassesModel(QObject *parent = nullptr);\n    void setClasses(const QList<BinClassDescription> &classes);\n};\n\nclass AnalysisClassesModel : public ClassesModel\n{\n    Q_OBJECT\n\nprivate:\n    /**\n     * @brief List entry below a class\n     *\n     * This roughly corresponds to attributes of Rizin analysis classes, which means it is not an\n     * attribute in the sense of a class member variable, but any kind of sub-info associated with\n     * the class. This struct in particular is used to provide a model for the list entries below a\n     * class.\n     */\n    struct Attribute\n    {\n        enum class Type { VTable, Base, Method };\n        Type type;\n        QVariant data;\n\n        Attribute() = default;\n        Attribute(Type type, const QVariant &data) : type(type), data(data) {}\n    };\n\n    /**\n     * This must always stay sorted alphabetically.\n     */\n    QList<QString> classes;\n\n    RefreshDeferrer *refreshDeferrer;\n\n    /**\n     * @brief Cache for class attributes\n     *\n     * Maps class names to a list of Attributes.\n     * This is filled only when the attributes of a specific class are requested.\n     * (i.e. the user expands the class in the QTreeView)\n     *\n     * This must be a pointer instead of just a QMap, because it has to be modified\n     * in methods that are defined as const by QAbstractItemModel.\n     */\n    std::unique_ptr<QMap<QString, QVector<Attribute>>> attrs;\n\n    const QVector<Attribute> &getAttrs(const QString &cls) const;\n\n    QModelIndex index(int row, int column,\n                      const QModelIndex &parent = QModelIndex()) const override;\n\n    QModelIndex parent(const QModelIndex &index) const override;\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;\n\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n    QVariant data(const QModelIndex &index, int role) const override;\n\npublic:\n    explicit AnalysisClassesModel(CutterDockWidget *parent);\n\npublic slots:\n    void refreshAll();\n    void classNew(const QString &cls);\n    void classDeleted(const QString &cls);\n    void classRenamed(const QString &oldName, const QString &newName);\n    void classAttrsChanged(const QString &cls);\n};\n\nclass ClassesSortFilterProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    explicit ClassesSortFilterProxyModel(QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n    bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;\n};\n\nclass ClassesWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit ClassesWidget(MainWindow *main);\n    ~ClassesWidget();\n\nprivate slots:\n    void seekToVTableActionTriggered();\n    void editMethodActionTriggered();\n    void addMethodActionTriggered();\n    void newClassActionTriggered();\n    void renameClassActionTriggered();\n    void deleteClassActionTriggered();\n\n    void refreshClasses();\n    void updateActions();\n\nprivate:\n    enum class Source { BIN, ANALYSIS };\n\n    Source getSource();\n\n    BinClassesModel *bin_model = nullptr;\n    AnalysisClassesModel *analysis_model = nullptr;\n    ClassesSortFilterProxyModel *proxy_model;\n\n    QComboBox *classSourceCombo;\n\n    QAction seekToVTableAction;\n    QAction editMethodAction;\n    QAction addMethodAction;\n    QAction newClassAction;\n    QAction renameClassAction;\n    QAction deleteClassAction;\n    QAction *classesMethodsSeparator;\n};\n\n#endif // CLASSESWIDGET_H\n"
  },
  {
    "path": "src/widgets/ColorPicker.cpp",
    "content": "#include \"ColorPicker.h\"\n#include \"ui_ColorPicker.h\"\n#include \"common/Helpers.h\"\n\n#include <QPaintEvent>\n#include <QPainter>\n#include <QPainterPath>\n#include <QMouseEvent>\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n#    include <QDesktopWidget>\n#else\n#    include <QWindow>\n#endif\n#include <QPixmap>\n#include <QCursor>\n#include <QScreen>\n\nusing namespace ColorPickerHelpers;\n\nColorPickArea::ColorPickArea(QWidget *parent) : ColorPickerWidget(parent)\n{\n    setMouseTracking(false);\n}\n\nvoid ColorPickArea::paintEvent(QPaintEvent *event)\n{\n    QPainter p(this);\n\n    for (int x = event->rect().x(); x <= event->rect().right(); x++) {\n        for (int y = event->rect().y(); y <= event->rect().bottom(); y++) {\n            qhelpers::ColorFloat h, s, v;\n            QColor c = pointToColor(x, y);\n            c.getHsvF(&h, &s, &v);\n            c.setHsvF(h, s, 1);\n            p.setPen(c);\n            p.drawPoint(x, y);\n        }\n    }\n\n    p.setPen(QPen(Qt::black, 3));\n    QPoint curr = colorToPoint(currColor);\n    p.drawLine(curr - QPoint(0, 8), curr + QPoint(0, 8));\n    p.drawLine(curr - QPoint(8, 0), curr + QPoint(8, 0));\n\n    p.end();\n}\n\nvoid ColorPickArea::setColor(const QColor &c)\n{\n    if (c == currColor) {\n        return;\n    }\n    QPoint p1 = colorToPoint(currColor);\n    QPoint p2 = colorToPoint(c);\n    currColor = c;\n    repaint(QRect(p2 - QPoint(10, 10), p2 + QPoint(10, 10)));\n    repaint(QRect(p1 - QPoint(10, 10), p1 + QPoint(10, 10)));\n\n    emit colorChanged(currColor);\n}\n\nColorPickerWidget::ColorPickerWidget(QWidget *parent) : ColorPickWidgetAbstract(parent) {}\n\nvoid ColorPickerWidget::mouseReleaseEvent(QMouseEvent *event)\n{\n    mouseEvent(event);\n}\n\nvoid ColorPickerWidget::mousePressEvent(QMouseEvent *event)\n{\n    mouseEvent(event);\n}\n\nvoid ColorPickerWidget::mouseMoveEvent(QMouseEvent *event)\n{\n    mouseEvent(event);\n}\n\nQColor ColorPickArea::pointToColor(int x, int y) const\n{\n    QColor color;\n    qhelpers::ColorFloat h, s, v, a;\n    currColor.getHsvF(&h, &s, &v, &a);\n    color.setHsvF(qreal(x) / width(), 1.0 - qreal(y) / height(), v, a);\n    return color;\n}\n\nQPoint ColorPickArea::colorToPoint(const QColor &color) const\n{\n    qhelpers::ColorFloat h, s, v;\n    color.getHsvF(&h, &s, &v);\n    return QPointF(h * width(), (1.0 - s) * height()).toPoint();\n}\n\nvoid ColorPickerWidget::mouseEvent(QMouseEvent *event)\n{\n    QPoint pos = event->pos();\n    if (!rect().contains(pos.x(), rect().y())) {\n        pos.setX(rect().x() < pos.x() ? rect().right() + 1 : rect().x());\n    }\n    if (!rect().contains(rect().x(), pos.y())) {\n        pos.setY(rect().y() < pos.y() ? rect().bottom() + 1 : rect().y());\n    }\n    setColor(pointToColor(pos.x(), pos.y()));\n}\n\nvoid ColorValueBar::setColor(const QColor &c)\n{\n    if (c == currColor) {\n        return;\n    }\n    currColor = c;\n    repaint();\n\n    emit colorChanged(currColor);\n}\n\nvoid ColorValueBar::paintEvent(QPaintEvent *event)\n{\n    QPainter p(this);\n    QColor color = currColor;\n    qhelpers::ColorFloat h, s, v;\n    currColor.getHsvF(&h, &s, &v);\n    v = 1.0 - v;\n\n    const int triangleSize = 10;\n    QRect barRect = rect();\n    barRect.setWidth(barRect.width() - triangleSize);\n\n    for (int y = barRect.y(); y <= barRect.bottom(); y++) {\n        color.setHsvF(h, s, 1.0 - qreal(y) / height());\n        p.setPen(color);\n        p.drawLine(barRect.x(), y, barRect.right(), y);\n    }\n\n    QRectF triangleRect =\n            QRectF(barRect.right(), v * height() - triangleSize / 2, triangleSize, triangleSize);\n\n    QPainterPath path;\n    path.moveTo(triangleRect.left(), triangleRect.top() + triangleRect.height() / 2);\n    path.lineTo(triangleRect.topRight());\n    path.lineTo(triangleRect.bottomRight());\n    path.lineTo(triangleRect.left(), triangleRect.top() + triangleRect.height() / 2);\n\n    p.fillPath(path, palette().text().color());\n\n    p.end();\n    QWidget::paintEvent(event);\n}\n\nQColor ColorValueBar::pointToColor(int x, int y) const\n{\n    Q_UNUSED(x)\n    QColor color = currColor;\n    qhelpers::ColorFloat h, s, v, a;\n    color.getHsvF(&h, &s, &v, &a);\n    color.setHsvF(h, s, 1.0 - qreal(y) / height(), a);\n    return color;\n}\n\nQPoint ColorValueBar::colorToPoint(const QColor &color) const\n{\n    qhelpers::ColorFloat h, s, v;\n    color.getHsvF(&h, &s, &v);\n    return QPoint(rect().x(), int((1.0 - v) * height()));\n}\n\nColorPicker::ColorPicker(QWidget *parent)\n    : ColorPickWidgetAbstract(parent), ui(new Ui::ColorPicker), pickingFromScreen(false)\n{\n    ui->setupUi(this);\n    connect(ui->colorPickArea, &ColorPickArea::colorChanged, this, &ColorPicker::setColor);\n    connect(ui->valuePickBar, &ColorValueBar::colorChanged, this, &ColorPicker::setColor);\n    connect(ui->alphaChannelBar, &AlphaChannelBar::colorChanged, this,\n            [this](const QColor &color) { emit colorChanged(color); });\n    connect(this, &ColorPicker::colorChanged, ui->colorPickArea, &ColorPickArea::setColor);\n    connect(this, &ColorPicker::colorChanged, ui->valuePickBar, &ColorValueBar::setColor);\n    connect(this, &ColorPicker::colorChanged, ui->colorShow, &ColorShowWidget::setColor);\n    connect(this, &ColorPicker::colorChanged, ui->alphaChannelBar, &AlphaChannelBar::setColor);\n\n    connect(ui->hueSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &ColorPicker::colorChannelChanged);\n    connect(ui->satSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &ColorPicker::colorChannelChanged);\n    connect(ui->valSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &ColorPicker::colorChannelChanged);\n    connect(ui->redSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &ColorPicker::colorChannelChanged);\n    connect(ui->blueSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &ColorPicker::colorChannelChanged);\n    connect(ui->greenSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,\n            &ColorPicker::colorChannelChanged);\n\n    connect(ui->hexLineEdit, &QLineEdit::textChanged, this, &ColorPicker::colorChannelChanged);\n\n    connect(ui->pickColorFromScreenButton, &QPushButton::clicked, this,\n            &ColorPicker::startPickingFromScreen);\n}\n\nColorPicker::~ColorPicker()\n{\n    if (pickingFromScreen) {\n        stopPickingFromScreen();\n    }\n}\n\nvoid ColorPicker::setColor(const QColor &color)\n{\n    updateColor(color);\n    emit colorChanged(currColor);\n}\n\nvoid ColorPicker::colorChannelChanged()\n{\n    QString txt = ui->hexLineEdit->text();\n    // Regex pattern below mimics the behaviour of former RegExp::exactMatch()\n    if (!QRegularExpression(\"\\\\A(?:#[0-9a-fA-F]{6})\\\\z\").match(txt).hasMatch()) {\n        return;\n    }\n    QColor hexColor = txt;\n\n    int h, s, v;\n    h = ui->hueSpinBox->value();\n    s = ui->satSpinBox->value();\n    v = ui->valSpinBox->value();\n    QColor hsvColor;\n    hsvColor.setHsv(h, s, v);\n\n    int r, g, b;\n    r = ui->redSpinBox->value();\n    g = ui->greenSpinBox->value();\n    b = ui->blueSpinBox->value();\n    QColor rgbColor;\n    rgbColor.setRgb(r, g, b);\n\n    if (hexColor.isValid() && hexColor != currColor) {\n        setColor(hexColor);\n    } else if (rgbColor.isValid() && rgbColor != currColor) {\n        setColor(rgbColor);\n    } else if (hsvColor.isValid() && hsvColor != currColor) {\n        setColor(hsvColor);\n    }\n}\n\nvoid ColorPicker::updateColor(const QColor &color)\n{\n    QSignalBlocker s0(ui->redSpinBox);\n    QSignalBlocker s1(ui->blueSpinBox);\n    QSignalBlocker s2(ui->greenSpinBox);\n\n    QSignalBlocker s3(ui->valSpinBox);\n    QSignalBlocker s4(ui->satSpinBox);\n    QSignalBlocker s5(ui->hueSpinBox);\n\n    QSignalBlocker s6(ui->hexLineEdit);\n\n    QSignalBlocker s7(ui->alphaChannelBar);\n    QSignalBlocker s8(ui->colorPickArea);\n    QSignalBlocker s9(ui->colorShow);\n    QSignalBlocker s10(ui->valuePickBar);\n\n    currColor = color;\n\n    ui->hexLineEdit->setText(currColor.name());\n\n    int h, s, v;\n    currColor.getHsv(&h, &s, &v);\n    ui->hueSpinBox->setValue(h);\n    ui->satSpinBox->setValue(s);\n    ui->valSpinBox->setValue(v);\n\n    int r, g, b;\n    currColor.getRgb(&r, &g, &b);\n    ui->redSpinBox->setValue(r);\n    ui->greenSpinBox->setValue(g);\n    ui->blueSpinBox->setValue(b);\n\n    ui->valuePickBar->setColor(color);\n    ui->colorPickArea->setColor(color);\n    ui->colorShow->setColor(color);\n    ui->alphaChannelBar->setColor(color);\n}\n\nvoid ColorPicker::startPickingFromScreen()\n{\n    if (!pickingFromScreen) {\n        setMouseTracking(true);\n        pickingFromScreen = true;\n        bufferColor = currColor;\n        grabMouse();\n        QGuiApplication::setOverrideCursor(Qt::CrossCursor);\n    }\n}\n\nvoid ColorPicker::mouseReleaseEvent(QMouseEvent *event)\n{\n    if (pickingFromScreen) {\n        setColor(getColorAtMouse());\n        pickingFromScreen = false;\n        setMouseTracking(false);\n        releaseMouse();\n        QGuiApplication::restoreOverrideCursor();\n    }\n    QWidget::mouseReleaseEvent(event);\n}\n\nvoid ColorPicker::mouseMoveEvent(QMouseEvent *event)\n{\n    if (pickingFromScreen) {\n        updateColor(getColorAtMouse());\n    }\n    QWidget::mouseMoveEvent(event);\n}\n\nQColor ColorPicker::getColorAtMouse()\n{\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    const QDesktopWidget *desktop = QApplication::desktop();\n    const QPixmap pixmap =\n            QGuiApplication::screens()\n                    .at(desktop->screenNumber())\n                    ->grabWindow(desktop->winId(), QCursor::pos().x(), QCursor::pos().y(), 1, 1);\n    return QColor(pixmap.toImage().pixel(0, 0));\n#else\n    QPoint pos = QCursor::pos();\n    auto screen = QGuiApplication::screenAt(pos);\n    if (!screen) {\n        screen = QGuiApplication::primaryScreen();\n    }\n    if (screen) {\n        auto screenRelativePos = pos - screen->geometry().topLeft();\n        const QPixmap pixmap =\n                screen->grabWindow(0, screenRelativePos.x(), screenRelativePos.y(), 1, 1);\n        return QColor(pixmap.toImage().pixel(0, 0));\n    }\n    return QColorConstants::Red;\n#endif\n}\n\nbool ColorPicker::isPickingFromScreen() const\n{\n    return pickingFromScreen;\n}\n\nvoid ColorPicker::setAlphaEnabled(bool enabled)\n{\n    ui->alphaChannelBar->setVisible(enabled);\n}\n\nvoid ColorPicker::stopPickingFromScreen()\n{\n    if (pickingFromScreen) {\n        pickingFromScreen = false;\n        updateColor(bufferColor);\n        releaseMouse();\n        setMouseTracking(false);\n        QGuiApplication::restoreOverrideCursor();\n    }\n}\n\nColorShowWidget::ColorShowWidget(QWidget *parent) : ColorPickWidgetAbstract(parent) {}\n\nvoid ColorShowWidget::setColor(const QColor &c)\n{\n    currColor = c;\n    repaint();\n}\n\nvoid ColorShowWidget::paintEvent(QPaintEvent *event)\n{\n    QPainter p(this);\n    const int miniRectWidth = rect().width() / 2;\n    for (int y = rect().topLeft().ry(); y < rect().bottomRight().ry(); y++) {\n        for (int x = rect().topLeft().rx(); x < rect().bottomRight().rx(); x++) {\n            p.setPen(((x % miniRectWidth) / (miniRectWidth / 2))\n                                     == ((y % miniRectWidth) / (miniRectWidth / 2))\n                             ? Qt::white\n                             : Qt::black);\n            p.drawPoint(x, y);\n        }\n    }\n    p.setPen(currColor);\n    p.setBrush(QBrush(currColor));\n    p.drawRect(event->rect());\n    p.end();\n}\n\nvoid AlphaChannelBar::setColor(const QColor &c)\n{\n    if (c == currColor) {\n        return;\n    }\n    currColor = c;\n    repaint();\n    emit colorChanged(currColor);\n}\n\nvoid AlphaChannelBar::paintEvent(QPaintEvent *event)\n{\n    QPainter p(this);\n    QRect barRect = rect();\n\n    qhelpers::ColorFloat h, s, v, a;\n    currColor.getHsvF(&h, &s, &v, &a);\n    a = 1.0 - a;\n    const int triangleSize = 10;\n\n    barRect.setWidth(barRect.width() - triangleSize);\n\n    const int miniRectWidth = barRect.width() / 2;\n    for (int y = barRect.topLeft().ry(); y < barRect.bottomRight().ry(); y++) {\n        for (int x = barRect.topLeft().rx(); x < barRect.bottomRight().rx(); x++) {\n            p.setPen(((x % miniRectWidth) / (miniRectWidth / 2))\n                                     == ((y % miniRectWidth) / (miniRectWidth / 2))\n                             ? Qt::white\n                             : Qt::black);\n            p.drawPoint(x, y);\n            p.setPen(pointToColor(x, y));\n            p.drawPoint(x, y);\n        }\n    }\n\n    QRectF triangleRect =\n            QRectF(barRect.right(), a * height() - triangleSize / 2, triangleSize, triangleSize);\n\n    QPainterPath path;\n    path.moveTo(triangleRect.left(), triangleRect.top() + triangleRect.height() / 2);\n    path.lineTo(triangleRect.topRight());\n    path.lineTo(triangleRect.bottomRight());\n    path.lineTo(triangleRect.left(), triangleRect.top() + triangleRect.height() / 2);\n    p.fillPath(path, palette().text().color());\n\n    p.end();\n    QWidget::paintEvent(event);\n}\n\nQColor AlphaChannelBar::pointToColor(int x, int y) const\n{\n    Q_UNUSED(x)\n    QColor color = currColor;\n    qhelpers::ColorFloat h, s, v;\n    color.getHsvF(&h, &s, &v);\n    color.setHsvF(h, s, v, 1.0 - qreal(y) / height());\n    return color;\n}\n\nQPoint AlphaChannelBar::colorToPoint(const QColor &) const\n{\n    return QPoint();\n}\n"
  },
  {
    "path": "src/widgets/ColorPicker.h",
    "content": "#ifndef COLORPICKER_H\n#define COLORPICKER_H\n\n#include <QWidget>\n\n/**\n * @namespace ColorPickerHelpers is a namespace that hides all classes needed for ColorPicker class,\n * because classes inherite QObject can not be declared in *.cpp files or inside of another class.\n */\nnamespace ColorPickerHelpers {\nclass ColorPickWidgetAbstract : public QWidget\n{\n    Q_OBJECT\npublic:\n    ColorPickWidgetAbstract(QWidget *parent = nullptr) : QWidget(parent) {}\n    virtual ~ColorPickWidgetAbstract() {}\n\nsignals:\n    void colorChanged(const QColor &color);\n\npublic slots:\n    virtual void setColor(const QColor &color) = 0;\n\nprotected:\n    QColor currColor;\n};\n}\n\nnamespace Ui {\nclass ColorPicker;\n}\n\n/**\n * @brief The ColorPicker class provides widget that allows user to pick color\n * from screen or from palette or type in HSV or RGB or HEX representation of color.\n */\nclass ColorPicker : public ColorPickerHelpers::ColorPickWidgetAbstract\n{\n    Q_OBJECT\npublic:\n    explicit ColorPicker(QWidget *parent = nullptr);\n    ~ColorPicker();\n\n    /**\n     * @brief isPickingFromScreen returns true if color picker is picking from screen.\n     */\n    bool isPickingFromScreen() const;\n\n    void setAlphaEnabled(bool enabled);\n\npublic slots:\n    /**\n     * @brief setColor sets displayed color to @a color and emits colorChanged signal.\n     */\n    virtual void setColor(const QColor &color) override;\n\n    void colorChannelChanged();\n\n    /**\n     * @brief updateColor sets displayed color to @a color.\n     */\n    void updateColor(const QColor &color);\n\n    /**\n     * @brief startPickingFromScreen starts process of picking from screen.\n     * Function is called automatically when \"Pick from screen\" button is clicked.\n     */\n    void startPickingFromScreen();\n\n    /**\n     * @brief stopPickingFromScreen terminates process of picking from screen.\n     */\n    void stopPickingFromScreen();\n\nprotected:\n    void mouseReleaseEvent(QMouseEvent *event) override;\n    void mouseMoveEvent(QMouseEvent *event) override;\n\nprivate:\n    Ui::ColorPicker *ui;\n    bool pickingFromScreen;\n\n    QColor getColorAtMouse();\n\n    /**\n     * @brief bufferColor is used to buffer current color while picking from screen.\n     */\n    QColor bufferColor;\n};\n\nnamespace ColorPickerHelpers {\n/**\n * @brief The ColorPickerWidget class is parent class for ColorPickArea and ColorValueBar classes.\n */\nclass ColorPickerWidget : public ColorPickWidgetAbstract\n{\n    Q_OBJECT\npublic:\n    ColorPickerWidget(QWidget *parent = nullptr);\n\nprotected:\n    void mouseReleaseEvent(QMouseEvent *event) override;\n    void mousePressEvent(QMouseEvent *event) override;\n    void mouseMoveEvent(QMouseEvent *event) override;\n\n    virtual void mouseEvent(QMouseEvent *event);\n\n    /**\n     * @brief pointToColor converts coordinates on widget to color these coordinates represents.\n     */\n    virtual QColor pointToColor(int x, int y) const = 0;\n\n    /**\n     * @brief colorToPoint converts color to coordinates that represent this color.\n     */\n    virtual QPoint colorToPoint(const QColor &color) const = 0;\n};\n\nclass ColorShowWidget : public ColorPickWidgetAbstract\n{\n    Q_OBJECT\npublic:\n    explicit ColorShowWidget(QWidget *parent = nullptr);\n\n    void setColor(const QColor &c) override;\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n};\n\n/**\n * @brief The ColorPickArea class provides widget that helps to pick\n * Saturation and Hue of color in HSV colorspace.\n */\nclass ColorPickArea : public ColorPickerWidget\n{\n    Q_OBJECT\npublic:\n    explicit ColorPickArea(QWidget *parent = nullptr);\n\n    void setColor(const QColor &c) override;\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n\nprivate:\n    QColor pointToColor(int x, int y) const override;\n\n    QPoint colorToPoint(const QColor &color) const override;\n};\n\nclass AlphaChannelBar : public ColorPickerWidget\n{\n    Q_OBJECT\npublic:\n    AlphaChannelBar(QWidget *parent = nullptr) : ColorPickerWidget(parent) {}\n\n    void setColor(const QColor &c) override;\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n\nprivate:\n    QColor pointToColor(int x, int y) const override;\n\n    QPoint colorToPoint(const QColor &color) const override;\n};\n\n/**\n * @brief The ColorValueBar class provides widget that helps to set Valuse of color\n * in HSV colorspace.\n */\nclass ColorValueBar : public ColorPickerWidget\n{\n    Q_OBJECT\npublic:\n    ColorValueBar(QWidget *parent = nullptr) : ColorPickerWidget(parent) {}\n\n    void setColor(const QColor &c) override;\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n\nprivate:\n    QColor pointToColor(int x, int y) const override;\n\n    QPoint colorToPoint(const QColor &color) const override;\n};\n}\n\n#endif // COLORPICKER_H\n"
  },
  {
    "path": "src/widgets/ColorPicker.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ColorPicker</class>\n <widget class=\"QWidget\" name=\"ColorPicker\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1027</width>\n    <height>232</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Color Picker</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"ColorPickerHelpers::ColorPickArea\" name=\"colorPickArea\" native=\"true\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>200</width>\n           <height>200</height>\n          </size>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"ColorPickerHelpers::ColorValueBar\" name=\"valuePickBar\" native=\"true\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>30</width>\n           <height>0</height>\n          </size>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"ColorPickerHelpers::ColorShowWidget\" name=\"colorShow\" native=\"true\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>30</width>\n           <height>40</height>\n          </size>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"ColorPickerHelpers::AlphaChannelBar\" name=\"alphaChannelBar\" native=\"true\">\n         <property name=\"minimumSize\">\n          <size>\n           <width>30</width>\n           <height>0</height>\n          </size>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"colorEditLayout\">\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n         <item>\n          <layout class=\"QVBoxLayout\" name=\"hsvLayout\">\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"valLayout\">\n             <item>\n              <spacer name=\"horizontalSpacer\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"valLabel\">\n               <property name=\"text\">\n                <string>Val:</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QSpinBox\" name=\"valSpinBox\">\n               <property name=\"maximum\">\n                <number>255</number>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"satLayout\">\n             <item>\n              <spacer name=\"horizontalSpacer_2\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"satLabel\">\n               <property name=\"text\">\n                <string>Sat:</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QSpinBox\" name=\"satSpinBox\">\n               <property name=\"maximum\">\n                <number>255</number>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"hueLayout\">\n             <item>\n              <spacer name=\"horizontalSpacer_3\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"hueLabel\">\n               <property name=\"text\">\n                <string>Hue:</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QSpinBox\" name=\"hueSpinBox\">\n               <property name=\"maximum\">\n                <number>359</number>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <layout class=\"QVBoxLayout\" name=\"rgbLayout\">\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"redLayout\">\n             <item>\n              <spacer name=\"horizontalSpacer_4\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"redLabel\">\n               <property name=\"text\">\n                <string>Red:</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QSpinBox\" name=\"redSpinBox\">\n               <property name=\"maximum\">\n                <number>255</number>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"greenLayout\">\n             <item>\n              <spacer name=\"horizontalSpacer_5\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"greenLabel\">\n               <property name=\"text\">\n                <string>Green:</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QSpinBox\" name=\"greenSpinBox\">\n               <property name=\"maximum\">\n                <number>255</number>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"blueLayout\">\n             <item>\n              <spacer name=\"horizontalSpacer_6\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"blueLabel\">\n               <property name=\"text\">\n                <string>Blue:</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QSpinBox\" name=\"blueSpinBox\">\n               <property name=\"maximum\">\n                <number>255</number>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"hexLayout\">\n         <item>\n          <spacer name=\"horizontalSpacer_7\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"hexLabel\">\n           <property name=\"text\">\n            <string>Hex:</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"hexLineEdit\">\n           <property name=\"inputMask\">\n            <string>\\#HHHHHH</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n         <item>\n          <spacer name=\"horizontalSpacer_9\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"pickColorFromScreenButton\">\n           <property name=\"text\">\n            <string>Pick color from screen</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"horizontalSpacer_8\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>40</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ColorPickerHelpers::ColorPickArea</class>\n   <extends>QWidget</extends>\n   <header>widgets/ColorPicker.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ColorPickerHelpers::ColorValueBar</class>\n   <extends>QWidget</extends>\n   <header>widgets/ColorPicker.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ColorPickerHelpers::ColorShowWidget</class>\n   <extends>QWidget</extends>\n   <header>widgets/ColorPicker.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ColorPickerHelpers::AlphaChannelBar</class>\n   <extends>QWidget</extends>\n   <header>widgets/ColorPicker.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/ColorThemeComboBox.cpp",
    "content": "#include \"ColorThemeComboBox.h\"\n\n#include \"core/Cutter.h\"\n\n#include \"common/ColorThemeWorker.h\"\n#include \"common/Configuration.h\"\n\nColorThemeComboBox::ColorThemeComboBox(QWidget *parent) : QComboBox(parent), showOnlyCustom(false)\n{\n    connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,\n            &ColorThemeComboBox::onCurrentIndexChanged);\n    updateFromConfig();\n}\n\nvoid ColorThemeComboBox::updateFromConfig(bool interfaceThemeChanged)\n{\n    QSignalBlocker signalBlockerColorBox(this);\n\n    const int curInterfaceThemeIndex = Config()->getInterfaceTheme();\n    const QList<QString> themes(showOnlyCustom ? ThemeWorker().customThemes()\n                                               : Core()->getColorThemes());\n\n    clear();\n    for (const QString &theme : themes) {\n        if (ThemeWorker().isCustomTheme(theme) || !Configuration::relevantThemes[theme]\n            || (Configuration::cutterInterfaceThemesList()[curInterfaceThemeIndex].flag\n                & Configuration::relevantThemes[theme])) {\n            addItem(theme);\n        }\n    }\n\n    QString curTheme = interfaceThemeChanged ? Config()->getLastThemeOf(\n                               Configuration::cutterInterfaceThemesList()[curInterfaceThemeIndex])\n                                             : Config()->getColorTheme();\n    const int index = findText(curTheme);\n\n    setCurrentIndex(index == -1 ? 0 : index);\n    if (interfaceThemeChanged || index == -1) {\n        curTheme = currentText();\n        Config()->setColorTheme(curTheme);\n    }\n    int maxThemeLen = 0;\n    for (const QString &str : themes) {\n        int strLen = str.length();\n        if (strLen > maxThemeLen) {\n            maxThemeLen = strLen;\n        }\n    }\n    setMinimumContentsLength(maxThemeLen);\n    setSizeAdjustPolicy(QComboBox::AdjustToContents);\n}\n\nvoid ColorThemeComboBox::onCurrentIndexChanged(int index)\n{\n    QString theme = itemText(index);\n\n    int curQtThemeIndex = Config()->getInterfaceTheme();\n    if (curQtThemeIndex >= Configuration::cutterInterfaceThemesList().size()) {\n        curQtThemeIndex = 0;\n        Config()->setInterfaceTheme(curQtThemeIndex);\n    }\n\n    Config()->setLastThemeOf(Configuration::cutterInterfaceThemesList()[curQtThemeIndex], theme);\n    Config()->setColorTheme(theme);\n}\n\nvoid ColorThemeComboBox::setShowOnlyCustom(bool value)\n{\n    showOnlyCustom = value;\n    updateFromConfig();\n}\n"
  },
  {
    "path": "src/widgets/ColorThemeComboBox.h",
    "content": "#ifndef COLORTHEMECOMBOBOX_H\n#define COLORTHEMECOMBOBOX_H\n\n#include <QComboBox>\n\n/**\n * @brief The ColorThemeComboBox class provides combobox with Cutter color themes.\n */\nclass ColorThemeComboBox : public QComboBox\n{\n    Q_OBJECT\npublic:\n    explicit ColorThemeComboBox(QWidget *parent = nullptr);\n\n    /**\n     * @brief setShowOnlyCustom sets whether or not combobox should contain only\n     * custom themes (created by user or imported) or custom and srandard rizin themes.\n     */\n    void setShowOnlyCustom(bool value);\n\npublic slots:\n    /**\n     * @brief updateFromConfig updates list of themes to be shown.\n     * @param interfaceThemeChanged should be set to true if the interface theme of Cutter was\n     * changed since the last call to the function. This will preserve the selected item in the\n     * combo box.\n     *\n     */\n    void updateFromConfig(bool interfaceThemeChanged = false);\n\nprivate slots:\n    void onCurrentIndexChanged(int index);\n\nprivate:\n    bool showOnlyCustom;\n};\n\n#endif // COLORTHEMECOMBOBOX_H\n"
  },
  {
    "path": "src/widgets/ColorThemeListView.cpp",
    "content": "#include <QDebug>\n#include <QJsonObject>\n#include <QMap>\n#include <QPainter>\n#include <QPainterPath>\n#include <QFontMetrics>\n#include <QScreen>\n#include <QJsonArray>\n#include <QScrollBar>\n#include <QApplication>\n#include <QSvgRenderer>\n#include <QMouseEvent>\n#include <QSortFilterProxyModel>\n\n#include \"common/Configuration.h\"\n#include \"common/ColorThemeWorker.h\"\n#include \"common/Helpers.h\"\n\n#include \"widgets/ColorThemeListView.h\"\n\nconstexpr int allFieldsRole = Qt::UserRole + 2;\n\nstruct OptionInfo\n{\n    const char *info;\n    const char *displayingtext;\n};\n\nnamespace {\nextern const QMap<QString, OptionInfo> OPTION_INFO_MAP;\n}\n\nColorOptionDelegate::ColorOptionDelegate(QObject *parent) : QStyledItemDelegate(parent)\n{\n    resetButtonPixmap = getPixmapFromSvg(\":/img/icons/reset.svg\", qApp->palette().text().color());\n    connect(qApp, &QGuiApplication::paletteChanged, this, [this]() {\n        resetButtonPixmap =\n                getPixmapFromSvg(\":/img/icons/reset.svg\", qApp->palette().text().color());\n    });\n}\n\nvoid ColorOptionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,\n                                const QModelIndex &index) const\n{\n    int margin = this->margin;\n    painter->save();\n    painter->setFont(option.font);\n    painter->setRenderHint(QPainter::Antialiasing);\n\n    ColorOption currCO = index.data(Qt::UserRole).value<ColorOption>();\n\n    QFontMetrics fm = QFontMetrics(painter->font());\n    int penWidth = painter->pen().width();\n    int fontHeight = fm.height();\n    QPoint tl = option.rect.topLeft();\n\n    QRect optionNameRect;\n    optionNameRect.setTopLeft(tl + QPoint(margin, penWidth));\n    optionNameRect.setWidth(option.rect.width() - margin * 2);\n    optionNameRect.setHeight(fontHeight);\n\n    QRect optionRect;\n    optionRect.setTopLeft(optionNameRect.bottomLeft() + QPoint(margin / 2, margin / 2));\n    optionRect.setWidth(option.rect.width() - (optionRect.topLeft() - tl).x() * 2);\n    optionRect.setHeight(option.rect.height() - (optionRect.topLeft() - tl).y() - margin);\n\n    QRect colorRect;\n    colorRect.setTopLeft(optionRect.topLeft() + QPoint(margin / 4, margin / 4));\n    colorRect.setBottom(optionRect.bottom() - margin / 4);\n    colorRect.setWidth(colorRect.height());\n\n    QRect descTextRect;\n    descTextRect.setTopLeft(colorRect.topRight()\n                            + QPoint(margin, colorRect.height() / 2 - fontHeight / 2));\n    descTextRect.setWidth(optionRect.width() - (descTextRect.left() - optionRect.left()) - margin);\n    descTextRect.setHeight(fontHeight);\n\n    bool paintResetButton = false;\n    QRect resetButtonRect;\n\n    if (option.state & (QStyle::State_Selected | QStyle::State_MouseOver)) {\n        QBrush br;\n        QPen pen;\n        if (option.state.testFlag(QStyle::State_Selected)) {\n            QColor c = qApp->palette().highlight().color();\n            c.setAlphaF(0.4);\n            br = c;\n            pen = QPen(qApp->palette().highlight().color(), margin / 2);\n            if (currCO.changed) {\n                paintResetButton = true;\n                descTextRect.setWidth(descTextRect.width() - descTextRect.height() - margin / 2);\n                resetButtonRect.setTopLeft(descTextRect.topRight() + QPoint(margin, 0));\n                resetButtonRect.setWidth(descTextRect.height());\n                resetButtonRect.setHeight(descTextRect.height());\n                resetButtonRect.setSize(resetButtonRect.size() * 1.0);\n            }\n        } else {\n#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))\n            QColor placeholderColor = qApp->palette().placeholderText().color();\n#else\n            QColor placeholderColor = qApp->palette().text().color();\n            placeholderColor.setAlphaF(0.5);\n#endif\n            QColor c = placeholderColor;\n            c.setAlphaF(0.2);\n            br = c;\n            pen = QPen(placeholderColor.darker(), margin / 2);\n        }\n\n        painter->fillRect(option.rect, br);\n\n        painter->setPen(pen);\n        int pw = painter->pen().width() / 2;\n        QPoint top = option.rect.topLeft() + QPoint(pw, pw);\n        QPoint bottom = option.rect.bottomLeft() - QPoint(-pw, pw - 1);\n        painter->drawLine(top, bottom);\n    }\n\n    if (paintResetButton) {\n        painter->drawPixmap(resetButtonRect, resetButtonPixmap);\n        auto self = const_cast<ColorOptionDelegate *>(this);\n        self->resetButtonRect = resetButtonRect;\n    }\n    if (option.rect.contains(this->resetButtonRect) && this->resetButtonRect != resetButtonRect) {\n        auto self = const_cast<ColorOptionDelegate *>(this);\n        self->resetButtonRect = QRect(0, 0, 0, 0);\n    }\n\n    painter->setPen(qApp->palette().text().color());\n\n    QFontMetrics fm2 = QFontMetrics(painter->font());\n    auto info = OPTION_INFO_MAP[currCO.optionName];\n    QString name = fm2.elidedText(QApplication::translate(\"ColorTheme\", info.displayingtext),\n                                  Qt::ElideRight, optionNameRect.width());\n    painter->drawText(optionNameRect, name);\n\n    QPainterPath roundedOptionRect;\n    roundedOptionRect.addRoundedRect(optionRect, fontHeight / 4, fontHeight / 4);\n    painter->setPen(qApp->palette().text().color());\n    painter->drawPath(roundedOptionRect);\n\n    QPainterPath roundedColorRect;\n    roundedColorRect.addRoundedRect(colorRect, fontHeight / 4, fontHeight / 4);\n    // Create chess-like pattern of black and white squares\n    // and fill background of roundedColorRect with it\n    if (currCO.color.alpha() < 255) {\n        const int c1 = static_cast<int>(8);\n        const int c2 = c1 / 2;\n        QPixmap p(c1, c1);\n        QPainter paint(&p);\n        paint.fillRect(0, 0, c1, c1, Qt::white);\n        paint.fillRect(0, 0, c2, c2, Qt::black);\n        paint.fillRect(c2, c2, c2, c2, Qt::black);\n        QBrush b;\n        b.setTexture(p);\n        painter->fillPath(roundedColorRect, b);\n    }\n    painter->setPen(currCO.color);\n    painter->fillPath(roundedColorRect, currCO.color);\n\n    QFontMetrics fm3 = QFontMetrics(painter->font());\n    QString desc = fm3.elidedText(currCO.optionName + \": \"\n                                          + QApplication::translate(\"ColorTheme\", info.info),\n                                  Qt::ElideRight, descTextRect.width());\n    painter->setPen(qApp->palette().text().color());\n    painter->setBrush(qApp->palette().text());\n    painter->drawText(descTextRect, desc);\n\n    painter->restore();\n}\n\nQSize ColorOptionDelegate::sizeHint(const QStyleOptionViewItem &option,\n                                    const QModelIndex &index) const\n{\n    qreal margin = this->margin;\n    qreal fontHeight = option.fontMetrics.height();\n    qreal h = QPen().width();\n    h += fontHeight; // option name\n    h += margin / 2; // margin between option rect and option name\n    h += margin / 4; // margin betveen option rect and color rect\n    h += fontHeight; // color rect\n    h += margin / 4; // margin betveen option rect and color rect\n    h += margin; // last margin\n\n    Q_UNUSED(index)\n    return QSize(-1, qRound(h));\n}\n\nQRect ColorOptionDelegate::getResetButtonRect() const\n{\n    return resetButtonRect;\n}\n\nQPixmap ColorOptionDelegate::getPixmapFromSvg(const QString &fileName, const QColor &after) const\n{\n    QFile file(fileName);\n    if (!file.open(QIODevice::ReadOnly)) {\n        return QPixmap();\n    }\n    QString data = file.readAll();\n    data.replace(QRegularExpression(\"#[0-9a-fA-F]{6}\"), QString(\"%1\").arg(after.name()));\n\n    QSvgRenderer svgRenderer(data.toUtf8());\n    QFontMetrics fm = QFontMetrics(qApp->font());\n    QPixmap pix(QSize(fm.height(), fm.height()));\n    pix.fill(Qt::transparent);\n\n    QPainter pixPainter(&pix);\n    svgRenderer.render(&pixPainter);\n\n    return pix;\n}\n\nColorThemeListView::ColorThemeListView(QWidget *parent) : QListView(parent)\n{\n    QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);\n    ColorSettingsModel *model = new ColorSettingsModel(this);\n    proxy->setSourceModel(model);\n    model->updateTheme();\n    setModel(proxy);\n    proxy->setFilterRole(allFieldsRole);\n    proxy->setFilterCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);\n    proxy->setSortRole(Qt::DisplayRole);\n    proxy->setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);\n    setItemDelegate(new ColorOptionDelegate(this));\n    setResizeMode(ResizeMode::Adjust);\n\n    backgroundColor = colorSettingsModel()->getTheme().find(\"gui.background\").value();\n\n    connect(&blinkTimer, &QTimer::timeout, this, &ColorThemeListView::blinkTimeout);\n\n    blinkTimer.setInterval(600);\n    blinkTimer.start();\n\n    setMouseTracking(true);\n}\n\nvoid ColorThemeListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)\n{\n    ColorOption prev = previous.data(Qt::UserRole).value<ColorOption>();\n    Config()->setColor(prev.optionName, prev.color);\n    bool isRizinOption = ThemeWorker().getRizinSpecificOptions().contains(prev.optionName);\n    if (isRizinOption) {\n        Core()->setColor(prev.optionName, prev.color.name());\n    }\n\n    QListView::currentChanged(current, previous);\n    emit itemChanged(current.data(Qt::UserRole).value<ColorOption>().color);\n\n    // restart the timer\n    if (isRizinOption) {\n        blinkTimeout();\n        blinkTimer.start();\n    }\n}\n\nvoid ColorThemeListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,\n                                     const QVector<int> &roles)\n{\n    ColorOption curr = topLeft.data(Qt::UserRole).value<ColorOption>();\n    if (curr.optionName == \"gui.background\") {\n        backgroundColor = curr.color;\n    }\n    QListView::dataChanged(topLeft, bottomRight, roles);\n    emit itemChanged(curr.color);\n    emit dataChanged(curr);\n}\n\nvoid ColorThemeListView::mouseReleaseEvent(QMouseEvent *e)\n{\n    if (qobject_cast<ColorOptionDelegate *>(itemDelegate())\n                ->getResetButtonRect()\n                .contains(e->pos())) {\n        ColorOption co = currentIndex().data(Qt::UserRole).value<ColorOption>();\n        co.changed = false;\n        co.color = ThemeWorker().getTheme(Config()->getColorTheme())[co.optionName];\n        model()->setData(currentIndex(), QVariant::fromValue(co));\n        QCursor c;\n        c.setShape(Qt::CursorShape::ArrowCursor);\n        setCursor(c);\n    }\n}\n\nvoid ColorThemeListView::mouseMoveEvent(QMouseEvent *e)\n{\n    if (qobject_cast<ColorOptionDelegate *>(itemDelegate())\n                ->getResetButtonRect()\n                .contains(e->pos())) {\n        QCursor c;\n        c.setShape(Qt::CursorShape::PointingHandCursor);\n        setCursor(c);\n    } else if (cursor().shape() == Qt::CursorShape::PointingHandCursor) {\n        QCursor c;\n        c.setShape(Qt::CursorShape::ArrowCursor);\n        setCursor(c);\n    }\n}\n\nColorSettingsModel *ColorThemeListView::colorSettingsModel() const\n{\n    return static_cast<ColorSettingsModel *>(\n            static_cast<QSortFilterProxyModel *>(model())->sourceModel());\n}\n\nvoid ColorThemeListView::blinkTimeout()\n{\n    static enum { Normal, Invisible } state = Normal;\n    state = state == Normal ? Invisible : Normal;\n    backgroundColor.setAlphaF(1);\n\n    auto updateColor = [](const QString &name, const QColor &color) {\n        Config()->setColor(name, color);\n        if (ThemeWorker().getRizinSpecificOptions().contains(name)) {\n            Core()->setColor(name, color.name());\n        }\n    };\n\n    ColorOption curr = currentIndex().data(Qt::UserRole).value<ColorOption>();\n    switch (state) {\n    case Normal:\n        updateColor(curr.optionName, curr.color);\n        break;\n    case Invisible:\n        updateColor(curr.optionName, backgroundColor);\n        break;\n    }\n    emit blink();\n}\n\nColorSettingsModel::ColorSettingsModel(QObject *parent) : QAbstractListModel(parent) {}\n\nQVariant ColorSettingsModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid()) {\n        return QVariant();\n    }\n\n    if (index.row() < 0 || index.row() >= theme.size()) {\n        return QVariant();\n    }\n\n    const QString key = theme.at(index.row()).optionName;\n    auto info = OPTION_INFO_MAP[key];\n\n    if (role == Qt::DisplayRole) {\n        return QVariant::fromValue(QApplication::translate(\"ColorTheme\", info.displayingtext));\n    }\n\n    if (role == Qt::UserRole) {\n        return QVariant::fromValue(theme.at(index.row()));\n    }\n\n    if (role == Qt::ToolTipRole) {\n        return QVariant::fromValue(QApplication::translate(\"ColorTheme\", info.info));\n    }\n\n    if (role == allFieldsRole) {\n        const QString name = key;\n        return QVariant::fromValue(QApplication::translate(\"ColorTheme\", info.displayingtext) + \" \"\n                                   + QApplication::translate(\"ColorTheme\", info.info) + \" \" + name);\n    }\n\n    return QVariant();\n}\n\nbool ColorSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n    if (!index.isValid() || role != Qt::EditRole) {\n        return false;\n    }\n\n    ColorOption currOpt = value.value<ColorOption>();\n    theme[index.row()] = currOpt;\n    emit dataChanged(index, index);\n    return true;\n}\n\nvoid ColorSettingsModel::updateTheme()\n{\n    beginResetModel();\n    theme.clear();\n    ColorThemeWorker::Theme obj = ThemeWorker().getTheme(Config()->getColorTheme());\n\n    for (auto it = obj.constBegin(); it != obj.constEnd(); it++) {\n        theme.push_back({ it.key(), it.value(), false });\n    }\n\n    std::sort(theme.begin(), theme.end(), [](const ColorOption &f, const ColorOption &s) {\n        QString s1 = f.optionName;\n        QString s2 = s.optionName;\n        int r = s1.compare(s2, Qt::CaseSensitivity::CaseInsensitive);\n        return r < 0;\n    });\n    endResetModel();\n}\n\nColorThemeWorker::Theme ColorSettingsModel::getTheme() const\n{\n    ColorThemeWorker::Theme th;\n    for (auto &it : theme) {\n        th.insert(it.optionName, it.color);\n    }\n    return th;\n}\n\nnamespace {\nconst QMap<QString, OptionInfo> OPTION_INFO_MAP = {\n    { \"comment\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for code comments\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Comment\") } },\n    { \"usrcmt\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for user comments\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"User Comment\") } },\n    { \"args\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for function arguments\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Arguments\") } },\n    { \"fname\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for function names\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Function Name\") } },\n    { \"floc\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for function locations\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Function Location\") } },\n    { \"fline\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for function lines\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Function Line\") } },\n    { \"flag\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for flags\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Flag\") } },\n    { \"label\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for labels\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Label\") } },\n    { \"help\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for help messages\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Help\") } },\n    { \"flow\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for control flow\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Flow\") } },\n    { \"flow2\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for control flow (alternative)\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Flow 2\") } },\n    { \"prompt\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for prompt\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Prompt\") } },\n    { \"offset\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for offsets\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Offset\") } },\n    { \"input\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for user input\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Input\") } },\n    { \"invalid\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for invalid instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Invalid\") } },\n    { \"other\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for other elements\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Other\") } },\n    { \"b0x00\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for null bytes (0x00)\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Byte 0x00\") } },\n    { \"b0x7f\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for 0x7f bytes\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Byte 0x7f\") } },\n    { \"b0xff\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for 0xff bytes\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Byte 0xff\") } },\n    { \"math\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for math operations\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Math\") } },\n    { \"bin\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for binary information\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Binary\") } },\n    { \"btext\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for text in binary\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Text\") } },\n    { \"push\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for push instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Push\") } },\n    { \"pop\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for pop instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Pop\") } },\n    { \"crypto\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for crypto instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Crypto\") } },\n    { \"jmp\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for jump instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Jump\") } },\n    { \"cjmp\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for conditional jumps\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Conditional Jump\") } },\n    { \"call\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for call instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Call\") } },\n    { \"nop\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for nop instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Nop\") } },\n    { \"ret\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for return instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Return\") } },\n    { \"trap\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for trap/interrupt instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Trap\") } },\n    { \"ucall\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for unknown calls\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Unknown Call\") } },\n    { \"ujmp\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for unknown jumps\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Unknown Jump\") } },\n    { \"swi\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for software interrupts\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Software Interrupt\") } },\n    { \"cmp\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for compare instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Compare\") } },\n    { \"reg\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for registers\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Register\") } },\n    { \"creg\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for changed registers\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Changed Register\") } },\n    { \"num\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for numbers\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Number\") } },\n    { \"mov\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for move instructions\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Move\") } },\n    { \"func_var\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for function variables\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Function Variable\") } },\n    { \"func_var_type\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for function variable types\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Variable Type\") } },\n    { \"func_var_addr\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for function variable addresses\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Variable Address\") } },\n    { \"widget_bg\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for widget background\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Widget Background\") } },\n    { \"widget_sel\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for selected widget\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Widget Selection\") } },\n    { \"meta\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for metadata\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Metadata\") } },\n    { \"ai.read\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for memory read access\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"AI Read\") } },\n    { \"ai.write\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for memory write access\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"AI Write\") } },\n    { \"ai.exec\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for executable memory\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"AI Exec\") } },\n    { \"ai.seq\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for sequential memory\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"AI Sequence\") } },\n    { \"ai.ascii\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for ASCII in memory\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"AI ASCII\") } },\n    { \"graph.box\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for graph box\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Box\") } },\n    { \"graph.box2\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for graph box (alternative 2)\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Box 2\") } },\n    { \"graph.box3\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for graph box (alternative 3)\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Box 3\") } },\n    { \"graph.box4\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for graph box (alternative 4)\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Box 4\") } },\n    { \"graph.true\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for true branch in graph\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Arrow True\") } },\n    { \"graph.false\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for false branch in graph\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Arrow False\") } },\n    { \"graph.trufae\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"In graph view jump arrow (no condition)\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Arrow\") } },\n    { \"graph.ujump\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for unknown jump in graph\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Undefined Jump\") } },\n    { \"graph.current\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for current node in graph\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Current\") } },\n    { \"graph.traced\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for traced node in graph\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Traced\") } },\n    { \"diff.unknown\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for unknown diff\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Diff Unknown\") } },\n    { \"diff.new\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for new diff\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Diff New\") } },\n    { \"diff.match\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for matched diff\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Diff Match\") } },\n    { \"diff.unmatch\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for unmatched diff\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Diff Unmatch\") } },\n    { \"gui.overview.node\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Background color of Graph Overview's node\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Overview Node\") } },\n    { \"gui.overview.fill\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Fill color of Graph Overview's selection\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Overview Fill\") } },\n    { \"gui.overview.border\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Border color of Graph Overview's selection\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Graph Overview Border\") } },\n    { \"gui.cflow\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for GUI control flow\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Control Flow\") } },\n    { \"gui.dataoffset\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for GUI data offset\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Data Offset\") } },\n    { \"gui.background\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for GUI background\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Background\") } },\n    { \"gui.alt_background\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for GUI alternate background\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Node Background\") } },\n    { \"gui.disass_selected\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Background of current graph node\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Current Node\") } },\n    { \"gui.border\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for GUI border\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Node Border\") } },\n    { \"lineHighlight\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for highlighted line\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Line Highlight\") } },\n    { \"wordHighlightBg\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Background color for highlighted word\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Word Highlight Background\") } },\n    { \"wordHighlightFg\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Foreground color for highlighted word\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Word Highlight Foreground\") } },\n    { \"searchCurrent\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Background color for the currently selected search match\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Current Search Highlight\") } },\n    { \"searchHighlight\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Background color for all search matches\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Search Highlight\") } },\n    { \"gui.main\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Main function color\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Main Function\") } },\n    { \"gui.imports\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for imported symbols\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Imports\") } },\n    { \"highlightPC\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Color for Program Counter highlight\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"PC Highlight\") } },\n    { \"gui.navbar.err\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Error color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Error\") } },\n    { \"gui.navbar.seek\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Seek color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Seek\") } },\n    { \"gui.navbar.str\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"String color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar String\") } },\n    { \"gui.navbar.pc\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"PC position color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar PC\") } },\n    { \"gui.navbar.sym\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Symbol color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Symbol\") } },\n    { \"gui.navbar.code\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Code section color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Code\") } },\n    { \"gui.navbar.import\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Import section color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Import\") } },\n    { \"gui.navbar.signature\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Signature section color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Signature\") } },\n    { \"gui.navbar.data\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Data section color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Data\") } },\n    { \"gui.navbar.unexplored\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Unexplored section color in navigation bar\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Navbar Unexplored\") } },\n    { \"gui.breakpoint_background\",\n      { QT_TRANSLATE_NOOP(\"ColorTheme\", \"Background color for breakpoints\"),\n        QT_TRANSLATE_NOOP(\"ColorTheme\", \"Breakpoint Background\") } },\n};\n}\n"
  },
  {
    "path": "src/widgets/ColorThemeListView.h",
    "content": "#ifndef COLORTHEMELISTVIEW_H\n#define COLORTHEMELISTVIEW_H\n\n#include <QTimer>\n#include <QListView>\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QAbstractListModel>\n#include <QStyledItemDelegate>\n\nstruct ColorOption\n{\n    QString optionName;\n    QColor color;\n    bool changed;\n};\nQ_DECLARE_METATYPE(ColorOption);\n\nclass ColorSettingsModel;\n\nclass ColorThemeListView : public QListView\n{\n    Q_OBJECT\npublic:\n    ColorThemeListView(QWidget *parent = nullptr);\n    virtual ~ColorThemeListView() override {}\n\n    ColorSettingsModel *colorSettingsModel() const;\n\nprotected slots:\n    void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;\n\n    void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,\n                     const QVector<int> &roles = QVector<int>()) override;\n\n    void mouseReleaseEvent(QMouseEvent *e) override;\n\n    void mouseMoveEvent(QMouseEvent *e) override;\n\nprivate slots:\n    void blinkTimeout();\n\nsignals:\n    void itemChanged(const QColor &option);\n\n    void dataChanged(const ColorOption &data);\n\n    void blink();\n\nprivate:\n    QTimer blinkTimer;\n    QColor backgroundColor;\n};\n\n//==============================================\n\nclass ColorSettingsModel : public QAbstractListModel\n{\n    Q_OBJECT\npublic:\n    ColorSettingsModel(QObject *parent = nullptr);\n    virtual ~ColorSettingsModel() override {}\n\n    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;\n\n    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override\n    {\n        Q_UNUSED(parent)\n        return theme.size();\n    }\n\n    void updateTheme();\n\n    QHash<QString, QColor> getTheme() const;\n\nprivate:\n    QList<ColorOption> theme;\n};\n\nclass ColorOptionDelegate : public QStyledItemDelegate\n{\n    Q_OBJECT\npublic:\n    ColorOptionDelegate(QObject *parent = nullptr);\n    ~ColorOptionDelegate() override {}\n\n    void paint(QPainter *painter, const QStyleOptionViewItem &option,\n               const QModelIndex &index) const override;\n\n    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;\n\n    QRect getResetButtonRect() const;\n\nprivate:\n    const int margin = 12;\n    QPixmap resetButtonPixmap;\n    QRect resetButtonRect;\n\n    QPixmap getPixmapFromSvg(const QString &fileName, const QColor &after) const;\n};\n\n#endif // COLORTHEMELISTVIEW_H\n"
  },
  {
    "path": "src/widgets/ComboQuickFilterView.cpp",
    "content": "#include \"ComboQuickFilterView.h\"\n#include \"ui_ComboQuickFilterView.h\"\n\nComboQuickFilterView::ComboQuickFilterView(QWidget *parent)\n    : QWidget(parent), ui(new Ui::ComboQuickFilterView)\n{\n    ui->setupUi(this);\n\n    debounceTimer = new QTimer(this);\n    debounceTimer->setSingleShot(true);\n\n    connect(debounceTimer, &QTimer::timeout, this,\n            [this]() { emit filterTextChanged(ui->lineEdit->text()); });\n\n    connect(ui->lineEdit, &QLineEdit::textChanged, this, [this]() { debounceTimer->start(150); });\n}\n\nComboQuickFilterView::~ComboQuickFilterView()\n{\n    delete ui;\n}\n\nvoid ComboQuickFilterView::setLabelText(const QString &text)\n{\n    ui->label->setText(text);\n}\n\nQComboBox *ComboQuickFilterView::comboBox()\n{\n    return ui->comboBox;\n}\n\nvoid ComboQuickFilterView::showFilter()\n{\n    show();\n    ui->lineEdit->setFocus();\n}\n\nvoid ComboQuickFilterView::clearFilter()\n{\n    ui->lineEdit->setText(\"\");\n}\n\nvoid ComboQuickFilterView::closeFilter()\n{\n    ui->lineEdit->setText(\"\");\n    hide();\n    emit filterClosed();\n}\n"
  },
  {
    "path": "src/widgets/ComboQuickFilterView.h",
    "content": "#ifndef COMBOQUICKFILTERVIEW_H\n#define COMBOQUICKFILTERVIEW_H\n\n#include \"core/CutterCommon.h\"\n\n#include <QWidget>\n#include <QComboBox>\n#include <QTimer>\n\nnamespace Ui {\nclass ComboQuickFilterView;\n}\n\nclass CUTTER_EXPORT ComboQuickFilterView : public QWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit ComboQuickFilterView(QWidget *parent = nullptr);\n    ~ComboQuickFilterView();\n\n    void setLabelText(const QString &text);\n    QComboBox *comboBox();\n\npublic slots:\n    void showFilter();\n    void closeFilter();\n    void clearFilter();\n\nsignals:\n    void filterTextChanged(const QString &text);\n    void filterClosed();\n\nprivate:\n    Ui::ComboQuickFilterView *ui;\n    QTimer *debounceTimer;\n};\n\n#endif // COMBOQUICKFILTERVIEW_H\n"
  },
  {
    "path": "src/widgets/ComboQuickFilterView.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ComboQuickFilterView</class>\n <widget class=\"QWidget\" name=\"ComboQuickFilterView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>378</width>\n    <height>20</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QLineEdit\" name=\"lineEdit\">\n     <property name=\"placeholderText\">\n      <string>Quick Filter</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>TextLabel</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"comboBox\"/>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/CommentsWidget.cpp",
    "content": "#include \"CommentsWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n\n#include <QMenu>\n#include <QShortcut>\n#include <QActionGroup>\n\nCommentsModel::CommentsModel(QObject *parent) : AddressableItemModel<>(parent), nested(false) {}\n\nbool CommentsModel::isNested() const\n{\n    return nested;\n}\n\nvoid CommentsModel::setNested(bool nested)\n{\n    beginResetModel();\n    this->nested = nested;\n    endResetModel();\n}\n\nRVA CommentsModel::address(const QModelIndex &index) const\n{\n    if (isNested()) {\n        if (index.internalId() != 0) {\n            auto &group = nestedComments.at(index.parent().row());\n            return group.comments.at(index.row()).offset;\n        } else {\n            return nestedComments.at(index.row()).offset;\n        }\n    } else {\n        return comments.at(index.row()).offset;\n    }\n}\n\nQModelIndex CommentsModel::index(int row, int column, const QModelIndex &parent) const\n{\n    if (!parent.isValid()) {\n        return createIndex(row, column, (quintptr)0);\n    }\n\n    return createIndex(row, column, (quintptr)(parent.row() + 1));\n}\n\nQModelIndex CommentsModel::parent(const QModelIndex &index) const\n{\n    /* Ignore invalid indexes and root nodes */\n    if (!index.isValid() || index.internalId() == 0) {\n        return QModelIndex();\n    }\n\n    return this->index((int)(index.internalId() - 1), 0);\n}\n\nint CommentsModel::rowCount(const QModelIndex &parent) const\n{\n    if (!parent.isValid())\n        return (isNested() ? nestedComments.size() : comments.count());\n\n    if (isNested() && parent.internalId() == 0) {\n        return nestedComments.at(parent.row()).comments.size();\n    }\n\n    return 0;\n}\n\nint CommentsModel::columnCount(const QModelIndex &) const\n{\n    return (isNested() ? static_cast<int>(CommentsModel::NestedColumnCount)\n                       : static_cast<int>(CommentsModel::ColumnCount));\n}\n\nQVariant CommentsModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid() || (index.internalId() != 0 && !index.parent().isValid()))\n        return QVariant();\n\n    int commentIndex;\n    bool isSubnode;\n    if (index.internalId() != 0) {\n        /* Subnode */\n        commentIndex = index.parent().row();\n        isSubnode = true;\n    } else {\n        /* Root node */\n        commentIndex = index.row();\n        isSubnode = false;\n    }\n\n    QString groupName;\n    CommentDescription comment;\n    if (isNested()) {\n        auto &group = nestedComments.at(commentIndex);\n        groupName = group.name;\n        if (isSubnode) {\n            comment = group.comments.at(index.row());\n        }\n    } else {\n        comment = comments.at(commentIndex);\n    }\n\n    switch (role) {\n    case Qt::DisplayRole:\n        if (isNested()) {\n            if (isSubnode) {\n                switch (index.column()) {\n                case OffsetNestedColumn:\n                    return RzAddressString(comment.offset);\n                case CommentNestedColumn:\n                    return comment.name;\n                default:\n                    break;\n                }\n            } else if (index.column() == OffsetNestedColumn) {\n                return groupName;\n            }\n        } else {\n            switch (index.column()) {\n            case CommentsModel::OffsetColumn:\n                return RzAddressString(comment.offset);\n            case CommentsModel::FunctionColumn:\n                return Core()->flagAt(comment.offset);\n            case CommentsModel::CommentColumn:\n                return comment.name;\n            default:\n                break;\n            }\n        }\n        break;\n    case CommentsModel::CommentDescriptionRole:\n        if (isNested() && index.internalId() == 0) {\n            break;\n        }\n        return QVariant::fromValue(comment);\n    default:\n        break;\n    }\n\n    return QVariant();\n}\n\nQVariant CommentsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    if (role == Qt::DisplayRole) {\n        if (isNested()) {\n            switch (section) {\n            case CommentsModel::OffsetNestedColumn:\n                return tr(\"Function/Offset\");\n            case CommentsModel::CommentNestedColumn:\n                return tr(\"Comment\");\n            default:\n                break;\n            }\n        } else {\n            switch (section) {\n            case CommentsModel::OffsetColumn:\n                return tr(\"Offset\");\n            case CommentsModel::FunctionColumn:\n                return tr(\"Function\");\n            case CommentsModel::CommentColumn:\n                return tr(\"Comment\");\n            default:\n                break;\n            }\n        }\n    }\n\n    return QVariant();\n}\n\nCommentsProxyModel::CommentsProxyModel(CommentsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool CommentsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    CommentsModel *srcModel = static_cast<CommentsModel *>(sourceModel());\n    if (srcModel->isNested()) {\n        // Disable filtering\n        return true;\n    }\n\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    auto comment = index.data(CommentsModel::CommentDescriptionRole).value<CommentDescription>();\n\n    return qhelpers::filterStringContains(comment.name, this);\n}\n\nbool CommentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    CommentsModel *srcModel = static_cast<CommentsModel *>(sourceModel());\n    if (srcModel->isNested()) {\n        // Disable sorting\n        return false;\n    }\n\n    if (!left.isValid() || !right.isValid())\n        return false;\n\n    if (left.parent().isValid() || right.parent().isValid())\n        return false;\n\n    auto leftComment = left.data(CommentsModel::CommentDescriptionRole).value<CommentDescription>();\n    auto rightComment =\n            right.data(CommentsModel::CommentDescriptionRole).value<CommentDescription>();\n\n    switch (left.column()) {\n    case CommentsModel::OffsetColumn:\n        return leftComment.offset < rightComment.offset;\n    case CommentsModel::FunctionColumn:\n        return Core()->flagAt(leftComment.offset) < Core()->flagAt(rightComment.offset);\n    case CommentsModel::CommentColumn:\n        return leftComment.name < rightComment.name;\n    default:\n        break;\n    }\n\n    return false;\n}\n\nCommentsWidget::CommentsWidget(MainWindow *main)\n    : ListDockWidget(main),\n      actionHorizontal(tr(\"Horizontal\"), this),\n      actionVertical(tr(\"Vertical\"), this)\n{\n    setWindowTitle(tr(\"Comments\"));\n    setObjectName(\"CommentsWidget\");\n\n    commentsModel = new CommentsModel(this);\n    commentsProxyModel = new CommentsProxyModel(commentsModel, this);\n    setModels(commentsProxyModel);\n    ui->treeView->sortByColumn(CommentsModel::CommentColumn, Qt::AscendingOrder);\n\n    titleContextMenu = new QMenu(this);\n    auto viewTypeGroup = new QActionGroup(titleContextMenu);\n    actionHorizontal.setCheckable(true);\n    actionHorizontal.setActionGroup(viewTypeGroup);\n    connect(&actionHorizontal, &QAction::toggled, this, &CommentsWidget::onActionHorizontalToggled);\n    actionVertical.setCheckable(true);\n    actionVertical.setActionGroup(viewTypeGroup);\n    connect(&actionVertical, &QAction::toggled, this, &CommentsWidget::onActionVerticalToggled);\n    titleContextMenu->addActions(viewTypeGroup->actions());\n\n    actionHorizontal.setChecked(true);\n    this->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(this, &QWidget::customContextMenuRequested, this,\n            &CommentsWidget::showTitleContextMenu);\n\n    connect(Core(), &CutterCore::codeRebased, this, &CommentsWidget::refreshTree);\n    connect(Core(), &CutterCore::commentsChanged, this, &CommentsWidget::refreshTree);\n    connect(Core(), &CutterCore::refreshAll, this, &CommentsWidget::refreshTree);\n}\n\nCommentsWidget::~CommentsWidget() {}\n\nvoid CommentsWidget::onActionHorizontalToggled(bool checked)\n{\n    if (checked) {\n        commentsModel->setNested(false);\n        ui->treeView->setIndentation(8);\n    }\n}\n\nvoid CommentsWidget::onActionVerticalToggled(bool checked)\n{\n    if (checked) {\n        commentsModel->setNested(true);\n        ui->treeView->setIndentation(20);\n    }\n}\n\nvoid CommentsWidget::showTitleContextMenu(const QPoint &pt)\n{\n    titleContextMenu->exec(this->mapToGlobal(pt));\n}\n\nvoid CommentsWidget::refreshTree()\n{\n    commentsModel->beginResetModel();\n\n    commentsModel->comments = Core()->getAllComments(\"CCu\");\n    commentsModel->nestedComments.clear();\n    QMap<QString, size_t> nestedCommentMapping;\n    for (const CommentDescription &comment : commentsModel->comments) {\n        RVA offset = RVA_INVALID;\n        QString fcnName = Core()->nearestFlag(comment.offset, &offset);\n        auto nestedCommentIt = nestedCommentMapping.find(fcnName);\n        if (nestedCommentIt == nestedCommentMapping.end()) {\n            nestedCommentMapping.insert(fcnName, commentsModel->nestedComments.size());\n            commentsModel->nestedComments.push_back({ fcnName, offset, { comment } });\n        } else {\n            auto &commentGroup = commentsModel->nestedComments[nestedCommentIt.value()];\n            commentGroup.comments.append(comment);\n        }\n    }\n\n    commentsModel->endResetModel();\n\n    qhelpers::adjustColumns(ui->treeView, 3, 0);\n}\n"
  },
  {
    "path": "src/widgets/CommentsWidget.h",
    "content": "#ifndef COMMENTSWIDGET_H\n#define COMMENTSWIDGET_H\n\n#include <memory>\n#include <QAbstractItemModel>\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"common/AddressableItemModel.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeWidget.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass CommentsWidget;\n\nstruct CommentGroup\n{\n    QString name;\n    RVA offset;\n    QList<CommentDescription> comments;\n};\n\nclass CommentsModel : public AddressableItemModel<>\n{\n    Q_OBJECT\n\n    friend CommentsWidget;\n\nprivate:\n    QList<CommentDescription> comments;\n    QList<CommentGroup> nestedComments;\n    bool nested;\n\npublic:\n    enum Column { OffsetColumn = 0, FunctionColumn, CommentColumn, ColumnCount };\n    enum NestedColumn { OffsetNestedColumn = 0, CommentNestedColumn, NestedColumnCount };\n    enum Role { CommentDescriptionRole = Qt::UserRole, FunctionRole };\n\n    CommentsModel(QObject *parent = nullptr);\n\n    QModelIndex index(int row, int column,\n                      const QModelIndex &parent = QModelIndex()) const override;\n    QModelIndex parent(const QModelIndex &index) const override;\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    bool isNested() const;\n    void setNested(bool nested);\n\n    RVA address(const QModelIndex &index) const override;\n};\n\nclass CommentsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    CommentsProxyModel(CommentsModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass CommentsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit CommentsWidget(MainWindow *main);\n    ~CommentsWidget() override;\n\nprivate slots:\n    void onActionHorizontalToggled(bool checked);\n    void onActionVerticalToggled(bool checked);\n\n    void showTitleContextMenu(const QPoint &pt);\n\n    void refreshTree();\n\nprivate:\n    CommentsModel *commentsModel;\n    CommentsProxyModel *commentsProxyModel;\n    QAction actionHorizontal;\n    QAction actionVertical;\n\n    QMenu *titleContextMenu;\n};\n\n#endif // COMMENTSWIDGET_H\n"
  },
  {
    "path": "src/widgets/ConsoleWidget.cpp",
    "content": "#include <QScrollBar>\n#include <QMenu>\n#include <QCompleter>\n#include <QAction>\n#include <QShortcut>\n#include <QStringListModel>\n#include <QTimer>\n#include <QSettings>\n#include <QDir>\n#include <QUuid>\n#include <iostream>\n#include \"core/Cutter.h\"\n#include \"ConsoleWidget.h\"\n#include \"ui_ConsoleWidget.h\"\n#include \"common/Helpers.h\"\n#include \"common/SvgIconEngine.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#ifdef Q_OS_WIN\n#    include <windows.h>\n#    include <io.h>\n#    define dup2 _dup2\n#    define dup _dup\n#    define fileno _fileno\n#    define fdopen _fdopen\n#    define PIPE_SIZE 65536 // Match Linux size\n#    define PIPE_NAME \"\\\\\\\\.\\\\pipe\\\\cutteroutput-%1\"\n#else\n#    include <unistd.h>\n#    define PIPE_READ (0)\n#    define PIPE_WRITE (1)\n#    define STDIN_PIPE_NAME \"%1/cutter-stdin-%2\"\n#endif\n\nenum InputTarget { RizinConsole = 0, Debugee = 1 };\n\nstatic const int invalidHistoryPos = -1;\n\nstatic const char *consoleWrapSettingsKey = \"console.wrap\";\n\nConsoleWidget::ConsoleWidget(MainWindow *main)\n    : SearchableDockWidget(main),\n      ui(new Ui::ConsoleWidget),\n      debugOutputEnabled(true),\n      maxHistoryEntries(100),\n      lastHistoryPosition(invalidHistoryPos),\n      completer(nullptr),\n      historyUpShortcut(nullptr),\n      historyDownShortcut(nullptr)\n{\n    ui->setupUi(this);\n\n    // Adjust console lineedit\n    ui->rzInputLineEdit->setTextMargins(10, 0, 0, 0);\n    ui->debugeeInputLineEdit->setTextMargins(10, 0, 0, 0);\n\n    setupFont();\n\n    // Adjust text margins of consoleOutputTextEdit\n    QTextDocument *console_docu = ui->outputTextEdit->document();\n    console_docu->setDocumentMargin(10);\n\n    // Ctrl+` and ';' to toggle console widget\n    QAction *toggleConsole = toggleViewAction();\n    Shortcuts()->setupAction(*toggleConsole, \"Console.toggle\");\n    connect(toggleConsole, &QAction::triggered, this, [this, toggleConsole]() {\n        if (toggleConsole->isChecked()) {\n            widgetToFocusOnRaise()->setFocus();\n        }\n    });\n\n    QAction *actionClear = Shortcuts()->makeAction(\"Console.clear\", this);\n    connect(actionClear, &QAction::triggered, this, [this] {\n        ui->outputTextEdit->clear();\n        ui->outputTextEdit->setExtraSelections({});\n        m_searchBar->clear();\n    });\n    addAction(actionClear);\n\n    // Ctrl+l to clear the output\n    actionClear->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n    actions.append(actionClear);\n\n    actionWrapLines = new QAction(tr(\"Wrap Lines\"), ui->outputTextEdit);\n    actionWrapLines->setCheckable(true);\n    setWrap(QSettings().value(consoleWrapSettingsKey, true).toBool());\n    connect(actionWrapLines, &QAction::triggered, this, [this](bool checked) { setWrap(checked); });\n    actions.append(actionWrapLines);\n\n    // Completion\n    completionActive = false;\n    completer = new QCompleter(&completionModel, this);\n    completer->setMaxVisibleItems(20);\n    completer->setCaseSensitivity(Qt::CaseInsensitive);\n    completer->setFilterMode(Qt::MatchStartsWith);\n    ui->rzInputLineEdit->setCompleter(completer);\n\n    connect(ui->rzInputLineEdit, &QLineEdit::textEdited, this, &ConsoleWidget::updateCompletion);\n    updateCompletion();\n\n    // Set console output context menu\n    ui->outputTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(ui->outputTextEdit, &QWidget::customContextMenuRequested, this,\n            &ConsoleWidget::showCustomContextMenu);\n\n    // Esc clears rzInputLineEdit and debugeeInputLineEdit (like OmniBar)\n    QShortcut *rizin_clear_shortcut =\n            Shortcuts()->makeQShortcut(\"Console.clearRzInputLineEdit\", ui->rzInputLineEdit);\n    connect(rizin_clear_shortcut, &QShortcut::activated, this, &ConsoleWidget::clear);\n    rizin_clear_shortcut->setContext(Qt::WidgetShortcut);\n\n    QShortcut *debugee_clear_shortcut =\n            Shortcuts()->makeQShortcut(\"Console.clearDebugee\", ui->debugeeInputLineEdit);\n    connect(debugee_clear_shortcut, &QShortcut::activated, this, &ConsoleWidget::clear);\n    debugee_clear_shortcut->setContext(Qt::WidgetShortcut);\n\n    // Up and down arrows show history\n    historyUpShortcut = Shortcuts()->makeQShortcut(\"Console.historyUp\", ui->rzInputLineEdit);\n    connect(historyUpShortcut, &QShortcut::activated, this, &ConsoleWidget::historyPrev);\n    historyUpShortcut->setContext(Qt::WidgetShortcut);\n\n    historyDownShortcut = Shortcuts()->makeQShortcut(\"Console.historyDown\", ui->rzInputLineEdit);\n    connect(historyDownShortcut, &QShortcut::activated, this, &ConsoleWidget::historyNext);\n    historyDownShortcut->setContext(Qt::WidgetShortcut);\n\n    QShortcut *completionShortcut =\n            Shortcuts()->makeQShortcut(\"Console.complete\", ui->rzInputLineEdit);\n    connect(completionShortcut, &QShortcut::activated, this, &ConsoleWidget::triggerCompletion);\n\n    connect(ui->rzInputLineEdit, &QLineEdit::editingFinished, this,\n            &ConsoleWidget::disableCompletion);\n\n    connect(Config(), &Configuration::fontsUpdated, this, &ConsoleWidget::setupFont);\n\n    connect(ui->inputCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),\n            this, &ConsoleWidget::onIndexChange);\n\n    connect(Core(), &CutterCore::debugTaskStateChanged, this, [=]() {\n        if (Core()->isRedirectableDebugee()) {\n            ui->inputCombo->setVisible(true);\n        } else {\n            ui->inputCombo->setVisible(false);\n            // Return to the rizin console\n            ui->inputCombo->setCurrentIndex(InputTarget::RizinConsole);\n        }\n    });\n\n    completer->popup()->installEventFilter(this);\n    ui->outputTextEdit->verticalScrollBar()->installEventFilter(this);\n\n    if (Config()->getOutputRedirectionEnabled()) {\n        redirectOutput();\n    }\n\n    connect(ui->outputTextEdit, &SearchableTextEdit::textChanged, this, [this] {\n        if (m_searchBar && m_searchBar->isVisible()) {\n            QPair<int, int> range =\n                    ui->outputTextEdit->search(m_searchBar->text(), m_searchBar->options());\n            m_searchBar->setRange(range.first, range.second);\n        }\n    });\n\n    connect(ui->outputTextEdit, &SearchableTextEdit::updateRequest, this,\n            [this](const QRect &, int dy) {\n                if (m_searchBar && m_searchBar->isVisible() && dy != 0) {\n                    ui->outputTextEdit->highlightMatches();\n                }\n            });\n}\n\nConsoleWidget::~ConsoleWidget()\n{\n#ifndef Q_OS_WIN\n    if (hasOutputRedirection) {\n        ::close(stdinFile);\n        remove(stdinFifoPath.toStdString().c_str());\n        if (origStdin) {\n            dup2(fileno(origStdin), fileno(stdin));\n        }\n        if (origStderr) {\n            dup2(fileno(origStderr), fileno(stderr));\n        }\n        if (origStdout) {\n            dup2(fileno(origStdout), fileno(stdout));\n        }\n    }\n#else\n\n#endif\n}\n\nbool ConsoleWidget::eventFilter(QObject *obj, QEvent *event)\n{\n    if (completer && obj == completer->popup() &&\n        // disable up/down shortcuts if completer is shown\n        (event->type() == QEvent::Type::Show || event->type() == QEvent::Type::Hide)) {\n        bool enabled = !completer->popup()->isVisible();\n        if (historyUpShortcut) {\n            historyUpShortcut->setEnabled(enabled);\n        }\n        if (historyDownShortcut) {\n            historyDownShortcut->setEnabled(enabled);\n        }\n    } else if (m_searchBar && m_searchBar->isVisible()\n               && obj == ui->outputTextEdit->verticalScrollBar()) {\n        if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {\n            this->updateSearchBarPosition();\n        }\n    }\n    return false;\n}\n\nQWidget *ConsoleWidget::widgetToFocusOnRaise()\n{\n    return ui->rzInputLineEdit;\n}\n\nvoid ConsoleWidget::setupFont()\n{\n    ui->outputTextEdit->setFont(Config()->getFont());\n}\n\nvoid ConsoleWidget::addOutput(const QString &msg)\n{\n    ui->outputTextEdit->appendPlainText(msg);\n    scrollOutputToEnd();\n}\n\nvoid ConsoleWidget::addDebugOutput(const QString &msg)\n{\n    if (debugOutputEnabled) {\n        ui->outputTextEdit->appendHtml(\"<font color=\\\"red\\\"> [DEBUG]:\\t\" + msg + \"</font>\");\n        scrollOutputToEnd();\n    }\n}\n\nvoid ConsoleWidget::focusInputLineEdit()\n{\n    ui->rzInputLineEdit->setFocus();\n}\n\nvoid ConsoleWidget::removeLastLine()\n{\n    ui->outputTextEdit->setFocus();\n    QTextCursor cur = ui->outputTextEdit->textCursor();\n    ui->outputTextEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);\n    ui->outputTextEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);\n    ui->outputTextEdit->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor);\n    ui->outputTextEdit->textCursor().removeSelectedText();\n    ui->outputTextEdit->textCursor().deletePreviousChar();\n    ui->outputTextEdit->setTextCursor(cur);\n}\n\nvoid ConsoleWidget::executeCommand(const QString &command)\n{\n    if (!commandTask.isNull()) {\n        return;\n    }\n    ui->rzInputLineEdit->setEnabled(false);\n\n    QString cmd_line = \"[\" + RzAddressString(Core()->getOffset()) + \"]> \" + command;\n    addOutput(cmd_line);\n\n    RVA oldOffset = Core()->getOffset();\n    commandTask =\n            QSharedPointer<CommandTask>(new CommandTask(command, CommandTask::ColorMode::MODE_16M));\n    connect(commandTask.data(), &CommandTask::finished, this,\n            [this, cmd_line, command, oldOffset](const QString &result) {\n                ui->outputTextEdit->appendHtml(CutterCore::ansiEscapeToHtml(result));\n                scrollOutputToEnd();\n                historyAdd(command);\n                commandTask.clear();\n                ui->rzInputLineEdit->setEnabled(true);\n                ui->rzInputLineEdit->setFocus();\n\n                if (oldOffset != Core()->getOffset()) {\n                    Core()->updateSeek();\n                }\n            });\n\n    Core()->getAsyncTaskManager()->start(commandTask);\n}\n\nvoid ConsoleWidget::sendToStdin(const QString &input)\n{\n#ifndef Q_OS_WIN\n    write(stdinFile, (input + \"\\n\").toStdString().c_str(), input.size() + 1);\n    fsync(stdinFile);\n    addOutput(\"Sent input: '\" + input + \"'\");\n#else\n    // Stdin redirection isn't currently available in windows because console applications\n    // with stdin already get their own console window with stdin when they are launched\n    // that the user can type into.\n    addOutput(\"Unsupported feature\");\n#endif\n}\n\nvoid ConsoleWidget::onIndexChange()\n{\n    int target = ui->inputCombo->currentIndex();\n    if (target == InputTarget::Debugee) {\n        ui->rzInputLineEdit->setVisible(false);\n        ui->debugeeInputLineEdit->setVisible(true);\n    } else if (target == InputTarget::RizinConsole) {\n        ui->rzInputLineEdit->setVisible(true);\n        ui->debugeeInputLineEdit->setVisible(false);\n    }\n}\n\nvoid ConsoleWidget::setWrap(bool wrap)\n{\n    QSettings().setValue(consoleWrapSettingsKey, wrap);\n    actionWrapLines->setChecked(wrap);\n    ui->outputTextEdit->setLineWrapMode(wrap ? QPlainTextEdit::WidgetWidth\n                                             : QPlainTextEdit::NoWrap);\n}\n\nvoid ConsoleWidget::on_rzInputLineEdit_returnPressed()\n{\n    QString input = ui->rzInputLineEdit->text();\n    if (input.isEmpty()) {\n        return;\n    }\n    executeCommand(input);\n    ui->rzInputLineEdit->clear();\n}\n\nvoid ConsoleWidget::on_debugeeInputLineEdit_returnPressed()\n{\n    QString input = ui->debugeeInputLineEdit->text();\n    if (input.isEmpty()) {\n        return;\n    }\n    sendToStdin(input);\n    ui->debugeeInputLineEdit->clear();\n}\n\nvoid ConsoleWidget::on_execButton_clicked()\n{\n    on_rzInputLineEdit_returnPressed();\n}\n\nvoid ConsoleWidget::showCustomContextMenu(const QPoint &pt)\n{\n    actionWrapLines->setChecked(ui->outputTextEdit->lineWrapMode() == QPlainTextEdit::WidgetWidth);\n\n    QMenu *menu = new QMenu(ui->outputTextEdit);\n    menu->addActions(actions);\n    menu->exec(ui->outputTextEdit->mapToGlobal(pt));\n    menu->deleteLater();\n}\n\nvoid ConsoleWidget::historyNext()\n{\n    if (!history.isEmpty()) {\n        if (lastHistoryPosition > invalidHistoryPos) {\n            if (lastHistoryPosition >= history.size()) {\n                lastHistoryPosition = history.size() - 1;\n            }\n\n            --lastHistoryPosition;\n\n            if (lastHistoryPosition >= 0) {\n                ui->rzInputLineEdit->setText(history.at(lastHistoryPosition));\n            } else {\n                ui->rzInputLineEdit->clear();\n            }\n        }\n    }\n}\n\nvoid ConsoleWidget::historyPrev()\n{\n    if (!history.isEmpty()) {\n        if (lastHistoryPosition >= history.size() - 1) {\n            lastHistoryPosition = history.size() - 2;\n        }\n\n        ui->rzInputLineEdit->setText(history.at(++lastHistoryPosition));\n    }\n}\n\nvoid ConsoleWidget::triggerCompletion()\n{\n    if (completionActive) {\n        return;\n    }\n    completionActive = true;\n    updateCompletion();\n    completer->complete();\n}\n\nvoid ConsoleWidget::disableCompletion()\n{\n    if (!completionActive) {\n        return;\n    }\n    completionActive = false;\n    updateCompletion();\n    completer->popup()->hide();\n}\n\nvoid ConsoleWidget::updateCompletion()\n{\n    if (!completionActive) {\n        completionModel.setStringList({});\n        return;\n    }\n\n    auto current = ui->rzInputLineEdit->text();\n    auto completions = Core()->autocomplete(current, RZ_LINE_PROMPT_DEFAULT);\n    int lastSpace = current.lastIndexOf(' ');\n    if (lastSpace >= 0) {\n        current = current.left(lastSpace + 1);\n        for (auto &s : completions) {\n            s = current + s;\n        }\n    }\n    completionModel.setStringList(completions);\n}\n\nvoid ConsoleWidget::clear()\n{\n    disableCompletion();\n    ui->rzInputLineEdit->clear();\n    ui->debugeeInputLineEdit->clear();\n\n    invalidateHistoryPosition();\n\n    // Close the potential shown completer popup\n    ui->rzInputLineEdit->clearFocus();\n    ui->rzInputLineEdit->setFocus();\n}\n\nvoid ConsoleWidget::scrollOutputToEnd()\n{\n    const int maxValue = ui->outputTextEdit->verticalScrollBar()->maximum();\n    ui->outputTextEdit->verticalScrollBar()->setValue(maxValue);\n}\n\nvoid ConsoleWidget::historyAdd(const QString &input)\n{\n    if (history.size() + 1 > maxHistoryEntries) {\n        history.removeLast();\n    }\n\n    history.prepend(input);\n\n    invalidateHistoryPosition();\n}\nvoid ConsoleWidget::invalidateHistoryPosition()\n{\n    lastHistoryPosition = invalidHistoryPos;\n}\n\nvoid ConsoleWidget::processQueuedOutput()\n{\n    // Partial lines are ignored since carriage return is currently unsupported\n    while (pipeSocket->canReadLine()) {\n        QString output = QString(pipeSocket->readLine());\n\n        if (origStderr) {\n            fprintf(origStderr, \"%s\", output.toStdString().c_str());\n        }\n\n        // Get the last segment that wasn't overwritten by carriage return\n        output = output.trimmed();\n        output = output.remove(0, output.lastIndexOf('\\r')).trimmed();\n        ui->outputTextEdit->appendHtml(CutterCore::ansiEscapeToHtml(output));\n        scrollOutputToEnd();\n    }\n}\n\n// Haiku doesn't have O_ASYNC\n#ifdef Q_OS_HAIKU\n#    define O_ASYNC O_NONBLOCK\n#endif\n\nvoid ConsoleWidget::redirectOutput()\n{\n    // Make sure that we are running in a valid console with initialized output handles\n    if (0 > fileno(stderr) && 0 > fileno(stdout)) {\n        addOutput(\"Run cutter in a console to enable rizin output redirection into this widget.\");\n        return;\n    }\n\n    pipeSocket = new QLocalSocket(this);\n\n    origStdin = fdopen(dup(fileno(stdin)), \"r\");\n    origStderr = fdopen(dup(fileno(stderr)), \"a\");\n    origStdout = fdopen(dup(fileno(stdout)), \"a\");\n#ifdef Q_OS_WIN\n    QString pipeName = QString::fromLatin1(PIPE_NAME).arg(QUuid::createUuid().toString());\n\n    SECURITY_ATTRIBUTES attributes = { sizeof(SECURITY_ATTRIBUTES), 0, false };\n    hWrite =\n            CreateNamedPipeW((wchar_t *)pipeName.utf16(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,\n                             PIPE_TYPE_BYTE | PIPE_WAIT, 1, PIPE_SIZE, PIPE_SIZE, 0, &attributes);\n\n    int writeFd = _open_osfhandle((intptr_t)hWrite, _O_WRONLY | _O_TEXT);\n    dup2(writeFd, fileno(stdout));\n    dup2(writeFd, fileno(stderr));\n\n    pipeSocket->connectToServer(pipeName, QIODevice::ReadOnly);\n#else\n    pipe(redirectPipeFds);\n    stdinFifoPath = QString(STDIN_PIPE_NAME).arg(QDir::tempPath(), QUuid::createUuid().toString());\n    mkfifo(stdinFifoPath.toStdString().c_str(), (mode_t)0777);\n    stdinFile = open(stdinFifoPath.toStdString().c_str(), O_RDWR | O_ASYNC);\n\n    dup2(stdinFile, fileno(stdin));\n    dup2(redirectPipeFds[PIPE_WRITE], fileno(stderr));\n    dup2(redirectPipeFds[PIPE_WRITE], fileno(stdout));\n\n    // Attempt to force line buffering to avoid calling processQueuedOutput\n    // for partial lines\n    setlinebuf(stderr);\n    setlinebuf(stdout);\n\n    // Configure the pipe to work in async mode\n    fcntl(redirectPipeFds[PIPE_READ], F_SETFL, O_ASYNC | O_NONBLOCK);\n\n    pipeSocket->setSocketDescriptor(redirectPipeFds[PIPE_READ]);\n    pipeSocket->connectToServer(QIODevice::ReadOnly);\n#endif\n\n    hasOutputRedirection = true;\n    connect(pipeSocket, &QIODevice::readyRead, this, &ConsoleWidget::processQueuedOutput);\n}\n\nQWidget *ConsoleWidget::searchableArea() const\n{\n    return ui->outputTextEdit;\n}\n\nvoid ConsoleWidget::searchBarHidden()\n{\n    ui->outputTextEdit->clearSearch();\n}\n\nvoid ConsoleWidget::searchBarShown()\n{\n    searchChanged(m_searchBar->text(), m_searchBar->options());\n}\n\nvoid ConsoleWidget::findNext()\n{\n    int index = ui->outputTextEdit->findNext();\n    m_searchBar->setCurrentIndex(index);\n}\n\nvoid ConsoleWidget::findPrev()\n{\n    int index = ui->outputTextEdit->findPrev();\n    m_searchBar->setCurrentIndex(index);\n}\n\nvoid ConsoleWidget::findLast()\n{\n    int index = ui->outputTextEdit->findLast();\n    m_searchBar->setCurrentIndex(index);\n}\n\nvoid ConsoleWidget::searchChanged(const QString &text, int options)\n{\n    QPair<int, int> range = ui->outputTextEdit->search(text, options);\n    m_searchBar->setRange(range.first, range.second);\n}\n"
  },
  {
    "path": "src/widgets/ConsoleWidget.h",
    "content": "#ifndef CONSOLEWIDGET_H\n#define CONSOLEWIDGET_H\n\n#include \"core/MainWindow.h\"\n#include \"CutterDockWidget.h\"\n#include \"common/CommandTask.h\"\n#include \"common/DirectionalComboBox.h\"\n#include \"SearchBarWidget.h\"\n#include \"SearchableDockWidget.h\"\n\n#include <QStringListModel>\n#include <QSocketNotifier>\n#include <QLocalSocket>\n\n#include <memory>\n\nclass QCompleter;\nclass QShortcut;\n\nnamespace Ui {\nclass ConsoleWidget;\n}\n\nclass ConsoleWidget : public SearchableDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit ConsoleWidget(MainWindow *main);\n\n    ~ConsoleWidget();\n\n    void setDebugOutputEnabled(bool enabled) { debugOutputEnabled = enabled; }\n\n    void setMaxHistoryEntries(int max) { maxHistoryEntries = max; }\n\nprotected:\n    bool eventFilter(QObject *obj, QEvent *event) override;\n    QWidget *widgetToFocusOnRaise() override;\n\n    // search related\n    void searchChanged(const QString &text, int options) override;\n    void findNext() override;\n    void findPrev() override;\n    void findLast() override;\n    void searchBarShown() override;\n    void searchBarHidden() override;\n    QWidget *searchableArea() const override;\n\npublic slots:\n    void focusInputLineEdit();\n\n    void addOutput(const QString &msg);\n    void addDebugOutput(const QString &msg);\n\nprivate slots:\n    void setupFont();\n\n    void on_rzInputLineEdit_returnPressed();\n    void on_debugeeInputLineEdit_returnPressed();\n    void onIndexChange();\n\n    void on_execButton_clicked();\n\n    void showCustomContextMenu(const QPoint &pt);\n\n    void historyNext();\n    void historyPrev();\n\n    void triggerCompletion();\n    void disableCompletion();\n    void updateCompletion();\n\n    void clear();\n\n    /**\n     * @brief Passes redirected output from the pipe to the terminal and console\n     */\n    void processQueuedOutput();\n\nprivate:\n    void scrollOutputToEnd();\n    void historyAdd(const QString &input);\n    void invalidateHistoryPosition();\n    void removeLastLine();\n    void executeCommand(const QString &command);\n    void sendToStdin(const QString &input);\n    void setWrap(bool wrap);\n\n    /**\n     * @brief Redirects stderr and stdout to the output pipe which is handled by\n     *        processQueuedOutput\n     */\n    void redirectOutput();\n\n    QSharedPointer<CommandTask> commandTask;\n\n    std::unique_ptr<Ui::ConsoleWidget> ui;\n    QAction *actionWrapLines;\n    QList<QAction *> actions;\n    bool debugOutputEnabled;\n    int maxHistoryEntries;\n    int lastHistoryPosition;\n    QStringList history;\n    bool completionActive;\n    QStringListModel completionModel;\n    QCompleter *completer;\n    QShortcut *historyUpShortcut;\n    QShortcut *historyDownShortcut;\n    FILE *origStderr = nullptr;\n    FILE *origStdout = nullptr;\n    FILE *origStdin = nullptr;\n    QLocalSocket *pipeSocket = nullptr;\n    bool hasOutputRedirection = false;\n#ifdef Q_OS_WIN\n    HANDLE hRead;\n    HANDLE hWrite;\n#else\n    int redirectPipeFds[2];\n    int stdinFile = -1;\n    QString stdinFifoPath;\n    QVector<char> *redirectionBuffer;\n#endif\n};\n\n#endif // CONSOLEWIDGET_H\n"
  },
  {
    "path": "src/widgets/ConsoleWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ConsoleWidget</class>\n <widget class=\"QDockWidget\" name=\"ConsoleWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Console</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"SearchableTextEdit\" name=\"outputTextEdit\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"font\">\n       <font>\n        <family>Monospace</family>\n       </font>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"undoRedoEnabled\">\n       <bool>false</bool>\n      </property>\n      <property name=\"readOnly\">\n       <bool>true</bool>\n      </property>\n      <property name=\"plainText\">\n       <string/>\n      </property>\n      <property name=\"textInteractionFlags\">\n       <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <layout class=\"QGridLayout\" name=\"gridLayout\">\n      <property name=\"spacing\">\n       <number>5</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>4</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>2</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>2</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>2</number>\n      </property>\n      <item row=\"0\" column=\"0\">\n       <widget class=\"DirectionalComboBox\" name=\"inputCombo\">\n        <property name=\"sizeAdjustPolicy\">\n         <enum>QComboBox::AdjustToContents</enum>\n        </property>\n        <property name=\"visible\">\n            <bool>false</bool>\n        </property>\n        <item>\n         <property name=\"text\">\n          <string>Rizin Console</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Debugee Input</string>\n         </property>\n        </item>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"1\">\n       <widget class=\"QLineEdit\" name=\"rzInputLineEdit\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"frame\">\n         <bool>false</bool>\n        </property>\n        <property name=\"placeholderText\">\n         <string> Type &quot;?&quot; for help</string>\n        </property>\n        <property name=\"clearButtonEnabled\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"1\">\n       <widget class=\"QLineEdit\" name=\"debugeeInputLineEdit\">\n        <property name=\"visible\">\n            <bool>false</bool>\n        </property>\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"frame\">\n         <bool>false</bool>\n        </property>\n        <property name=\"placeholderText\">\n         <string> Enter input for the debugee</string>\n        </property>\n        <property name=\"clearButtonEnabled\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"2\">\n       <widget class=\"QToolButton\" name=\"execButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"toolTip\">\n         <string>Execute command</string>\n        </property>\n        <property name=\"styleSheet\">\n         <string notr=\"true\"/>\n        </property>\n        <property name=\"text\">\n         <string>...</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"../resources.qrc\">\n          <normaloff>:/img/icons/arrow_right.svg</normaloff>:/img/icons/arrow_right.svg</iconset>\n        </property>\n        <property name=\"iconSize\">\n         <size>\n          <width>24</width>\n          <height>16</height>\n         </size>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>DirectionalComboBox</class>\n   <extends>QComboBox</extends>\n   <header>common/DirectionalComboBox.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>SearchableTextEdit</class>\n   <extends>QPlainTextEdit</extends>\n   <header>widgets/SearchableTextEdit.h</header>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"../resources.qrc\"/>\n </resources>\n <connections/>\n</ui>\n\n"
  },
  {
    "path": "src/widgets/CutterDockWidget.cpp",
    "content": "#include \"CutterDockWidget.h\"\n#include \"core/MainWindow.h\"\n\n#include <QEvent>\n#include <QShortcut>\n\nCutterDockWidget::CutterDockWidget(MainWindow *parent, QAction *) : CutterDockWidget(parent) {}\n\nCutterDockWidget::CutterDockWidget(MainWindow *parent) : QDockWidget(parent), mainWindow(parent)\n{\n    // Install event filter to catch redraw widgets when needed\n    installEventFilter(this);\n    updateIsVisibleToUser();\n    connect(toggleViewAction(), &QAction::triggered, this, &QWidget::raise);\n}\n\nCutterDockWidget::~CutterDockWidget() = default;\n\nbool CutterDockWidget::eventFilter(QObject *object, QEvent *event)\n{\n    if (event->type() == QEvent::FocusIn || event->type() == QEvent::ZOrderChange\n        || event->type() == QEvent::Paint || event->type() == QEvent::Close\n        || event->type() == QEvent::Show || event->type() == QEvent::Hide) {\n        updateIsVisibleToUser();\n    }\n    return QDockWidget::eventFilter(object, event);\n}\n\nQVariantMap CutterDockWidget::serializeViewProprties()\n{\n    return {};\n}\n\nvoid CutterDockWidget::deserializeViewProperties(const QVariantMap &) {}\n\nvoid CutterDockWidget::ignoreVisibilityStatus(bool ignore)\n{\n    this->ignoreVisibility = ignore;\n    updateIsVisibleToUser();\n}\n\nvoid CutterDockWidget::raiseMemoryWidget()\n{\n    show();\n    raise();\n    widgetToFocusOnRaise()->setFocus(Qt::FocusReason::TabFocusReason);\n}\n\nvoid CutterDockWidget::toggleDockWidget(bool show)\n{\n    if (!show) {\n        this->hide();\n    } else {\n        this->show();\n        this->raise();\n    }\n}\n\nQWidget *CutterDockWidget::widgetToFocusOnRaise()\n{\n    return this;\n}\n\nvoid CutterDockWidget::updateIsVisibleToUser()\n{\n    // Check if the user can actually see the widget.\n    bool visibleToUser = isVisible() && !visibleRegion().isEmpty() && !ignoreVisibility;\n    if (visibleToUser == isVisibleToUserCurrent) {\n        return;\n    }\n    isVisibleToUserCurrent = visibleToUser;\n    if (isVisibleToUserCurrent) {\n        emit becameVisibleToUser();\n    }\n}\n\nvoid CutterDockWidget::closeEvent(QCloseEvent *event)\n{\n    QDockWidget::closeEvent(event);\n    if (isTransient) {\n        if (mainWindow) {\n            mainWindow->removeWidget(this);\n        }\n\n        // remove parent, otherwise dock layout may still decide to use this widget which is about\n        // to be deleted\n        setParent(nullptr);\n\n        deleteLater();\n    }\n\n    emit closed();\n}\n\nQString CutterDockWidget::getDockNumber()\n{\n    auto name = this->objectName();\n    if (name.contains(';')) {\n        auto parts = name.split(';');\n        if (parts.size() >= 2) {\n            return parts[1];\n        }\n    }\n    return QString();\n}\n"
  },
  {
    "path": "src/widgets/CutterDockWidget.h",
    "content": "#ifndef CUTTERWIDGET_H\n#define CUTTERWIDGET_H\n\n#include \"core/CutterCommon.h\"\n#include \"common/RefreshDeferrer.h\"\n\n#include <QDockWidget>\n\nclass MainWindow;\n\nclass CUTTER_EXPORT CutterDockWidget : public QDockWidget\n{\n    Q_OBJECT\n\npublic:\n    CUTTER_DEPRECATED(\"Action will be ignored. Use CutterDockWidget(MainWindow*) instead.\")\n    CutterDockWidget(MainWindow *parent, QAction *action);\n\n    explicit CutterDockWidget(MainWindow *parent);\n    ~CutterDockWidget() override;\n    bool eventFilter(QObject *object, QEvent *event) override;\n    bool isVisibleToUser() { return isVisibleToUserCurrent; }\n\n    /**\n     * @brief Set whether the Widget should be deleted after it is closed.\n     * This is especially important for extra widgets.\n     */\n    void setTransient(bool v) { isTransient = v; }\n\n    /**\n     * @brief Convenience method for creating and registering a RefreshDeferrer without any\n     * parameters\n     * @param refreshNowFunc lambda taking no parameters, called when a refresh should occur\n     */\n    template<typename Func>\n    RefreshDeferrer *createRefreshDeferrer(Func refreshNowFunc)\n    {\n        auto *deferrer = new RefreshDeferrer(nullptr, this);\n        deferrer->registerFor(this);\n        connect(deferrer, &RefreshDeferrer::refreshNow, this,\n                [refreshNowFunc](const RefreshDeferrerParamsResult) { refreshNowFunc(); });\n        return deferrer;\n    }\n\n    /**\n     * @brief Convenience method for creating and registering a RefreshDeferrer with a replacing\n     * Accumulator\n     * @param replaceIfNull passed to the ReplacingRefreshDeferrerAccumulator\n     * @param refreshNowFunc lambda taking a single parameter of type ParamResult, called when a\n     * refresh should occur\n     */\n    template<class ParamResult, typename Func>\n    RefreshDeferrer *createReplacingRefreshDeferrer(bool replaceIfNull, Func refreshNowFunc)\n    {\n        auto *deferrer = new RefreshDeferrer(\n                new ReplacingRefreshDeferrerAccumulator<ParamResult>(replaceIfNull), this);\n        deferrer->registerFor(this);\n        connect(deferrer, &RefreshDeferrer::refreshNow, this,\n                [refreshNowFunc](const RefreshDeferrerParamsResult paramsResult) {\n                    auto *result = static_cast<const ParamResult *>(paramsResult);\n                    refreshNowFunc(result);\n                });\n        return deferrer;\n    }\n    /**\n     * @brief Serialize dock properties for saving as part of layout.\n     *\n     * Override this function for saving dock specific view properties. Use\n     * in situations where it makes sense to have different properties for\n     * multiple instances of widget. Don't use for options that are more suitable\n     * as global settings and should be applied equally to all widgets or all\n     * widgets of this kind.\n     *\n     * Keep synchrononized with deserializeViewProperties. When modifying add\n     * project upgrade step in SettingsUpgrade.cpp if necessary.\n     *\n     * @return Dictionary of current dock properties.\n     * @see CutterDockWidget#deserializeViewProperties\n     */\n    virtual QVariantMap serializeViewProprties();\n    /**\n     * @brief Deserialization half of serialize view properties.\n     *\n     * When a property is not specified in property map dock should reset it\n     * to default value instead of leaving it umodified. Empty map should reset\n     * all properties controlled by serializeViewProprties/deserializeViewProperties\n     * mechanism.\n     *\n     * @param properties to modify for current widget\n     * @see CutterDockWidget#serializeViewProprties\n     */\n    virtual void deserializeViewProperties(const QVariantMap &properties);\n    /**\n     * @brief Ignore visibility status.\n     * Useful for temporary ignoring visibility changes while this information is unreliable.\n     * @param ignored - set to true for enabling ignoring mode\n     */\n    void ignoreVisibilityStatus(bool ignored);\n\n    void raiseMemoryWidget();\nsignals:\n    void becameVisibleToUser();\n    void closed();\n\npublic slots:\n    void toggleDockWidget(bool show);\n\nprotected:\n    virtual QWidget *widgetToFocusOnRaise();\n\n    void closeEvent(QCloseEvent *event) override;\n    QString getDockNumber();\n\n    MainWindow *mainWindow;\n\nprivate:\n    bool isTransient = false;\n\n    bool isVisibleToUserCurrent = false;\n    bool ignoreVisibility = false;\n    void updateIsVisibleToUser();\n};\n\n#endif // CUTTERWIDGET_H\n"
  },
  {
    "path": "src/widgets/CutterGraphView.cpp",
    "content": "#include \"CutterGraphView.h\"\n\n#include \"core/Cutter.h\"\n#include \"common/Configuration.h\"\n#include \"dialogs/MultitypeFileSaveDialog.h\"\n#include \"TempConfig.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <cmath>\n\n#include <QStandardPaths>\n#include <QActionGroup>\n\nstatic const uint64_t BITMPA_EXPORT_WARNING_SIZE = 32 * 1024 * 1024;\n\n#ifndef NDEBUG\n#    define GRAPH_GRID_DEBUG_MODES true\n#else\n#    define GRAPH_GRID_DEBUG_MODES false\n#endif\n\nCutterGraphView::CutterGraphView(QWidget *parent)\n    : GraphView(parent),\n      mFontMetrics(nullptr),\n      actionExportGraph(tr(\"Export Graph\"), this),\n      graphLayout(GraphView::Layout::GridMedium)\n{\n    connect(Core(), &CutterCore::graphOptionsChanged, this, &CutterGraphView::refreshView);\n    connect(Config(), &Configuration::colorsUpdated, this, &CutterGraphView::colorsUpdatedSlot);\n    connect(Config(), &Configuration::fontsUpdated, this, &CutterGraphView::fontsUpdatedSlot);\n\n    initFont();\n    updateColors();\n\n    connect(&actionExportGraph, &QAction::triggered, this, &CutterGraphView::showExportDialog);\n\n    layoutMenu = new QMenu(tr(\"Layout\"), this);\n    horizontalLayoutAction = layoutMenu->addAction(tr(\"Horizontal\"));\n    horizontalLayoutAction->setCheckable(true);\n\n    static const std::pair<QString, GraphView::Layout> LAYOUT_CONFIG[] = {\n        { tr(\"Grid narrow\"), GraphView::Layout::GridNarrow },\n        { tr(\"Grid medium\"), GraphView::Layout::GridMedium },\n        { tr(\"Grid wide\"), GraphView::Layout::GridWide }\n#if GRAPH_GRID_DEBUG_MODES\n        ,\n        { \"GridAAA\", GraphView::Layout::GridAAA },\n        { \"GridAAB\", GraphView::Layout::GridAAB },\n        { \"GridABA\", GraphView::Layout::GridABA },\n        { \"GridABB\", GraphView::Layout::GridABB },\n        { \"GridBAA\", GraphView::Layout::GridBAA },\n        { \"GridBAB\", GraphView::Layout::GridBAB },\n        { \"GridBBA\", GraphView::Layout::GridBBA },\n        { \"GridBBB\", GraphView::Layout::GridBBB }\n#endif\n#ifdef CUTTER_ENABLE_GRAPHVIZ\n        ,\n        { tr(\"Graphviz polyline\"), GraphView::Layout::GraphvizPolyline },\n        { tr(\"Graphviz ortho\"), GraphView::Layout::GraphvizOrtho },\n        { tr(\"Graphviz sfdp\"), GraphView::Layout::GraphvizSfdp },\n        { tr(\"Graphviz neato\"), GraphView::Layout::GraphvizNeato },\n        { tr(\"Graphviz twopi\"), GraphView::Layout::GraphvizTwoPi },\n        { tr(\"Graphviz circo\"), GraphView::Layout::GraphvizCirco }\n#endif\n    };\n    layoutMenu->addSeparator();\n    connect(horizontalLayoutAction, &QAction::toggled, this, &CutterGraphView::updateLayout);\n    QActionGroup *layoutGroup = new QActionGroup(layoutMenu);\n    for (auto &item : LAYOUT_CONFIG) {\n        auto action = layoutGroup->addAction(item.first);\n        action->setCheckable(true);\n        GraphView::Layout layout = item.second;\n        if (layout == this->graphLayout) {\n            action->setChecked(true);\n        }\n        connect(action, &QAction::triggered, this, [this, layout]() {\n            this->graphLayout = layout;\n            updateLayout();\n        });\n    }\n    layoutMenu->addActions(layoutGroup->actions());\n\n    grabGesture(Qt::PinchGesture);\n}\n\nQPoint CutterGraphView::getTextOffset(int line) const\n{\n    return QPoint(padding, padding + line * charHeight);\n}\n\nvoid CutterGraphView::initFont()\n{\n    setFont(Config()->getFont());\n    QFontMetricsF metrics(font());\n    baseline = int(metrics.ascent());\n#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)\n    ACharWidth = metrics.width('A');\n#else\n    ACharWidth = metrics.horizontalAdvance('A');\n#endif\n    padding = ACharWidth;\n    charHeight = static_cast<int>(metrics.height());\n    charOffset = 0;\n    mFontMetrics.reset(new CachedFontMetrics<qreal>(font()));\n}\n\nvoid CutterGraphView::zoom(QPointF mouseRelativePos, double velocity)\n{\n    qreal newScale = getViewScale() * std::pow(1.25, velocity);\n    setZoom(mouseRelativePos, newScale);\n}\n\nvoid CutterGraphView::setZoom(QPointF mouseRelativePos, double scale)\n{\n    mouseRelativePos.rx() *= size().width();\n    mouseRelativePos.ry() *= size().height();\n    mouseRelativePos /= getViewScale();\n\n    auto globalMouse = mouseRelativePos + getViewOffset();\n    mouseRelativePos *= getViewScale();\n    qreal newScale = scale;\n    newScale = std::max(newScale, 0.05);\n    mouseRelativePos /= newScale;\n    setViewScale(newScale);\n\n    // Adjusting offset, so that zooming will be approaching to the cursor.\n    setViewOffset(globalMouse.toPoint() - mouseRelativePos.toPoint());\n\n    viewport()->update();\n    emit viewZoomed();\n}\n\nvoid CutterGraphView::zoomIn()\n{\n    zoom(QPointF(0.5, 0.5), 1);\n}\n\nvoid CutterGraphView::zoomOut()\n{\n    zoom(QPointF(0.5, 0.5), -1);\n}\n\nvoid CutterGraphView::zoomReset()\n{\n    setZoom(QPointF(0.5, 0.5), 1);\n}\n\nvoid CutterGraphView::showExportDialog()\n{\n    showExportGraphDialog(\"global_funcall\", RZ_CORE_GRAPH_TYPE_FUNCALL, RVA_INVALID);\n}\n\nvoid CutterGraphView::updateColors()\n{\n    disassemblyBackgroundColor = ConfigColor(\"gui.alt_background\");\n    disassemblySelectedBackgroundColor = ConfigColor(\"gui.disass_selected\");\n    mDisabledBreakpointColor = disassemblyBackgroundColor;\n    graphNodeColor = ConfigColor(\"gui.border\");\n    backgroundColor = ConfigColor(\"gui.background\");\n    disassemblySelectionColor = ConfigColor(\"lineHighlight\");\n    PCSelectionColor = ConfigColor(\"highlightPC\");\n\n    jmpColor = ConfigColor(\"graph.trufae\");\n    brtrueColor = ConfigColor(\"graph.true\");\n    brfalseColor = ConfigColor(\"graph.false\");\n\n    mCommentColor = ConfigColor(\"comment\");\n}\n\nvoid CutterGraphView::colorsUpdatedSlot()\n{\n    updateColors();\n    refreshView();\n}\n\nGraphLayout::LayoutConfig CutterGraphView::getLayoutConfig()\n{\n    auto blockSpacing = Config()->getGraphBlockSpacing();\n    auto edgeSpacing = Config()->getGraphEdgeSpacing();\n    GraphLayout::LayoutConfig layoutConfig;\n    layoutConfig.blockHorizontalSpacing = blockSpacing.x();\n    layoutConfig.blockVerticalSpacing = blockSpacing.y();\n    layoutConfig.edgeHorizontalSpacing = edgeSpacing.x();\n    layoutConfig.edgeVerticalSpacing = edgeSpacing.y();\n    return layoutConfig;\n}\n\nvoid CutterGraphView::updateLayout()\n{\n    setGraphLayout(GraphView::makeGraphLayout(graphLayout, horizontalLayoutAction->isChecked()));\n    saveCurrentBlock();\n    setLayoutConfig(getLayoutConfig());\n    computeGraphPlacement();\n    restoreCurrentBlock();\n    emit viewRefreshed();\n}\n\nvoid CutterGraphView::fontsUpdatedSlot()\n{\n    initFont();\n    refreshView();\n}\n\nbool CutterGraphView::event(QEvent *event)\n{\n    switch (event->type()) {\n    case QEvent::ShortcutOverride: {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n        qhelpers::KeyComb key = Qt::Key(keyEvent->key()) | keyEvent->modifiers();\n        if (Shortcuts()->matchesKeySequence(\"General.zoomOut\", key)\n            || Shortcuts()->matchesKeySequence(\"General.zoomReset\", key)\n            || Shortcuts()->matchesKeySequence(\"General.zoomIn\", key)) {\n            event->accept();\n            return true;\n        }\n        break;\n    }\n    case QEvent::KeyPress: {\n        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n        qhelpers::KeyComb key = Qt::Key(keyEvent->key()) | keyEvent->modifiers();\n        if (Shortcuts()->matchesKeySequence(\"General.zoomIn\", key)) {\n            zoomIn();\n            return true;\n        } else if (Shortcuts()->matchesKeySequence(\"General.zoomOut\", key)) {\n            zoomOut();\n            return true;\n        } else if (Shortcuts()->matchesKeySequence(\"General.zoomReset\", key)) {\n            zoomReset();\n            return true;\n        }\n        break;\n    }\n    default:\n        break;\n    }\n    return GraphView::event(event);\n}\n\nvoid CutterGraphView::refreshView()\n{\n    initFont();\n    setLayoutConfig(getLayoutConfig());\n}\n\nbool CutterGraphView::gestureEvent(QGestureEvent *event)\n{\n    if (!event) {\n        return false;\n    }\n\n    if (auto gesture = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture))) {\n        auto changeFlags = gesture->changeFlags();\n\n        if (changeFlags & QPinchGesture::ScaleFactorChanged) {\n            auto cursorPos = gesture->centerPoint();\n            cursorPos.rx() /= size().width();\n            cursorPos.ry() /= size().height();\n\n            setZoom(cursorPos, getViewScale() * gesture->scaleFactor());\n        }\n\n        event->accept(gesture);\n        return true;\n    }\n\n    return false;\n}\n\nvoid CutterGraphView::wheelEvent(QWheelEvent *event)\n{\n    // when CTRL is pressed, we zoom in/out with mouse wheel\n    if (Qt::ControlModifier == event->modifiers()) {\n        const QPoint numDegrees = event->angleDelta() / 8;\n        if (!numDegrees.isNull()) {\n            int numSteps = numDegrees.y() / 15;\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n            QPointF relativeMousePos = event->pos();\n#else\n            QPointF relativeMousePos = event->position();\n#endif\n            relativeMousePos.rx() /= size().width();\n            relativeMousePos.ry() /= size().height();\n\n            zoom(relativeMousePos, numSteps);\n        }\n        event->accept();\n    } else {\n        // use mouse wheel for scrolling when CTRL is not pressed\n        GraphView::wheelEvent(event);\n    }\n    emit graphMoved();\n}\n\nvoid CutterGraphView::resizeEvent(QResizeEvent *event)\n{\n    GraphView::resizeEvent(event);\n    emit resized();\n}\n\nvoid CutterGraphView::saveCurrentBlock() {}\n\nvoid CutterGraphView::restoreCurrentBlock() {}\n\nvoid CutterGraphView::mousePressEvent(QMouseEvent *event)\n{\n    GraphView::mousePressEvent(event);\n    emit graphMoved();\n}\n\nvoid CutterGraphView::mouseMoveEvent(QMouseEvent *event)\n{\n    GraphView::mouseMoveEvent(event);\n    emit graphMoved();\n}\n\nvoid CutterGraphView::exportGraph(QString filePath, GraphExportType exportType,\n                                  RzCoreGraphType graphType, RVA address)\n{\n    bool graphTransparent = Config()->getBitmapTransparentState();\n    double graphScaleFactor = Config()->getBitmapExportScaleFactor();\n    switch (exportType) {\n    case GraphExportType::Png:\n        this->saveAsBitmap(filePath, \"png\", graphScaleFactor, graphTransparent);\n        break;\n    case GraphExportType::Jpeg:\n        this->saveAsBitmap(filePath, \"jpg\", graphScaleFactor, false);\n        break;\n    case GraphExportType::Svg:\n        this->saveAsSvg(filePath);\n        break;\n\n    case GraphExportType::GVDot:\n        exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_DOT, address);\n        break;\n    case GraphExportType::RzJson:\n        exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_JSON, address);\n        break;\n    case GraphExportType::RzGml:\n        exportRzTextGraph(filePath, graphType, RZ_CORE_GRAPH_FORMAT_GML, address);\n        break;\n\n    case GraphExportType::GVJson:\n        Core()->writeGraphvizGraphToFile(filePath, \"json\", graphType, address);\n        break;\n    case GraphExportType::GVGif:\n        Core()->writeGraphvizGraphToFile(filePath, \"gif\", graphType, address);\n        break;\n    case GraphExportType::GVPng:\n        Core()->writeGraphvizGraphToFile(filePath, \"png\", graphType, address);\n        break;\n    case GraphExportType::GVJpeg:\n        Core()->writeGraphvizGraphToFile(filePath, \"jpg\", graphType, address);\n        break;\n    case GraphExportType::GVPostScript:\n        Core()->writeGraphvizGraphToFile(filePath, \"ps\", graphType, address);\n        break;\n    case GraphExportType::GVSvg:\n        Core()->writeGraphvizGraphToFile(filePath, \"svg\", graphType, address);\n        break;\n    case GraphExportType::GVPdf:\n        Core()->writeGraphvizGraphToFile(filePath, \"pdf\", graphType, address);\n        break;\n    }\n}\n\nvoid CutterGraphView::exportRzTextGraph(QString filePath, RzCoreGraphType type,\n                                        RzCoreGraphFormat format, RVA address)\n{\n    char *string = Core()->getTextualGraphAt(type, format, address);\n    if (!string) {\n        return;\n    }\n\n    QFile file(filePath);\n    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {\n        QTextStream fileOut(&file);\n        fileOut << string;\n    } else {\n        qWarning() << \"Can't open or create file: \" << filePath;\n    }\n    free(string);\n}\n\nbool CutterGraphView::graphIsBitamp(CutterGraphView::GraphExportType type)\n{\n    switch (type) {\n    case GraphExportType::Png:\n    case GraphExportType::Jpeg:\n    case GraphExportType::GVGif:\n    case GraphExportType::GVPng:\n    case GraphExportType::GVJpeg:\n        return true;\n    default:\n        return false;\n    }\n}\n\nQ_DECLARE_METATYPE(CutterGraphView::GraphExportType);\n\nvoid CutterGraphView::showExportGraphDialog(QString defaultName, RzCoreGraphType type, RVA address)\n{\n    qWarning() << defaultName << \" - \" << type << \" addr \" << RzAddressString(address);\n    QVector<MultitypeFileSaveDialog::TypeDescription> types = {\n        { tr(\"PNG (*.png)\"), \"png\", QVariant::fromValue(GraphExportType::Png) },\n        { tr(\"JPEG (*.jpg)\"), \"jpg\", QVariant::fromValue(GraphExportType::Jpeg) },\n        { tr(\"SVG (*.svg)\"), \"svg\", QVariant::fromValue(GraphExportType::Svg) }\n    };\n\n    types.append({\n            { tr(\"Graphviz dot (*.dot)\"), \"dot\", QVariant::fromValue(GraphExportType::GVDot) },\n            { tr(\"Graph Modelling Language (*.gml)\"), \"gml\",\n              QVariant::fromValue(GraphExportType::RzGml) },\n            { tr(\"RZ JSON (*.json)\"), \"json\", QVariant::fromValue(GraphExportType::RzJson) },\n    });\n\n    bool hasGraphviz = !QStandardPaths::findExecutable(\"dot\").isEmpty()\n            || !QStandardPaths::findExecutable(\"xdot\").isEmpty();\n    if (hasGraphviz) {\n        types.append({ { tr(\"Graphviz json (*.json)\"), \"json\",\n                         QVariant::fromValue(GraphExportType::GVJson) },\n                       { tr(\"Graphviz gif (*.gif)\"), \"gif\",\n                         QVariant::fromValue(GraphExportType::GVGif) },\n                       { tr(\"Graphviz png (*.png)\"), \"png\",\n                         QVariant::fromValue(GraphExportType::GVPng) },\n                       { tr(\"Graphviz jpg (*.jpg)\"), \"jpg\",\n                         QVariant::fromValue(GraphExportType::GVJpeg) },\n                       { tr(\"Graphviz PostScript (*.ps)\"), \"ps\",\n                         QVariant::fromValue(GraphExportType::GVPostScript) },\n                       { tr(\"Graphviz svg (*.svg)\"), \"svg\",\n                         QVariant::fromValue(GraphExportType::GVSvg) },\n                       { tr(\"Graphviz pdf (*.pdf)\"), \"pdf\",\n                         QVariant::fromValue(GraphExportType::GVPdf) } });\n    }\n\n    MultitypeFileSaveDialog dialog(this, tr(\"Export Graph\"));\n    dialog.setTypes(types);\n    dialog.selectFile(defaultName);\n    if (!dialog.exec()) {\n        return;\n    }\n\n    auto selectedType = dialog.selectedType();\n    if (!selectedType.data.canConvert<GraphExportType>()) {\n        qWarning() << \"Bad selected type, should not happen.\";\n        return;\n    }\n    auto exportType = selectedType.data.value<GraphExportType>();\n\n    if (graphIsBitamp(exportType)) {\n        uint64_t bitmapSize = uint64_t(width) * uint64_t(height);\n        if (bitmapSize > BITMPA_EXPORT_WARNING_SIZE) {\n            auto answer =\n                    QMessageBox::question(this, tr(\"Graph Export\"),\n                                          tr(\"Do you really want to export %1 x %2 = %3 pixel \"\n                                             \"bitmap image? Consider using different format.\")\n                                                  .arg(width)\n                                                  .arg(height)\n                                                  .arg(bitmapSize));\n            if (answer != QMessageBox::Yes) {\n                return;\n            }\n        }\n    }\n\n    QString filePath = dialog.selectedFiles().first();\n    exportGraph(filePath, exportType, type, address);\n}\n"
  },
  {
    "path": "src/widgets/CutterGraphView.h",
    "content": "#ifndef CUTTER_GRAPHVIEW_H\n#define CUTTER_GRAPHVIEW_H\n\n#include <QWidget>\n#include <QPainter>\n#include <QShortcut>\n#include <QLabel>\n\n#include <QGestureEvent>\n\n#include \"widgets/GraphView.h\"\n#include \"common/CachedFontMetrics.h\"\n\n/**\n * @brief Common Cutter specific graph functionality.\n */\nclass CutterGraphView : public GraphView\n{\n    Q_OBJECT\npublic:\n    CutterGraphView(QWidget *parent);\n    virtual bool event(QEvent *event) override;\n\n    enum class GraphExportType {\n        Png,\n        Jpeg,\n        Svg,\n        GVDot,\n        GVJson,\n        GVGif,\n        GVPng,\n        GVJpeg,\n        GVPostScript,\n        GVSvg,\n        GVPdf,\n        RzGml,\n        RzJson\n    };\n    /**\n     * @brief Export graph to a file in the specified format\n     * @param filePath - output file path\n     * @param exportType - export type, GV* and Rz* types require \\p graphCommand\n     * @param graphType - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or\n     * RZ_CORE_GRAPH_TYPE_IMPORT\n     * @param address - object address (if global set it to RVA_INVALID)\n     */\n    void exportGraph(QString filePath, GraphExportType exportType, RzCoreGraphType graphType,\n                     RVA address = RVA_INVALID);\n\n    /**\n     * @brief Export graph in one of the text formats supported by rizin json, gml, SDB key-value\n     * @param filePath - output file path\n     * @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT\n     * @param format - graph format, example RZ_CORE_GRAPH_FORMAT_DOT or RZ_CORE_GRAPH_FORMAT_GML\n     * @param address - object address (if global set it to RVA_INVALID)\n     */\n    void exportRzTextGraph(QString filePath, RzCoreGraphType type, RzCoreGraphFormat format,\n                           RVA address);\n    static bool graphIsBitamp(GraphExportType type);\n    /**\n     * @brief Show graph export dialog.\n     * @param defaultName - default file name in the export dialog\n     * @param type - graph type, example RZ_CORE_GRAPH_TYPE_FUNCALL or RZ_CORE_GRAPH_TYPE_IMPORT\n     * @param address - object address (if global set it to RVA_INVALID)\n     */\n    void showExportGraphDialog(QString defaultName, RzCoreGraphType type,\n                               RVA address = RVA_INVALID);\n\npublic slots:\n    virtual void refreshView();\n    void updateColors();\n    void fontsUpdatedSlot();\n\n    void zoom(QPointF mouseRelativePos, double velocity);\n    void setZoom(QPointF mouseRelativePos, double scale);\n    void zoomIn();\n    void zoomOut();\n    void zoomReset();\n\n    /**\n     * @brief Show the export file dialog. Override this to support rizin based export formats.\n     */\n    virtual void showExportDialog();\nsignals:\n    void viewRefreshed();\n    void viewZoomed();\n    void graphMoved();\n    void resized();\n\nprotected:\n    void mousePressEvent(QMouseEvent *event) override;\n    void mouseMoveEvent(QMouseEvent *event) override;\n    void wheelEvent(QWheelEvent *event) override;\n    void resizeEvent(QResizeEvent *event) override;\n    bool gestureEvent(QGestureEvent *event) override;\n\n    /**\n     * @brief Save the the currently viewed or displayed block.\n     * Called before reloading graph. Override to this to implement graph specific logic for what\n     * block is selected. Default implementation does nothing.\n     */\n    virtual void saveCurrentBlock();\n    /**\n     * @brief Restore view focus and block last saved using saveCurrentBlock().\n     * Called after the graph is reloaded. Default implementation does nothing. Can center the view\n     * if the new graph displays completely different content and the matching node doesn't exist.\n     */\n    virtual void restoreCurrentBlock();\n\n    void initFont();\n    QPoint getTextOffset(int line) const;\n    GraphLayout::LayoutConfig getLayoutConfig();\n    virtual void updateLayout();\n\n    // Font data\n    std::unique_ptr<CachedFontMetrics<qreal>> mFontMetrics;\n    qreal ACharWidth; // width of character A\n    int charHeight;\n    int charOffset;\n    int baseline;\n    qreal padding;\n\n    // colors\n    QColor disassemblyBackgroundColor;\n    QColor disassemblySelectedBackgroundColor;\n    QColor disassemblySelectionColor;\n    QColor PCSelectionColor;\n    QColor jmpColor;\n    QColor brtrueColor;\n    QColor brfalseColor;\n    QColor retShadowColor;\n    QColor indirectcallShadowColor;\n    QColor mAutoCommentColor;\n    QColor mAutoCommentBackgroundColor;\n    QColor mCommentColor;\n    QColor mCommentBackgroundColor;\n    QColor mLabelColor;\n    QColor mLabelBackgroundColor;\n    QColor graphNodeColor;\n    QColor mAddressColor;\n    QColor mAddressBackgroundColor;\n    QColor mCipColor;\n    QColor mBreakpointColor;\n    QColor mDisabledBreakpointColor;\n\n    QAction actionExportGraph;\n\n    GraphView::Layout graphLayout;\n    QMenu *layoutMenu;\n    QAction *horizontalLayoutAction;\n\nprivate:\n    void colorsUpdatedSlot();\n};\n\n#endif // CUTTER_GRAPHVIEW_H\n"
  },
  {
    "path": "src/widgets/CutterTreeView.cpp",
    "content": "#include \"CutterTreeView.h\"\n#include \"ui_CutterTreeView.h\"\n\nCutterTreeView::CutterTreeView(QWidget *parent) : QTreeView(parent), ui(new Ui::CutterTreeView())\n{\n    ui->setupUi(this);\n    applyCutterStyle(this);\n}\n\nCutterTreeView::~CutterTreeView() {}\n\nvoid CutterTreeView::applyCutterStyle(QTreeView *view)\n{\n    view->setSelectionMode(QAbstractItemView::ExtendedSelection);\n    view->setUniformRowHeights(true);\n}\n"
  },
  {
    "path": "src/widgets/CutterTreeView.h",
    "content": "#ifndef CUTTERTREEVIEW_H\n#define CUTTERTREEVIEW_H\n\n#include \"core/CutterCommon.h\"\n\n#include <memory>\n#include <QAbstractItemView>\n#include <QTreeView>\n\nnamespace Ui {\nclass CutterTreeView;\n}\n\nclass CUTTER_EXPORT CutterTreeView : public QTreeView\n{\n    Q_OBJECT\n\npublic:\n    explicit CutterTreeView(QWidget *parent = nullptr);\n    ~CutterTreeView();\n\n    static void applyCutterStyle(QTreeView *view);\n\nprivate:\n    std::unique_ptr<Ui::CutterTreeView> ui;\n};\n\n#endif // CUTTERTREEVIEW_H\n"
  },
  {
    "path": "src/widgets/CutterTreeView.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>CutterTreeView</class>\n <widget class=\"QTreeView\" name=\"CutterTreeView\">\n </widget>\n</ui>\n"
  },
  {
    "path": "src/widgets/CutterTreeWidget.cpp",
    "content": "#include \"CutterTreeWidget.h\"\n#include \"core/MainWindow.h\"\n\nCutterTreeWidget::CutterTreeWidget(QObject *parent) : QObject(parent), bar(nullptr) {}\n\nvoid CutterTreeWidget::addStatusBar(QVBoxLayout *pos)\n{\n    if (!bar) {\n        bar = new QStatusBar;\n        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);\n        bar->setSizePolicy(sizePolicy);\n        pos->addWidget(bar);\n    }\n}\n\nvoid CutterTreeWidget::showItemsNumber(int count)\n{\n    if (bar) {\n        bar->showMessage(tr(\"%1 Items\").arg(count));\n    }\n}\n\nvoid CutterTreeWidget::showStatusBar(bool show)\n{\n    bar->setVisible(show);\n}\n\nCutterTreeWidget::~CutterTreeWidget() {}\n"
  },
  {
    "path": "src/widgets/CutterTreeWidget.h",
    "content": "#ifndef CUTTERTREEWIDGET_H\n#define CUTTERTREEWIDGET_H\n\n#include \"core/CutterCommon.h\"\n\n#include <QStatusBar>\n#include <QVBoxLayout>\n\nclass MainWindow;\n\nclass CUTTER_EXPORT CutterTreeWidget : public QObject\n{\n\n    Q_OBJECT\n\npublic:\n    explicit CutterTreeWidget(QObject *parent = nullptr);\n    ~CutterTreeWidget();\n    void addStatusBar(QVBoxLayout *pos);\n    void showItemsNumber(int count);\n    void showStatusBar(bool show);\n\nprivate:\n    QStatusBar *bar;\n};\n#endif // CUTTERTREEWIDGET_H\n"
  },
  {
    "path": "src/widgets/Dashboard.cpp",
    "content": "#include \"Dashboard.h\"\n#include \"ui_Dashboard.h\"\n#include \"common/Helpers.h\"\n#include \"common/JsonModel.h\"\n#include \"common/TempConfig.h\"\n#include \"dialogs/VersionInfoDialog.h\"\n\n#include \"core/MainWindow.h\"\n#include \"CutterTreeView.h\"\n\n#include <QDebug>\n#include <QJsonArray>\n#include <QStringList>\n#include <QJsonObject>\n#include <QJsonDocument>\n#include <QFile>\n#include <QLayoutItem>\n#include <QString>\n#include <QMessageBox>\n#include <QDialog>\n#include <QTreeWidget>\n\nDashboard::Dashboard(MainWindow *main) : CutterDockWidget(main), ui(new Ui::Dashboard)\n{\n    ui->setupUi(this);\n\n    connect(Core(), &CutterCore::refreshAll, this, &Dashboard::updateContents);\n}\n\nDashboard::~Dashboard() {}\n\nvoid Dashboard::updateContents()\n{\n    RzCoreLocked core(Core());\n    int fd = rz_io_fd_get_current(core->io);\n    RzIODesc *desc = rz_io_desc_get(core->io, fd);\n    setPlainText(this->ui->modeEdit, desc ? rz_str_rwx_i(desc->perm & RZ_PERM_RWX) : \"\");\n\n    RzBinFile *bf = rz_bin_cur(core->bin);\n    if (bf) {\n        setPlainText(this->ui->compilationDateEdit, rz_core_bin_get_compile_time(bf));\n        if (bf->o) {\n            char *relco_buf = sdb_get(bf->o->kv, \"elf.relro\");\n            if (RZ_STR_ISNOTEMPTY(relco_buf)) {\n                QString relro = QString(relco_buf).section(QLatin1Char(' '), 0, 0);\n                relro[0] = relro[0].toUpper();\n                setPlainText(this->ui->relroEdit, relro);\n            } else {\n                setPlainText(this->ui->relroEdit, \"N/A\");\n            }\n        }\n    }\n\n    // Add file hashes, analysis info and libraries\n    RzBinObject *bobj = rz_bin_cur_object(core->bin);\n    const RzBinInfo *binInfo = bobj ? rz_bin_object_get_info(bobj) : nullptr;\n\n    setPlainText(ui->fileEdit, binInfo ? binInfo->file : \"\");\n    setPlainText(ui->formatEdit, binInfo ? binInfo->rclass : \"\");\n    setPlainText(ui->typeEdit, binInfo ? binInfo->type : \"\");\n    setPlainText(ui->archEdit, binInfo ? binInfo->arch : \"\");\n    setPlainText(ui->langEdit, binInfo ? binInfo->lang : \"\");\n    setPlainText(ui->classEdit, binInfo ? binInfo->bclass : \"\");\n    setPlainText(ui->machineEdit, binInfo ? binInfo->machine : \"\");\n    setPlainText(ui->osEdit, binInfo ? binInfo->os : \"\");\n    setPlainText(ui->subsysEdit, binInfo ? binInfo->subsystem : \"\");\n    setPlainText(ui->compilerEdit, binInfo ? binInfo->compiler : \"\");\n    setPlainText(ui->bitsEdit, binInfo ? QString::number(binInfo->bits) : \"\");\n    setPlainText(ui->baddrEdit, bf ? RzAddressString(rz_bin_file_get_baddr(bf)) : \"\");\n    setPlainText(ui->sizeEdit, bf ? qhelpers::formatBytecount(bf->size) : \"\");\n    setPlainText(ui->fdEdit, bf ? QString::number(bf->fd) : \"\");\n\n    // Setting the value of \"Endianness\"\n    const char *endian = binInfo ? (binInfo->big_endian ? \"BE\" : \"LE\") : \"\";\n    setPlainText(this->ui->endianEdit, endian);\n\n    // Setting boolean values\n    setRzBinInfo(binInfo);\n\n    // Setting the value of \"static\"\n    int static_value = rz_bin_is_static(core->bin);\n    setPlainText(ui->staticEdit, setBoolText(static_value));\n\n    const RzPVector *hashes = bf ? rz_bin_file_compute_hashes(core->bin, bf, UT64_MAX) : nullptr;\n\n    // Delete hashesWidget if it isn't null to avoid duplicate components\n    if (hashesWidget) {\n        hashesWidget->deleteLater();\n    }\n\n    // Define dynamic components to hold the hashes\n    hashesWidget = new QWidget();\n    QFormLayout *hashesLayout = new QFormLayout;\n    hashesWidget->setLayout(hashesLayout);\n    ui->hashesVerticalLayout->addWidget(hashesWidget);\n\n    // Add hashes as a pair of Hash Name : Hash Value.\n    if (hashes != nullptr) {\n        for (const auto &hash : CutterPVector<RzBinFileHash>(hashes)) {\n            // Create a bold QString with the hash name uppercased\n            QString label = QString(\"<b>%1:</b>\").arg(QString(hash->type).toUpper());\n\n            // Define a Read-Only line edit to display the hash value\n            QLineEdit *hashLineEdit = new QLineEdit();\n            hashLineEdit->setReadOnly(true);\n            hashLineEdit->setText(hash->hex);\n\n            // Set cursor position to begining to avoid long hashes (e.g sha256)\n            // to look truncated at the begining\n            hashLineEdit->setCursorPosition(0);\n\n            // Add both controls to a form layout in a single row\n            hashesLayout->addRow(new QLabel(label), hashLineEdit);\n        }\n    }\n\n    st64 fcns = rz_list_length(core->analysis->fcns);\n    st64 strs = rz_flag_count(core->flags, \"str.*\");\n    st64 syms = rz_flag_count(core->flags, \"sym.*\");\n    st64 imps = rz_flag_count(core->flags, \"sym.imp.*\");\n    st64 code = rz_core_analysis_code_count(core);\n    st64 covr = rz_core_analysis_coverage_count(core);\n    st64 call = rz_core_analysis_calls_count(core);\n    ut64 xrfs = rz_analysis_xrefs_count(core->analysis);\n    double precentage = (code > 0) ? (covr * 100.0 / code) : 0;\n\n    setPlainText(ui->functionsLineEdit, QString::number(fcns));\n    setPlainText(ui->xRefsLineEdit, QString::number(xrfs));\n    setPlainText(ui->callsLineEdit, QString::number(call));\n    setPlainText(ui->stringsLineEdit, QString::number(strs));\n    setPlainText(ui->symbolsLineEdit, QString::number(syms));\n    setPlainText(ui->importsLineEdit, QString::number(imps));\n    setPlainText(ui->coverageLineEdit, QString::number(covr) + \" bytes\");\n    setPlainText(ui->codeSizeLineEdit, QString::number(code) + \" bytes\");\n    setPlainText(ui->percentageLineEdit, QString::number(precentage) + \"%\");\n\n    ui->libraryList->setPlainText(\"\");\n    const RzPVector *libs = bf ? rz_bin_object_get_libs(bf->o) : nullptr;\n    if (libs) {\n        QString libText;\n        bool first = true;\n        for (const auto &lib : CutterPVector<char>(libs)) {\n            if (!first) {\n                libText.append(\"\\n\");\n            }\n            libText.append(lib);\n            first = false;\n        }\n        ui->libraryList->setPlainText(libText);\n    }\n\n    // Check if signature info and version info available\n    if (!Core()->getSignatureInfo().size()) {\n        ui->certificateButton->setEnabled(false);\n    }\n    ui->versioninfoButton->setEnabled(Core()->existsFileInfo());\n}\n\nvoid Dashboard::on_certificateButton_clicked()\n{\n    QDialog dialog(this);\n    auto view = new QTreeWidget(&dialog);\n    view->setHeaderLabels({ tr(\"Key\"), tr(\"Value\") });\n    view->addTopLevelItem(Cutter::jsonTreeWidgetItem(QString(\"<%1>\").arg(tr(\"root\")),\n                                                     Core()->getSignatureInfo()));\n    CutterTreeView::applyCutterStyle(view);\n    view->expandAll();\n    view->resize(900, 600);\n    QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);\n    sizePolicy.setHorizontalStretch(0);\n    sizePolicy.setVerticalStretch(0);\n    sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());\n    dialog.setSizePolicy(sizePolicy);\n    dialog.setMinimumSize(QSize(900, 600));\n    dialog.setMaximumSize(QSize(900, 600));\n    dialog.setSizeGripEnabled(false);\n    dialog.setWindowTitle(tr(\"Certificates\"));\n    dialog.exec();\n}\n\nvoid Dashboard::on_versioninfoButton_clicked()\n{\n\n    static QDialog *infoDialog = nullptr;\n\n    if (!infoDialog) {\n        infoDialog = new VersionInfoDialog(this);\n    }\n\n    if (!infoDialog->isVisible()) {\n        infoDialog->show();\n    }\n}\n\n/**\n * @brief Set the text of a QLineEdit. If no text, then \"N/A\" is set.\n * @param textBox\n * @param text\n */\nvoid Dashboard::setPlainText(QLineEdit *textBox, const QString &text)\n{\n    if (!text.isEmpty()) {\n        textBox->setText(text);\n    } else {\n        textBox->setText(tr(\"N/A\"));\n    }\n\n    textBox->setCursorPosition(0);\n}\n\n/**\n * @brief Setting boolean values of binary information in dashboard\n * @param RzBinInfo\n */\nvoid Dashboard::setRzBinInfo(const RzBinInfo *binInfo)\n{\n    setPlainText(ui->vaEdit, binInfo ? setBoolText(binInfo->has_va) : \"\");\n    setPlainText(ui->canaryEdit, binInfo ? setBoolText(binInfo->has_canary) : \"\");\n    setPlainText(ui->cryptoEdit, binInfo ? setBoolText(binInfo->has_crypto) : \"\");\n    setPlainText(ui->nxEdit, binInfo ? setBoolText(binInfo->has_nx) : \"\");\n    setPlainText(ui->picEdit, binInfo ? setBoolText(binInfo->has_pi) : \"\");\n    setPlainText(ui->strippedEdit,\n                 binInfo ? setBoolText(RZ_BIN_DBG_STRIPPED & binInfo->dbg_info) : \"\");\n    setPlainText(ui->relocsEdit, binInfo ? setBoolText(RZ_BIN_DBG_RELOCS & binInfo->dbg_info) : \"\");\n}\n\n/**\n * @brief Set the text of a QLineEdit as True, False\n * @param boolean value\n */\nQString Dashboard::setBoolText(bool value)\n{\n    return value ? tr(\"True\") : tr(\"False\");\n}\n"
  },
  {
    "path": "src/widgets/Dashboard.h",
    "content": "#ifndef DASHBOARD_H\n#define DASHBOARD_H\n\n#include <QFormLayout>\n#include <memory>\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n\nQT_BEGIN_NAMESPACE\nQT_FORWARD_DECLARE_CLASS(QLineEdit)\nQT_FORWARD_DECLARE_CLASS(QJsonObject)\nQT_END_NAMESPACE\n\nclass MainWindow;\n\nnamespace Ui {\nclass Dashboard;\n}\n\nclass Dashboard : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit Dashboard(MainWindow *main);\n    ~Dashboard();\n\nprivate slots:\n    void updateContents();\n    void on_certificateButton_clicked();\n    void on_versioninfoButton_clicked();\n\nprivate:\n    std::unique_ptr<Ui::Dashboard> ui;\n    void setPlainText(QLineEdit *textBox, const QString &text);\n    void setRzBinInfo(const RzBinInfo *binInfo);\n    QString setBoolText(bool value);\n\n    QWidget *hashesWidget = nullptr;\n};\n\n#endif // DASHBOARD_H\n"
  },
  {
    "path": "src/widgets/Dashboard.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>Dashboard</class>\n <widget class=\"QDockWidget\" name=\"Dashboard\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1055</width>\n    <height>999</height>\n   </rect>\n  </property>\n  <property name=\"styleSheet\">\n   <string notr=\"true\"/>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dashboard</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QHBoxLayout\" name=\"horizontalLayout\" stretch=\"0\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QScrollArea\" name=\"scrollArea\">\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"frameShadow\">\n       <enum>QFrame::Plain</enum>\n      </property>\n      <property name=\"verticalScrollBarPolicy\">\n       <enum>Qt::ScrollBarAsNeeded</enum>\n      </property>\n      <property name=\"horizontalScrollBarPolicy\">\n       <enum>Qt::ScrollBarAlwaysOff</enum>\n      </property>\n      <property name=\"widgetResizable\">\n       <bool>true</bool>\n      </property>\n      <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n       <property name=\"geometry\">\n        <rect>\n         <x>0</x>\n         <y>0</y>\n         <width>1055</width>\n         <height>980</height>\n        </rect>\n       </property>\n       <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n        <property name=\"spacing\">\n         <number>0</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <spacer name=\"horizontalSpacer\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>39</width>\n            <height>20</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n        <item>\n         <widget class=\"QFrame\" name=\"mainFrame\">\n          <property name=\"minimumSize\">\n           <size>\n            <width>800</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"frameShape\">\n           <enum>QFrame::NoFrame</enum>\n          </property>\n          <property name=\"frameShadow\">\n           <enum>QFrame::Plain</enum>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n           <item>\n            <widget class=\"QLabel\" name=\"label\">\n             <property name=\"font\">\n              <font>\n               <pointsize>19</pointsize>\n               <weight>75</weight>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>OVERVIEW</string>\n             </property>\n             <property name=\"textInteractionFlags\">\n              <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"Line\" name=\"line\">\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLabel\" name=\"label_2\">\n             <property name=\"font\">\n              <font>\n               <pointsize>17</pointsize>\n               <weight>75</weight>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Info</string>\n             </property>\n             <property name=\"textInteractionFlags\">\n              <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n             <property name=\"spacing\">\n              <number>10</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <layout class=\"QFormLayout\" name=\"formLayout\">\n               <property name=\"fieldGrowthPolicy\">\n                <enum>QFormLayout::ExpandingFieldsGrow</enum>\n               </property>\n               <property name=\"labelAlignment\">\n                <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n               </property>\n               <property name=\"verticalSpacing\">\n                <number>3</number>\n               </property>\n               <item row=\"0\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_3\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>File:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"0\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"fileEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"1\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_10\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Format:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"1\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"formatEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"4\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_11\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Mode:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"4\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"modeEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"5\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_12\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Size:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"6\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_13\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Type:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"6\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"typeEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"3\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_15\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Class:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"3\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"classEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"7\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_18\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Language:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"7\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"langEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"2\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_19\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Bits:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"2\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"bitsEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"5\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"sizeEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <layout class=\"QFormLayout\" name=\"formLayout_4\">\n               <property name=\"fieldGrowthPolicy\">\n                <enum>QFormLayout::ExpandingFieldsGrow</enum>\n               </property>\n               <property name=\"labelAlignment\">\n                <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n               </property>\n               <property name=\"verticalSpacing\">\n                <number>3</number>\n               </property>\n               <property name=\"topMargin\">\n                <number>0</number>\n               </property>\n               <item row=\"0\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_4\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>FD:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"0\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"fdEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"1\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_20\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Base addr:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"1\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"baddrEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"2\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_21\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Virtual addr:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"2\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"vaEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"3\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_22\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Canary:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"3\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"canaryEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"4\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_23\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Crypto:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"4\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"cryptoEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"5\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_24\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>NX bit:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"5\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"nxEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"6\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_25\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>PIC:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"6\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"picEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"7\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_26\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Static:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"8\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_34\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Relro:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"8\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"relroEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"7\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"staticEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <layout class=\"QFormLayout\" name=\"formLayout_3\">\n               <property name=\"fieldGrowthPolicy\">\n                <enum>QFormLayout::ExpandingFieldsGrow</enum>\n               </property>\n               <property name=\"labelAlignment\">\n                <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n               </property>\n               <property name=\"verticalSpacing\">\n                <number>3</number>\n               </property>\n               <item row=\"1\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_14\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Architecture:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"1\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"archEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"2\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_16\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Machine:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"2\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"machineEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"3\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_17\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>OS:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"3\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"osEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"4\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_30\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Subsystem:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"4\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"subsysEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"5\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_27\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Stripped:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"5\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"strippedEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"6\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_29\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Relocs:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"6\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"relocsEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"7\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_28\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Endianness:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"7\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"endianEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"8\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_31\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Compiled:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"8\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"compilationDateEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"9\" column=\"0\">\n                <widget class=\"QLabel\" name=\"label_5\">\n                 <property name=\"font\">\n                  <font>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Compiler:</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"9\" column=\"1\">\n                <widget class=\"QLineEdit\" name=\"compilerEdit\">\n                 <property name=\"text\">\n                  <string>--</string>\n                 </property>\n                 <property name=\"frame\">\n                  <bool>false</bool>\n                 </property>\n                 <property name=\"readOnly\">\n                  <bool>true</bool>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer_2\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeType\">\n              <enum>QSizePolicy::Fixed</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>5</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n           <item>\n            <widget class=\"Line\" name=\"line_3\">\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n             <item>\n              <widget class=\"QPushButton\" name=\"certificateButton\">\n               <property name=\"enabled\">\n                <bool>true</bool>\n               </property>\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"text\">\n                <string>Certificates</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"versioninfoButton\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"text\">\n                <string>Version info</string>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <widget class=\"Line\" name=\"line_2\">\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\" stretch=\"2,1\">\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QLabel\" name=\"label_7\">\n               <property name=\"font\">\n                <font>\n                 <pointsize>17</pointsize>\n                 <weight>75</weight>\n                 <bold>true</bold>\n                </font>\n               </property>\n               <property name=\"text\">\n                <string>Hashes</string>\n               </property>\n               <property name=\"textInteractionFlags\">\n                <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"label_6\">\n               <property name=\"font\">\n                <font>\n                 <pointsize>17</pointsize>\n                 <weight>75</weight>\n                 <bold>true</bold>\n                </font>\n               </property>\n               <property name=\"text\">\n                <string>Libraries</string>\n               </property>\n               <property name=\"textInteractionFlags\">\n                <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\" stretch=\"2,1\">\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n               <property name=\"rightMargin\">\n                <number>5</number>\n               </property>\n               <item>\n                <layout class=\"QVBoxLayout\" name=\"hashesVerticalLayout\"/>\n               </item>\n               <item>\n                <widget class=\"QLabel\" name=\"label_9\">\n                 <property name=\"font\">\n                  <font>\n                   <pointsize>17</pointsize>\n                   <weight>75</weight>\n                   <bold>true</bold>\n                  </font>\n                 </property>\n                 <property name=\"text\">\n                  <string>Analysis info</string>\n                 </property>\n                 <property name=\"textInteractionFlags\">\n                  <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <layout class=\"QFormLayout\" name=\"formLayout_5\">\n                 <property name=\"fieldGrowthPolicy\">\n                  <enum>QFormLayout::ExpandingFieldsGrow</enum>\n                 </property>\n                 <property name=\"verticalSpacing\">\n                  <number>3</number>\n                 </property>\n                 <item row=\"0\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"functionsLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Functions:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"functionsLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"xRefsLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>X-Refs:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"xRefsLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"callsLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Calls:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"callsLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"3\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"stringsLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Strings:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"3\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"stringsLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"4\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"symbolsLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Symbols:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"4\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"symbolsLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"5\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"importsLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Imports:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"5\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"importsLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"6\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"coverageLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Analysis coverage:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"6\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"coverageLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"7\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"codeSizeLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Code size:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"7\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"codeSizeLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"8\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"percentageLabel\">\n                   <property name=\"font\">\n                    <font>\n                     <weight>75</weight>\n                     <bold>true</bold>\n                    </font>\n                   </property>\n                   <property name=\"text\">\n                    <string>Coverage percent:</string>\n                   </property>\n                   <property name=\"textInteractionFlags\">\n                    <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"8\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"percentageLineEdit\">\n                   <property name=\"frame\">\n                    <bool>false</bool>\n                   </property>\n                   <property name=\"readOnly\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                </layout>\n               </item>\n               <item>\n                <spacer name=\"verticalSpacer_3\">\n                 <property name=\"orientation\">\n                  <enum>Qt::Vertical</enum>\n                 </property>\n                 <property name=\"sizeHint\" stdset=\"0\">\n                  <size>\n                   <width>20</width>\n                   <height>40</height>\n                  </size>\n                 </property>\n                </spacer>\n               </item>\n              </layout>\n             </item>\n             <item>\n              <widget class=\"QPlainTextEdit\" name=\"libraryList\">\n               <property name=\"horizontalScrollBarPolicy\">\n                <enum>Qt::ScrollBarAlwaysOff</enum>\n               </property>\n               <property name=\"lineWrapMode\">\n                <enum>QPlainTextEdit::NoWrap</enum>\n               </property>\n               <property name=\"textInteractionFlags\">\n                <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <spacer name=\"verticalSpacer\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n          </layout>\n         </widget>\n        </item>\n        <item>\n         <spacer name=\"horizontalSpacer_2\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>40</width>\n            <height>20</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n       </layout>\n      </widget>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/DebugActions.cpp",
    "content": "#include \"DebugActions.h\"\n#include \"core/MainWindow.h\"\n#include \"dialogs/AttachProcDialog.h\"\n#include \"dialogs/NativeDebugDialog.h\"\n#include \"common/Configuration.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QPainter>\n#include <QMenu>\n#include <QList>\n#include <QFileInfo>\n#include <QToolBar>\n#include <QToolButton>\n#include <QSettings>\n\nDebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) : QObject(main), main(main)\n{\n    setObjectName(\"DebugActions\");\n    // setIconSize(QSize(16, 16));\n\n    // define icons\n    QIcon startEmulIcon = QIcon(\":/img/icons/play_light_emul.svg\");\n    QIcon startAttachIcon = QIcon(\":/img/icons/play_light_attach.svg\");\n    QIcon startRemoteIcon = QIcon(\":/img/icons/play_light_remote.svg\");\n    QIcon continueBackIcon = QIcon(\":/img/icons/reverse_continue.svg\");\n    QIcon stepBackIcon = QIcon(\":/img/icons/reverse_step.svg\");\n    startTraceIcon = QIcon(\":/img/icons/start_trace.svg\");\n    stopTraceIcon = QIcon(\":/img/icons/stop_trace.svg\");\n    stopIcon = QIcon(\":/img/icons/media-stop_light.svg\");\n    restartIcon = QIcon(\":/img/icons/spin_light.svg\");\n    detachIcon = QIcon(\":/img/icons/detach_debugger.svg\");\n    startDebugIcon = QIcon(\":/img/icons/play_light_debug.svg\");\n    continueIcon = QIcon(\":/img/icons/media-skip-forward_light.svg\");\n    suspendIcon = QIcon(\":/img/icons/media-suspend_light.svg\");\n\n    // define action labels\n    QString startEmulLabel = tr(\"Start emulation\");\n    QString startAttachLabel = tr(\"Attach to process\");\n    QString startRemoteLabel = tr(\"Connect to a remote debugger\");\n    QString stopDebugLabel = tr(\"Stop debug\");\n    QString stopEmulLabel = tr(\"Stop emulation\");\n    QString restartEmulLabel = tr(\"Restart emulation\");\n    QString continueUMLabel = tr(\"Continue until main\");\n    QString continueUCLabel = tr(\"Continue until call\");\n    QString continueUSLabel = tr(\"Continue until syscall\");\n    startTraceLabel = tr(\"Start trace session\");\n    stopTraceLabel = tr(\"Stop trace session\");\n    suspendLabel = tr(\"Suspend the process\");\n    continueLabel = tr(\"Continue\");\n    restartDebugLabel = tr(\"Restart program\");\n    startDebugLabel = tr(\"Start debug\");\n\n    // define actions\n    actionStart = Shortcuts()->makeAction(\"Debug.start\", this);\n    actionContinue = Shortcuts()->makeAction(\"Debug.continue\", this);\n    actionContinueBack = Shortcuts()->makeAction(\"Debug.continueBack\", this);\n    actionStep = Shortcuts()->makeAction(\"Debug.step\", this);\n    actionStepOver = Shortcuts()->makeAction(\"Debug.stepOver\", this);\n    actionStepOut = Shortcuts()->makeAction(\"Debug.stepOut\", this);\n    actionStepBack = Shortcuts()->makeAction(\"Debug.stepBack\", this);\n    actionStart->setIcon(startDebugIcon);\n    actionContinue->setIcon(continueIcon);\n    actionContinueBack->setIcon(continueBackIcon);\n    actionStepBack->setIcon(stepBackIcon);\n\n    actionStartEmul = new QAction(startEmulIcon, startEmulLabel, this);\n    actionAttach = new QAction(startAttachIcon, startAttachLabel, this);\n    actionStartRemote = new QAction(startRemoteIcon, startRemoteLabel, this);\n    actionStop = new QAction(stopIcon, stopDebugLabel, this);\n    actionContinueUntilMain = new QAction(continueUMLabel, this);\n    actionContinueUntilCall = new QAction(continueUCLabel, this);\n    actionContinueUntilSyscall = new QAction(continueUSLabel, this);\n    actionTrace = new QAction(startTraceIcon, startTraceLabel, this);\n\n    QToolButton *startButton = new QToolButton;\n    startButton->setPopupMode(QToolButton::MenuButtonPopup);\n    connect(startButton, &QToolButton::triggered, startButton, &QToolButton::setDefaultAction);\n    QMenu *startMenu = new QMenu(startButton);\n\n    startMenu->addAction(actionStart);\n    startMenu->addAction(actionStartEmul);\n    startMenu->addAction(actionAttach);\n    startMenu->addAction(actionStartRemote);\n    startButton->setDefaultAction(actionStart);\n    startButton->setMenu(startMenu);\n\n    continueUntilButton = new QToolButton;\n    continueUntilButton->setPopupMode(QToolButton::MenuButtonPopup);\n    connect(continueUntilButton, &QToolButton::triggered, continueUntilButton,\n            &QToolButton::setDefaultAction);\n    QMenu *continueUntilMenu = new QMenu(continueUntilButton);\n    continueUntilMenu->addAction(actionContinueUntilMain);\n    continueUntilMenu->addAction(actionContinueUntilCall);\n    continueUntilMenu->addAction(actionContinueUntilSyscall);\n    continueUntilButton->setMenu(continueUntilMenu);\n    continueUntilButton->setDefaultAction(actionContinueUntilMain);\n\n    // define toolbar widgets and actions\n    toolBar->addWidget(startButton);\n    toolBar->addAction(actionContinue);\n    toolBar->addAction(actionStop);\n    actionAllContinues = toolBar->addWidget(continueUntilButton);\n    toolBar->addAction(actionStepOver);\n    toolBar->addAction(actionStep);\n    toolBar->addAction(actionStepOut);\n    toolBar->addAction(actionStepBack);\n    toolBar->addAction(actionContinueBack);\n    toolBar->addAction(actionTrace);\n\n    allActions = { actionStop,\n                   actionAllContinues,\n                   actionContinue,\n                   actionContinueUntilCall,\n                   actionContinueUntilMain,\n                   actionContinueUntilSyscall,\n                   actionStep,\n                   actionStepOut,\n                   actionStepOver,\n                   actionContinueBack,\n                   actionStepBack,\n                   actionTrace };\n\n    // Hide all actions\n    setAllActionsVisible(false);\n\n    // Toggle all buttons except reverse step/continue which are handled separately and\n    // restart, suspend(=continue) and stop since those are necessary to avoid freezing\n    toggleActions = { actionStepOver,\n                      actionStep,\n                      actionStepOut,\n                      actionContinueUntilMain,\n                      actionContinueUntilCall,\n                      actionContinueUntilSyscall,\n                      actionTrace };\n    toggleConnectionActions = { actionAttach, actionStartRemote };\n    reverseActions = { actionStepBack, actionContinueBack };\n\n    connect(Core(), &CutterCore::debugProcessFinished, this, [=](int pid) {\n        QMessageBox msgBox(main);\n        msgBox.setText(tr(\"Debugged process exited (\") + QString::number(pid) + \")\");\n        msgBox.exec();\n    });\n\n    connect(Core(), &CutterCore::debugTaskStateChanged, this, [=]() {\n        bool disableToolbar = Core()->isDebugTaskInProgress();\n        if (Core()->currentlyDebugging) {\n            for (QAction *a : toggleActions) {\n                a->setDisabled(disableToolbar);\n            }\n            // Suspend should only be available when other icons are disabled\n            if (disableToolbar) {\n                actionContinue->setText(suspendLabel);\n                actionContinue->setIcon(suspendIcon);\n            } else {\n                actionContinue->setText(continueLabel);\n                actionContinue->setIcon(continueIcon);\n            }\n            for (QAction *a : reverseActions) {\n                a->setVisible(Core()->currentlyTracing);\n                a->setDisabled(disableToolbar);\n            }\n        } else {\n            for (QAction *a : toggleConnectionActions) {\n                a->setDisabled(disableToolbar);\n            }\n        }\n    });\n\n    connect(actionStop, &QAction::triggered, Core(), &CutterCore::stopDebug);\n    connect(actionStop, &QAction::triggered, [=]() {\n        actionStart->setVisible(true);\n        actionStartEmul->setVisible(true);\n        actionAttach->setVisible(true);\n        actionStartRemote->setVisible(true);\n        actionStop->setText(stopDebugLabel);\n        actionStop->setIcon(stopIcon);\n        actionStart->setText(startDebugLabel);\n        actionStart->setIcon(startDebugIcon);\n        actionStartEmul->setText(startEmulLabel);\n        actionStartEmul->setIcon(startEmulIcon);\n        continueUntilButton->setDefaultAction(actionContinueUntilMain);\n        setAllActionsVisible(false);\n    });\n\n    connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);\n    connect(actionStepBack, &QAction::triggered, Core(), &CutterCore::stepBackDebug);\n\n    connect(actionStart, &QAction::triggered, this, &DebugActions::startDebug);\n\n    connect(actionAttach, &QAction::triggered, this, &DebugActions::attachProcessDialog);\n    connect(actionStartRemote, &QAction::triggered, this, &DebugActions::attachRemoteDialog);\n    connect(Core(), &CutterCore::attachedRemote, this, &DebugActions::onAttachedRemoteDebugger);\n    connect(actionStartEmul, &QAction::triggered, Core(), &CutterCore::startEmulation);\n    connect(actionStartEmul, &QAction::triggered, [=]() {\n        setAllActionsVisible(true);\n        actionStart->setVisible(false);\n        actionAttach->setVisible(false);\n        actionStartRemote->setVisible(false);\n        actionContinueUntilMain->setVisible(false);\n        actionStepOut->setVisible(false);\n        continueUntilButton->setDefaultAction(actionContinueUntilSyscall);\n        actionStartEmul->setText(restartEmulLabel);\n        actionStartEmul->setIcon(restartIcon);\n        actionStop->setText(stopEmulLabel);\n        // Reverse debug actions aren't visible until we start tracing\n        for (QAction *a : reverseActions) {\n            a->setVisible(false);\n        }\n    });\n    connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);\n    connect(actionStepOut, &QAction::triggered, Core(), &CutterCore::stepOutDebug);\n    connect(actionContinueUntilMain, &QAction::triggered, this, &DebugActions::continueUntilMain);\n    connect(actionContinueUntilCall, &QAction::triggered, Core(), &CutterCore::continueUntilCall);\n    connect(actionContinueUntilSyscall, &QAction::triggered, Core(),\n            &CutterCore::continueUntilSyscall);\n    connect(actionContinueBack, &QAction::triggered, Core(), &CutterCore::continueBackDebug);\n    connect(actionContinue, &QAction::triggered, Core(), [=]() {\n        // Switch between continue and suspend depending on the debugger's state\n        if (Core()->isDebugTaskInProgress()) {\n            Core()->suspendDebug();\n        } else {\n            Core()->continueDebug();\n        }\n    });\n\n    connect(actionTrace, &QAction::triggered, Core(), [=]() {\n        // Check if a debug session was created to switch between start and stop\n        if (!Core()->currentlyTracing) {\n            Core()->startTraceSession();\n            actionTrace->setText(stopTraceLabel);\n            actionTrace->setIcon(stopTraceIcon);\n        } else {\n            Core()->stopTraceSession();\n            actionTrace->setText(startTraceLabel);\n            actionTrace->setIcon(startTraceIcon);\n        }\n    });\n\n    connect(Config(), &Configuration::interfaceThemeChanged, this, &DebugActions::chooseThemeIcons);\n    chooseThemeIcons();\n}\n\nvoid DebugActions::setButtonVisibleIfMainExists()\n{\n    RzCoreLocked core = Core()->lock();\n    // if main is not a flag we hide the continue until main button\n    if (!rz_flag_get(core->flags, \"sym.main\") && !rz_flag_get(core->flags, \"main\")) {\n        actionContinueUntilMain->setVisible(false);\n        continueUntilButton->setDefaultAction(actionContinueUntilCall);\n    }\n}\n\nvoid DebugActions::showDebugWarning()\n{\n    if (!acceptedDebugWarning) {\n        acceptedDebugWarning = true;\n        QMessageBox msgBox(main);\n        msgBox.setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);\n        msgBox.setText(tr(\"Debug is currently in beta.\\n\")\n                       + tr(\"If you encounter any problems or have suggestions, please submit an \"\n                            \"issue to https://github.com/rizinorg/cutter/issues\"));\n        msgBox.exec();\n    }\n}\n\nvoid DebugActions::continueUntilMain()\n{\n    RzCoreLocked core(Core()->lock());\n    RzFlagItem *main_flag = rz_flag_get(core->flags, \"sym.main\");\n    if (!main_flag) {\n        main_flag = rz_flag_get(core->flags, \"main\");\n        if (!main_flag) {\n            return;\n        }\n    }\n    Core()->continueUntilDebug(main_flag->offset);\n}\n\nvoid DebugActions::attachRemoteDebugger()\n{\n    QString stopAttachLabel = tr(\"Detach from process\");\n    // Hide unwanted buttons\n    setAllActionsVisible(true);\n    actionStart->setVisible(false);\n    actionStartRemote->setVisible(false);\n    actionStartEmul->setVisible(false);\n    actionStop->setText(stopAttachLabel);\n}\n\nvoid DebugActions::onAttachedRemoteDebugger(bool successfully)\n{\n    // TODO(#2829): Investigate why this is happening\n    if (remoteDialog == nullptr)\n        return;\n\n    if (!successfully) {\n        QMessageBox msgBox(main);\n        msgBox.setText(tr(\"Error connecting.\"));\n        msgBox.exec();\n        attachRemoteDialog();\n    } else {\n        QSettings settings;\n        QStringList ips = settings.value(\"recentIpList\").toStringList();\n        ips.removeAll(remoteDialog->getUri());\n        ips.prepend(remoteDialog->getUri());\n        settings.setValue(\"recentIpList\", ips);\n\n        delete remoteDialog;\n        remoteDialog = nullptr;\n        attachRemoteDebugger();\n    }\n}\n\nvoid DebugActions::attachRemoteDialog()\n{\n    showDebugWarning();\n\n    if (!remoteDialog) {\n        remoteDialog = new RemoteDebugDialog(main);\n    }\n    QMessageBox msgBox(main);\n    bool success = false;\n    while (!success) {\n        success = true;\n        if (remoteDialog->exec()) {\n            if (!remoteDialog->validate()) {\n                success = false;\n                continue;\n            }\n\n            Core()->attachRemote(remoteDialog->getUri());\n        }\n    }\n}\n\nvoid DebugActions::attachProcessDialog()\n{\n    showDebugWarning();\n\n    AttachProcDialog dialog(main);\n    bool success = false;\n    while (!success) {\n        success = true;\n        if (dialog.exec()) {\n            int pid = dialog.getPID();\n            if (pid >= 0) {\n                attachProcess(pid);\n            } else {\n                success = false;\n                QMessageBox msgBox(main);\n                msgBox.setText(tr(\"Error attaching. No process selected!\"));\n                msgBox.exec();\n            }\n        }\n    }\n}\n\nvoid DebugActions::attachProcess(int pid)\n{\n    QString stopAttachLabel = tr(\"Detach from process\");\n    // hide unwanted buttons\n    setAllActionsVisible(true);\n    actionStart->setVisible(false);\n    actionStartRemote->setVisible(false);\n    actionStartEmul->setVisible(false);\n    actionStop->setText(stopAttachLabel);\n    actionStop->setIcon(detachIcon);\n    // attach\n    Core()->attachDebug(pid);\n}\n\nvoid DebugActions::startDebug()\n{\n    // check if file is executable before starting debug\n    QString filename = Core()->getConfig(\"file.path\");\n\n    QFileInfo info(filename);\n    if (!Core()->currentlyDebugging && !info.isExecutable()) {\n        QMessageBox msgBox(main);\n        msgBox.setText(tr(\"File '%1' does not have executable permissions.\").arg(filename));\n        msgBox.exec();\n        return;\n    }\n\n    showDebugWarning();\n\n    NativeDebugDialog dialog(main);\n    dialog.setArgs(Core()->getConfig(\"dbg.args\"));\n    dialog.setProfilePath(Core()->getConfig(\"dbg.profile\"));\n    if (!dialog.exec()) {\n        return;\n    }\n\n    switch (dialog.getSelectedMethod()) {\n    case DebugConfigMethod::CommandLine: {\n        Core()->setConfig(\"dbg.args\", dialog.getArgs());\n        Core()->setConfig(\"dbg.profile\", \"\"); // profile overrides args, so remove it\n        break;\n    }\n    case DebugConfigMethod::RzRunProfile: {\n        Core()->setConfig(\"dbg.profile\", dialog.getProfilePath());\n        break;\n    }\n    case DebugConfigMethod::RzRunDirectives: {\n        Core()->setProfileDirectives(dialog.getDirectives());\n        break;\n    }\n    default:\n        break;\n    }\n\n    setAllActionsVisible(true);\n    actionAttach->setVisible(false);\n    actionStartRemote->setVisible(false);\n    actionStartEmul->setVisible(false);\n    actionStart->setText(restartDebugLabel);\n    actionStart->setIcon(restartIcon);\n    setButtonVisibleIfMainExists();\n\n    // Reverse debug actions aren't visible until we start tracing\n    for (QAction *a : reverseActions) {\n        a->setVisible(false);\n    }\n    actionTrace->setText(startTraceLabel);\n    actionTrace->setIcon(startTraceIcon);\n\n    Core()->startDebug();\n}\n\nvoid DebugActions::setAllActionsVisible(bool visible)\n{\n    for (QAction *action : allActions) {\n        action->setVisible(visible);\n    }\n}\n\n/**\n * @brief When theme changed, change icons which have a special version for the theme.\n */\nvoid DebugActions::chooseThemeIcons()\n{\n    // List of QActions which have alternative icons in different themes\n    const QList<QPair<void *, QString>> kSupportedIconsNames {\n        { actionStep, QStringLiteral(\"step_into.svg\") },\n        { actionStepOver, QStringLiteral(\"step_over.svg\") },\n        { actionStepOut, QStringLiteral(\"step_out.svg\") },\n        { actionContinueUntilMain, QStringLiteral(\"continue_until_main.svg\") },\n        { actionContinueUntilCall, QStringLiteral(\"continue_until_call.svg\") },\n        { actionContinueUntilSyscall, QStringLiteral(\"continue_until_syscall.svg\") },\n    };\n\n    // Set the correct icon for the QAction\n    qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) {\n        static_cast<QAction *>(obj)->setIcon(icon);\n    });\n}\n"
  },
  {
    "path": "src/widgets/DebugActions.h",
    "content": "#pragma once\n\n#include \"core/Cutter.h\"\n#include \"dialogs/RemoteDebugDialog.h\"\n\n#include <QAction>\n\nclass MainWindow;\nclass QToolBar;\nclass QToolButton;\n\nclass DebugActions : public QObject\n{\n    Q_OBJECT\n\npublic:\n    explicit DebugActions(QToolBar *toolBar, MainWindow *main);\n\n    void addToToolBar(QToolBar *toolBar);\n\n    QAction *actionStart;\n    QAction *actionStartRemote;\n    QAction *actionStartEmul;\n    QAction *actionAttach;\n    QAction *actionContinue;\n    QAction *actionContinueUntilMain;\n    QAction *actionContinueUntilCall;\n    QAction *actionContinueUntilSyscall;\n    QAction *actionContinueBack;\n    QAction *actionStep;\n    QAction *actionStepOver;\n    QAction *actionStepOut;\n    QAction *actionStepBack;\n    QAction *actionStop;\n    QAction *actionAllContinues;\n    QAction *actionTrace;\n\n    // Continue/suspend and start/restart interchange during runtime\n    QIcon continueIcon;\n    QIcon suspendIcon;\n    QIcon restartIcon;\n    QIcon startDebugIcon;\n    QIcon startTraceIcon;\n    QIcon stopTraceIcon;\n    QString continueLabel;\n    QString suspendLabel;\n    QString restartDebugLabel;\n    QString startDebugLabel;\n    QString startTraceLabel;\n    QString stopTraceLabel;\n\n    // Stop and Detach interchange during runtime\n    QIcon detachIcon;\n    QIcon stopIcon;\n\nprivate:\n    /**\n     * @brief buttons that will be disabled/enabled on (disable/enable)DebugToolbar\n     */\n    QList<QAction *> toggleActions;\n    QList<QAction *> toggleConnectionActions;\n    QList<QAction *> reverseActions;\n    QList<QAction *> allActions;\n    QToolButton *continueUntilButton;\n    RemoteDebugDialog *remoteDialog = nullptr;\n    MainWindow *main;\n    bool acceptedDebugWarning = false;\n\n    // TODO: Remove once debug is stable\n    void showDebugWarning();\n\nprivate slots:\n    void continueUntilMain();\n    void startDebug();\n    void attachProcessDialog();\n    void attachProcess(int pid);\n    void attachRemoteDialog();\n    void attachRemoteDebugger();\n    void onAttachedRemoteDebugger(bool successfully);\n    void setAllActionsVisible(bool visible);\n    void setButtonVisibleIfMainExists();\n    void chooseThemeIcons();\n};\n"
  },
  {
    "path": "src/widgets/DecompilerWidget.cpp",
    "content": "#include \"DecompilerWidget.h\"\n#include \"ui_DecompilerWidget.h\"\n#include \"menus/DecompilerContextMenu.h\"\n\n#include \"common/Configuration.h\"\n#include \"common/Helpers.h\"\n#include \"common/TempConfig.h\"\n#include \"common/SelectionHighlight.h\"\n#include \"common/Decompiler.h\"\n#include \"common/CutterSeekable.h\"\n#include \"core/MainWindow.h\"\n#include \"common/DecompilerHighlighter.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QTextEdit>\n#include <QPlainTextEdit>\n#include <QTextBlock>\n#include <QClipboard>\n#include <QObject>\n#include <QTextBlockUserData>\n#include <QScrollBar>\n#include <QAbstractSlider>\n\nDecompilerWidget::DecompilerWidget(MainWindow *main)\n    : MemoryDockWidget(MemoryWidgetType::Decompiler, main),\n      mCtxMenu(new DecompilerContextMenu(this, main)),\n      ui(new Ui::DecompilerWidget),\n      decompilerBusy(false),\n      seekFromCursor(false),\n      historyPos(0),\n      previousFunctionAddr(RVA_INVALID),\n      decompiledFunctionAddr(RVA_INVALID),\n      code(Decompiler::makeWarning(tr(\"Choose an offset and refresh to get decompiled code\")),\n           &rz_annotated_code_free)\n{\n    ui->setupUi(this);\n    setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());\n    updateWindowTitle();\n\n    setHighlighter(Config()->isDecompilerAnnotationHighlighterEnabled());\n    // Event filter to intercept double click and right click in the textbox\n    ui->textEdit->viewport()->installEventFilter(this);\n\n    setupFonts();\n    colorsUpdatedSlot();\n\n    connect(Config(), &Configuration::fontsUpdated, this, &DecompilerWidget::fontsUpdatedSlot);\n    connect(Config(), &Configuration::colorsUpdated, this, &DecompilerWidget::colorsUpdatedSlot);\n    connect(Core(), &CutterCore::registersChanged, this, &DecompilerWidget::highlightPC);\n    connect(mCtxMenu, &DecompilerContextMenu::copy, this, &DecompilerWidget::copy);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { doRefresh(); });\n\n    auto decompilers = Core()->getDecompilers();\n    QString selectedDecompilerId = Config()->getSelectedDecompiler();\n    if (selectedDecompilerId.isEmpty()) {\n        // If no decompiler was previously chosen. set rz-ghidra as default decompiler\n        selectedDecompilerId = \"ghidra\";\n    }\n    for (Decompiler *dec : decompilers) {\n        ui->decompilerComboBox->addItem(dec->getName(), dec->getId());\n        if (dec->getId() == selectedDecompilerId) {\n            ui->decompilerComboBox->setCurrentIndex(ui->decompilerComboBox->count() - 1);\n        }\n    }\n    decompilerSelectionEnabled = decompilers.size() > 1;\n    ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);\n    if (decompilers.isEmpty()) {\n        ui->textEdit->setPlainText(tr(\"No Decompiler available.\"));\n    }\n\n    connect(ui->decompilerComboBox,\n            static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,\n            &DecompilerWidget::decompilerSelected);\n    connectCursorPositionChanged(true);\n    connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DecompilerWidget::seekChanged);\n    ui->textEdit->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(ui->textEdit, &QWidget::customContextMenuRequested, this,\n            &DecompilerWidget::showDecompilerContextMenu);\n\n    connect(Core(), &CutterCore::breakpointsChanged, this, &DecompilerWidget::updateBreakpoints);\n    mCtxMenu->addSeparator();\n    mCtxMenu->addAction(&syncAction);\n    addActions(mCtxMenu->actions());\n\n    ui->progressLabel->setVisible(false);\n    doRefresh();\n\n    connect(Core(), &CutterCore::refreshAll, this, &DecompilerWidget::doRefresh);\n    connect(Core(), &CutterCore::functionRenamed, this, &DecompilerWidget::doRefresh);\n    connect(Core(), &CutterCore::varsChanged, this, &DecompilerWidget::doRefresh);\n    connect(Core(), &CutterCore::functionsChanged, this, &DecompilerWidget::doRefresh);\n    connect(Core(), &CutterCore::flagsChanged, this, &DecompilerWidget::doRefresh);\n    connect(Core(), &CutterCore::globalVarsChanged, this, &DecompilerWidget::doRefresh);\n    connect(Core(), &CutterCore::commentsChanged, this, &DecompilerWidget::refreshIfChanged);\n    connect(Core(), &CutterCore::instructionChanged, this, &DecompilerWidget::refreshIfChanged);\n    connect(Core(), &CutterCore::refreshCodeViews, this, &DecompilerWidget::doRefresh);\n\n    // Esc to seek backward\n    QAction *seekPrevAction = Shortcuts()->makeAction(\"General.seekPrev\", this);\n    seekPrevAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n    addAction(seekPrevAction);\n    connect(seekPrevAction, &QAction::triggered, seekable, &CutterSeekable::seekPrev);\n}\n\nDecompilerWidget::~DecompilerWidget() = default;\n\nQString DecompilerWidget::getWidgetType()\n{\n    return \"DecompilerWidget\";\n}\n\nDecompiler *DecompilerWidget::getCurrentDecompiler()\n{\n    return Core()->getDecompilerById(ui->decompilerComboBox->currentData().toString());\n}\n\nut64 DecompilerWidget::findReference(size_t pos)\n{\n    size_t closestPos = SIZE_MAX;\n    ut64 closestOffset = RVA_INVALID;\n    void *iter;\n    rz_vector_foreach(&code->annotations, iter)\n    {\n        RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter;\n\n        if (!(annotation->type == RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE)\n            || annotation->start > pos || annotation->end <= pos) {\n            continue;\n        }\n        if (closestPos != SIZE_MAX && closestPos >= annotation->start) {\n            continue;\n        }\n        closestPos = annotation->start;\n        closestOffset = annotation->reference.offset;\n    }\n    return closestOffset;\n}\n\nut64 DecompilerWidget::offsetForPosition(size_t pos)\n{\n    size_t closestPos = SIZE_MAX;\n    ut64 closestOffset = mCtxMenu->getFirstOffsetInLine();\n    void *iter;\n    rz_vector_foreach(&code->annotations, iter)\n    {\n        RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter;\n\n        if (!(annotation->type == RZ_CODE_ANNOTATION_TYPE_OFFSET) || annotation->start > pos\n            || annotation->end <= pos) {\n            continue;\n        }\n        if (closestPos != SIZE_MAX && closestPos >= annotation->start) {\n            continue;\n        }\n        closestPos = annotation->start;\n        closestOffset = annotation->offset.offset;\n    }\n    return closestOffset;\n}\n\nsize_t DecompilerWidget::positionForOffset(ut64 offset)\n{\n    size_t closestPos = SIZE_MAX;\n    ut64 closestOffset = UT64_MAX;\n    void *iter;\n    rz_vector_foreach(&code->annotations, iter)\n    {\n        RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter;\n        if (annotation->type != RZ_CODE_ANNOTATION_TYPE_OFFSET\n            || annotation->offset.offset > offset) {\n            continue;\n        }\n        if (closestOffset != UT64_MAX && closestOffset >= annotation->offset.offset) {\n            continue;\n        }\n        closestPos = annotation->start;\n        closestOffset = annotation->offset.offset;\n    }\n    return closestPos;\n}\n\nvoid DecompilerWidget::updateBreakpoints(RVA addr)\n{\n    if (!addressInRange(addr)) {\n        return;\n    }\n    setInfoForBreakpoints();\n    QTextCursor cursor = ui->textEdit->textCursor();\n    cursor.select(QTextCursor::Document);\n    cursor.setCharFormat(QTextCharFormat());\n    cursor.setBlockFormat(QTextBlockFormat());\n    ui->textEdit->setExtraSelections({});\n    highlightPC();\n    highlightBreakpoints();\n    updateSelection();\n}\n\nvoid DecompilerWidget::setInfoForBreakpoints()\n{\n    if (mCtxMenu->getIsTogglingBreakpoints()) {\n        return;\n    }\n    // Get the range of the line\n    QTextCursor cursorForLine = ui->textEdit->textCursor();\n    cursorForLine.movePosition(QTextCursor::StartOfLine);\n    size_t startPos = cursorForLine.position();\n    cursorForLine.movePosition(QTextCursor::EndOfLine);\n    size_t endPos = cursorForLine.position();\n    gatherBreakpointInfo(*code, startPos, endPos);\n}\n\nvoid DecompilerWidget::gatherBreakpointInfo(RzAnnotatedCode &codeDecompiled, size_t startPos,\n                                            size_t endPos)\n{\n    RVA firstOffset = RVA_MAX;\n    void *iter;\n    rz_vector_foreach(&codeDecompiled.annotations, iter)\n    {\n        RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter;\n        if (annotation->type != RZ_CODE_ANNOTATION_TYPE_OFFSET) {\n            continue;\n        }\n        if ((startPos <= annotation->start && annotation->start < endPos)\n            || (startPos < annotation->end && annotation->end < endPos)) {\n            firstOffset = (annotation->offset.offset < firstOffset) ? annotation->offset.offset\n                                                                    : firstOffset;\n        }\n    }\n    mCtxMenu->setFirstOffsetInLine(firstOffset);\n    QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);\n    QVector<RVA> offsetList;\n    for (RVA bpOffset : functionBreakpoints) {\n        size_t pos = positionForOffset(bpOffset);\n        if (startPos <= pos && pos <= endPos) {\n            offsetList.push_back(bpOffset);\n        }\n    }\n    std::sort(offsetList.begin(), offsetList.end());\n    mCtxMenu->setAvailableBreakpoints(offsetList);\n}\n\nvoid DecompilerWidget::refreshIfChanged(RVA addr)\n{\n    if (addressInRange(addr)) {\n        doRefresh();\n    }\n}\n\nvoid DecompilerWidget::doRefresh()\n{\n    RVA addr = seekable->getOffset();\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n    if (ui->decompilerComboBox->currentIndex() < 0) {\n        return;\n    }\n    Decompiler *dec = getCurrentDecompiler();\n    if (!dec) {\n        return;\n    }\n    // Disabling decompiler selection combo box and making progress label visible ahead of\n    // decompilation.\n    ui->progressLabel->setVisible(true);\n    ui->decompilerComboBox->setEnabled(false);\n    if (dec->isRunning()) {\n        if (!decompilerBusy) {\n            connect(dec, &Decompiler::finished, this, &DecompilerWidget::doRefresh);\n        }\n        return;\n    }\n    disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::doRefresh);\n    // Clear all selections since we just refreshed\n    ui->textEdit->setExtraSelections({});\n    previousFunctionAddr = decompiledFunctionAddr;\n    decompiledFunctionAddr = Core()->getFunctionStart(addr);\n    updateWindowTitle();\n    if (decompiledFunctionAddr == RVA_INVALID) {\n        // No function was found, so making the progress label invisible and enabling\n        // the decompiler selection combo box as we are not waiting for any decompilation to finish.\n        ui->progressLabel->setVisible(false);\n        ui->decompilerComboBox->setEnabled(true);\n        setCode(Decompiler::makeWarning(\n                tr(\"No function found at this offset. \"\n                   \"Seek to a function or define one in order to decompile it.\")));\n        return;\n    }\n    mCtxMenu->setDecompiledFunctionAddress(decompiledFunctionAddr);\n    connect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);\n    decompilerBusy = true;\n    dec->decompileAt(addr);\n}\n\nvoid DecompilerWidget::refreshDecompiler()\n{\n    doRefresh();\n    setInfoForBreakpoints();\n}\n\nQTextCursor DecompilerWidget::getCursorForAddress(RVA addr)\n{\n    size_t pos = positionForOffset(addr);\n    if (pos == SIZE_MAX || pos == 0) {\n        return QTextCursor();\n    }\n    QTextCursor cursor = ui->textEdit->textCursor();\n    cursor.setPosition(pos);\n    return cursor;\n}\n\nvoid DecompilerWidget::decompilationFinished(RzAnnotatedCode *codeDecompiled)\n{\n    ui->progressLabel->setVisible(false);\n    ui->decompilerComboBox->setEnabled(decompilerSelectionEnabled);\n\n    mCtxMenu->setAnnotationHere(nullptr);\n    setCode(codeDecompiled);\n\n    Decompiler *dec = getCurrentDecompiler();\n    QObject::disconnect(dec, &Decompiler::finished, this, &DecompilerWidget::decompilationFinished);\n    decompilerBusy = false;\n\n    if (ui->textEdit->toPlainText().isEmpty()) {\n        setCode(Decompiler::makeWarning(tr(\"Cannot decompile at this address (Not a function?)\")));\n        lowestOffsetInCode = RVA_MAX;\n        highestOffsetInCode = 0;\n        return;\n    } else {\n        updateCursorPosition();\n        highlightPC();\n        highlightBreakpoints();\n        lowestOffsetInCode = RVA_MAX;\n        highestOffsetInCode = 0;\n        void *iter;\n        rz_vector_foreach(&code->annotations, iter)\n        {\n            RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter;\n            if (annotation->type == RZ_CODE_ANNOTATION_TYPE_OFFSET) {\n                if (lowestOffsetInCode > annotation->offset.offset) {\n                    lowestOffsetInCode = annotation->offset.offset;\n                }\n                if (highestOffsetInCode < annotation->offset.offset) {\n                    highestOffsetInCode = annotation->offset.offset;\n                }\n            }\n        }\n    }\n\n    if (!scrollHistory.empty()) {\n        ui->textEdit->horizontalScrollBar()->setSliderPosition(scrollHistory[historyPos].first);\n        ui->textEdit->verticalScrollBar()->setSliderPosition(scrollHistory[historyPos].second);\n    }\n}\n\nvoid DecompilerWidget::setAnnotationsAtCursor(size_t pos)\n{\n    RzCodeAnnotation *annotationAtPos = nullptr;\n    void *iter;\n    rz_vector_foreach(&this->code->annotations, iter)\n    {\n        RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter;\n        if (annotation->type == RZ_CODE_ANNOTATION_TYPE_OFFSET\n            || annotation->type == RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT\n            || annotation->start > pos || annotation->end <= pos) {\n            continue;\n        }\n        annotationAtPos = annotation;\n        break;\n    }\n    mCtxMenu->setAnnotationHere(annotationAtPos);\n}\n\nvoid DecompilerWidget::decompilerSelected()\n{\n    Config()->setSelectedDecompiler(ui->decompilerComboBox->currentData().toString());\n    doRefresh();\n}\n\nvoid DecompilerWidget::connectCursorPositionChanged(bool connectPositionChange)\n{\n    if (!connectPositionChange) {\n        disconnect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this,\n                   &DecompilerWidget::cursorPositionChanged);\n    } else {\n        connect(ui->textEdit, &QPlainTextEdit::cursorPositionChanged, this,\n                &DecompilerWidget::cursorPositionChanged);\n    }\n}\n\nvoid DecompilerWidget::cursorPositionChanged()\n{\n    // Do not perform seeks along with the cursor while selecting multiple lines\n    if (!ui->textEdit->textCursor().selectedText().isEmpty()) {\n        return;\n    }\n\n    size_t pos = ui->textEdit->textCursor().position();\n    setAnnotationsAtCursor(pos);\n    setInfoForBreakpoints();\n\n    RVA offset = offsetForPosition(pos);\n    if (offset != RVA_INVALID && offset != seekable->getOffset()) {\n        seekFromCursor = true;\n        seekable->seek(offset);\n        mCtxMenu->setOffset(offset);\n        seekFromCursor = false;\n    }\n    updateSelection();\n}\n\nvoid DecompilerWidget::seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type)\n{\n    if (seekFromCursor) {\n        return;\n    }\n\n    if (!scrollHistory.empty()) { // History is empty upon init.\n        scrollHistory[historyPos] = { ui->textEdit->horizontalScrollBar()->sliderPosition(),\n                                      ui->textEdit->verticalScrollBar()->sliderPosition() };\n    }\n    if (type == CutterCore::SeekHistoryType::New) {\n        // Erase previous history past this point.\n        if (scrollHistory.size() > historyPos + 1) {\n            scrollHistory.erase(scrollHistory.begin() + historyPos + 1, scrollHistory.end());\n        }\n        scrollHistory.push_back({ 0, 0 });\n        historyPos = scrollHistory.size() - 1;\n    } else if (type == CutterCore::SeekHistoryType::Undo) {\n        --historyPos;\n    } else if (type == CutterCore::SeekHistoryType::Redo) {\n        ++historyPos;\n    }\n    RVA fcnAddr = Core()->getFunctionStart(seekable->getOffset());\n    if (fcnAddr == RVA_INVALID || fcnAddr != decompiledFunctionAddr) {\n        doRefresh();\n        return;\n    }\n    updateCursorPosition();\n}\n\nvoid DecompilerWidget::updateCursorPosition()\n{\n    RVA offset = seekable->getOffset();\n    size_t pos = positionForOffset(offset);\n    if (pos == SIZE_MAX) {\n        return;\n    }\n    mCtxMenu->setOffset(offset);\n    connectCursorPositionChanged(false);\n    QTextCursor cursor = ui->textEdit->textCursor();\n    cursor.setPosition(pos);\n    ui->textEdit->setTextCursor(cursor);\n    updateSelection();\n    connectCursorPositionChanged(true);\n}\n\nvoid DecompilerWidget::setupFonts()\n{\n    ui->textEdit->setFont(Config()->getFont());\n}\n\nvoid DecompilerWidget::updateSelection()\n{\n    QList<QTextEdit::ExtraSelection> extraSelections;\n\n    // Highlight the current line\n    QTextCursor cursor = ui->textEdit->textCursor();\n    extraSelections.append(createLineHighlightSelection(cursor));\n\n    // Highlight all the words in the document same as the current one\n    cursor.select(QTextCursor::WordUnderCursor);\n    QString searchString = cursor.selectedText();\n    mCtxMenu->setCurHighlightedWord(searchString);\n    extraSelections.append(createSameWordsSelections(ui->textEdit, searchString));\n\n    ui->textEdit->setExtraSelections(extraSelections);\n    // Highlight PC after updating the selected line\n    highlightPC();\n}\n\nQString DecompilerWidget::getWindowTitle() const\n{\n    RzAnalysisFunction *fcn = Core()->functionAt(decompiledFunctionAddr);\n    QString windowTitle;\n    if (fcn != NULL) {\n        windowTitle = tr(\"Decompiler (%1)\").arg(fcn->name);\n    } else {\n        windowTitle = tr(\"Decompiler (Empty)\");\n    }\n    return windowTitle;\n}\n\nvoid DecompilerWidget::fontsUpdatedSlot()\n{\n    setupFonts();\n}\n\nvoid DecompilerWidget::colorsUpdatedSlot()\n{\n    bool useAnotationHiglighter = Config()->isDecompilerAnnotationHighlighterEnabled();\n    if (useAnotationHiglighter != usingAnnotationBasedHighlighting) {\n        setHighlighter(useAnotationHiglighter);\n    }\n}\n\nvoid DecompilerWidget::showDecompilerContextMenu(const QPoint &pt)\n{\n    mCtxMenu->exec(ui->textEdit->mapToGlobal(pt));\n}\n\nvoid DecompilerWidget::seekToReference()\n{\n    size_t pos = ui->textEdit->textCursor().position();\n    RVA offset = findReference(pos);\n    if (offset != RVA_INVALID) {\n        seekable->seek(offset);\n    }\n    seekable->seekToReference(offsetForPosition(pos));\n}\n\nbool DecompilerWidget::eventFilter(QObject *obj, QEvent *event)\n{\n    if (event->type() == QEvent::MouseButtonDblClick\n        && (obj == ui->textEdit || obj == ui->textEdit->viewport())) {\n        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);\n        ui->textEdit->setTextCursor(ui->textEdit->cursorForPosition(mouseEvent->pos()));\n        seekToReference();\n        return true;\n    }\n    if (event->type() == QEvent::MouseButtonPress\n        && (obj == ui->textEdit || obj == ui->textEdit->viewport())) {\n        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);\n        if (mouseEvent->button() == Qt::RightButton && !ui->textEdit->textCursor().hasSelection()) {\n            ui->textEdit->setTextCursor(ui->textEdit->cursorForPosition(mouseEvent->pos()));\n            return true;\n        }\n    }\n    return MemoryDockWidget::eventFilter(obj, event);\n}\n\nvoid DecompilerWidget::highlightPC()\n{\n    RVA PCAddress = Core()->getProgramCounterValue();\n    if (PCAddress == RVA_INVALID\n        || (Core()->getFunctionStart(PCAddress) != decompiledFunctionAddr)) {\n        return;\n    }\n\n    QTextCursor cursor = getCursorForAddress(PCAddress);\n    if (!cursor.isNull()) {\n        colorLine(createLineHighlightPC(cursor));\n    }\n}\n\nvoid DecompilerWidget::highlightBreakpoints()\n{\n\n    QList<RVA> functionBreakpoints = Core()->getBreakpointsInFunction(decompiledFunctionAddr);\n    QTextCursor cursor;\n    for (RVA &bp : functionBreakpoints) {\n        if (bp == RVA_INVALID) {\n            continue;\n        }\n        cursor = getCursorForAddress(bp);\n        if (!cursor.isNull()) {\n            // Use a Block formatting since these lines are not updated frequently as selections and\n            // PC\n            QTextBlockFormat f;\n            f.setBackground(ConfigColor(\"gui.breakpoint_background\"));\n            cursor.setBlockFormat(f);\n        }\n    }\n}\n\nbool DecompilerWidget::colorLine(QTextEdit::ExtraSelection extraSelection)\n{\n    QList<QTextEdit::ExtraSelection> extraSelections = ui->textEdit->extraSelections();\n    extraSelections.append(extraSelection);\n    ui->textEdit->setExtraSelections(extraSelections);\n    return true;\n}\n\nvoid DecompilerWidget::copy()\n{\n    if (ui->textEdit->textCursor().hasSelection()) {\n        ui->textEdit->copy();\n    } else {\n        QTextCursor cursor = ui->textEdit->textCursor();\n        QClipboard *clipboard = QApplication::clipboard();\n        cursor.select(QTextCursor::WordUnderCursor);\n        if (!cursor.selectedText().isEmpty()) {\n            clipboard->setText(cursor.selectedText());\n        } else {\n            cursor.select(QTextCursor::LineUnderCursor);\n            clipboard->setText(cursor.selectedText());\n        }\n    }\n}\n\nbool DecompilerWidget::addressInRange(RVA addr)\n{\n    if (lowestOffsetInCode <= addr && addr <= highestOffsetInCode) {\n        return true;\n    }\n    return false;\n}\n\n/**\n * Convert annotation ranges from byte offsets in utf8 used by RzAnnotated code to QString QChars\n * used by QString and Qt text editor.\n * @param code - RzAnnotated code with annotations that need to be modified\n * @return Decompiled code\n */\nstatic QString remapAnnotationOffsetsToQString(RzAnnotatedCode &code)\n{\n    QByteArray bytes(code.code);\n    std::vector<size_t> offsets;\n    offsets.reserve(bytes.size());\n    char c;\n    for (size_t off = 0; c = code.code[off], c; off++) {\n        if ((c & 0xc0) == 0x80) {\n            continue;\n        }\n        offsets.push_back(off);\n    }\n    auto mapPos = [&](size_t pos) {\n        auto it = std::upper_bound(offsets.begin(), offsets.end(), pos);\n        if (it != offsets.begin()) {\n            --it;\n        }\n        return it - offsets.begin();\n    };\n\n    void *iter;\n    rz_vector_foreach(&code.annotations, iter)\n    {\n        RzCodeAnnotation *annotation = (RzCodeAnnotation *)iter;\n        annotation->start = mapPos(annotation->start);\n        annotation->end = mapPos(annotation->end);\n    }\n    return QString::fromUtf8(code.code);\n}\n\nvoid DecompilerWidget::setCode(RzAnnotatedCode *code)\n{\n    connectCursorPositionChanged(false);\n    if (auto highlighter = qobject_cast<DecompilerHighlighter *>(syntaxHighlighter.get())) {\n        highlighter->setAnnotations(code);\n    }\n    this->code.reset(code);\n    QString text = remapAnnotationOffsetsToQString(*this->code);\n    this->ui->textEdit->setPlainText(text);\n    connectCursorPositionChanged(true);\n    syntaxHighlighter->rehighlight();\n}\n\nvoid DecompilerWidget::setHighlighter(bool annotationBasedHighlighter)\n{\n    usingAnnotationBasedHighlighting = annotationBasedHighlighter;\n    if (usingAnnotationBasedHighlighting) {\n        syntaxHighlighter.reset(new DecompilerHighlighter());\n        static_cast<DecompilerHighlighter *>(syntaxHighlighter.get())->setAnnotations(code.get());\n    } else {\n        syntaxHighlighter.reset(Config()->createSyntaxHighlighter(nullptr));\n    }\n    syntaxHighlighter->setDocument(ui->textEdit->document());\n}\n"
  },
  {
    "path": "src/widgets/DecompilerWidget.h",
    "content": "#ifndef DECOMPILERWIDGET_H\n#define DECOMPILERWIDGET_H\n\n#include <QTextEdit>\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"MemoryDockWidget.h\"\n#include \"Decompiler.h\"\n\nnamespace Ui {\nclass DecompilerWidget;\n}\n\nclass QTextEdit;\nclass QSyntaxHighlighter;\nclass QTextCursor;\nclass DecompilerContextMenu;\nstruct DecompiledCodeTextLine;\n\nclass DecompilerWidget : public MemoryDockWidget\n{\n    Q_OBJECT\nprotected:\n    DecompilerContextMenu *mCtxMenu;\n\npublic:\n    explicit DecompilerWidget(MainWindow *main);\n    ~DecompilerWidget();\n    static QString getWidgetType();\npublic slots:\n    void showDecompilerContextMenu(const QPoint &pt);\n\n    void highlightPC();\nprivate slots:\n    /**\n     * @brief Copy to clipboard what's needed depending on the state of text widget.\n     *\n     * @note If something is selected in the text widget, copy selection.\n     *       If something is highlighted, copy highlighted word.\n     *       Otherwise, copy the line under cursor.\n     */\n    void copy();\n    void fontsUpdatedSlot();\n    void colorsUpdatedSlot();\n    void refreshDecompiler();\n    void decompilerSelected();\n    void cursorPositionChanged();\n    /**\n     * @brief When the synced seek is changed, this refreshes the decompiler widget if needed.\n     *\n     * Decompiler widget is not refreshed in the following two cases\n     *     - Seek changed to an offset contained in the decompiled function.\n     *     - Auto-refresh is disabled.\n     */\n    void seekChanged(RVA /* addr */, CutterCore::SeekHistoryType type);\n    void decompilationFinished(RzAnnotatedCode *code);\n\nprivate:\n    std::unique_ptr<Ui::DecompilerWidget> ui;\n\n    RefreshDeferrer *refreshDeferrer;\n\n    bool usingAnnotationBasedHighlighting = false;\n    std::unique_ptr<QSyntaxHighlighter> syntaxHighlighter;\n    bool decompilerSelectionEnabled;\n\n    /**\n     * True if the selected decompiler is currently running a decompilation for this widget. Once\n     * the decompilation is over, this should be set to false.\n     */\n    bool decompilerBusy;\n\n    bool seekFromCursor;\n    int historyPos;\n    QVector<QPair<int, int>> scrollHistory;\n    RVA previousFunctionAddr;\n    RVA decompiledFunctionAddr;\n    std::unique_ptr<RzAnnotatedCode, void (*)(RzAnnotatedCode *)> code;\n\n    /**\n     * Specifies the lowest offset of instructions among all the instructions in the decompiled\n     * function.\n     */\n    RVA lowestOffsetInCode;\n    /**\n     * Specifies the highest offset of instructions among all the instructions in the decompiled\n     * function.\n     */\n    RVA highestOffsetInCode;\n\n    /**\n     * @brief Gets the current decompiler selected by the user.\n     *\n     * @return A pointer to the currently selected decompiler\n     */\n    Decompiler *getCurrentDecompiler();\n\n    /**\n     * @brief Calls the function doRefresh() if the address specified is a part of the decompiled\n     * function.\n     *\n     * @param addr Address at which a change occurred.\n     */\n    void refreshIfChanged(RVA addr);\n    /**\n     * @brief Refreshes the decompiler.\n     *\n     * - This does the following if the specified offset is valid\n     *     - Decompile function that contains the specified offset.\n     *     - Clears all selections stored for highlighting purposes.\n     *     - Reset previousFunctionAddr with the current function's address\n     *       and decompiledFunctionAddr with the address of the function that\n     *       was decompiled.\n     * - If the offset is invalid, error message is shown in the text widget.\n     *\n     * @param addr Specified offset/offset in sync.\n     */\n    void doRefresh();\n    /**\n     * @brief Update fonts\n     */\n    void setupFonts();\n    /**\n     * @brief Update highlights in the text widget.\n     *\n     * These include respective highlights for:\n     *     - Line under cursor\n     *     - Word under cursor\n     *     - Program Counter(PC) while debugging\n     */\n    void updateSelection();\n    /**\n     * @brief Connect/Disconnect SIGNAL-SLOT connection that deals with changes in cursor position.\n     *\n     * If the argument is true, then connect the SIGNAL-SLOT connection\n     * that changes the view as cursor position gets changed in the text widget.\n     * Otherwise, disconnect the corresponding signal with slot.\n     *\n     * @param connectPositionChange\n     */\n    void connectCursorPositionChanged(bool connectPositionChange);\n    /**\n     * @brief Find the current global offset in sync and update cursor\n     * to the position specified by this offset (found using positionForOffset() )\n     */\n    void updateCursorPosition();\n\n    QString getWindowTitle() const override;\n\n    /**\n     * @brief Event filter that intercept the following events:\n     *     1. Double click\n     *     2. Right click\n     *\n     * @param obj\n     * @param event\n     * @return\n     */\n    bool eventFilter(QObject *obj, QEvent *event) override;\n\n    /**\n     * @brief a wrapper around CutterSeekable::seekToReference to seek to an object which is\n     * referenced from the address under cursor\n     */\n    void seekToReference();\n\n    /**\n     * @brief Retrieve the Cursor for a location as close as possible to the given address\n     * @param addr - an address in the decompiled function\n     * @return a Cursor object for the given address\n     */\n    QTextCursor getCursorForAddress(RVA addr);\n\n    /**\n     * @brief Append a highlighted line to the TextEdit\n     * @param extraSelection - an ExtraSelection object colored with the appropriate color\n     * @return True on success, otherwise False\n     */\n    bool colorLine(QTextEdit::ExtraSelection extraSelection);\n\n    /**\n     * @brief This function is responsible for highlighting all the breakpoints in the decompiler\n     * view. It will also run when a breakpoint is added, removed or modified.\n     */\n    void highlightBreakpoints();\n    /**\n     * @brief Finds the earliest offset and breakpoints within the specified range [startPos,\n     * endPos] in the specified RzAnnotatedCode.\n     *\n     * This function is supposed to be used for finding the earliest offset and breakpoints within\n     * the specified range [startPos, endPos]. This will set the value of the variables 'RVA\n     * firstOffsetInLine' and 'QVector<RVA> availableBreakpoints' in the context menu.\n     *\n     * @param codeDecompiled - A reference to the RzAnnotatedCode for the function that is\n     * decompiled.\n     * @param startPos - Position of the start of the range(inclusive).\n     * @param endPos - Position of the end of the range(inclusive).\n     */\n    void gatherBreakpointInfo(RzAnnotatedCode &codeDecompiled, size_t startPos, size_t endPos);\n    /**\n     * @brief Finds the global variable reference that's closes to the specified position in the\n     * decompiled code. Same as offsetForPosition but for global references only\n     *\n     * @note If no global reference annotations are found at the given position, an RVA_INVALID is\n     * returned\n     *\n     * @param pos - Position in the decompiled code\n     * @return Address of the referenced global for the specified position, or RVA_INVALID if none\n     * is found\n     */\n    ut64 findReference(size_t pos);\n    /**\n     * @brief Finds the offset that's closest to the specified position in the decompiled code.\n     *\n     * @note If no annotations that covers the specified position is found, the first offset in the\n     * line containing specified position will be returned\n     *\n     * @param pos - Position of the decompiled code.\n     * @return Offset for the specified position/first offset in line.\n     */\n    ut64 offsetForPosition(size_t pos);\n    /**\n     * @brief Find the start position of the annotation with the offset that's closest to\n     * the specified offset\n     *\n     * @param offset\n     * @return Position found or SIZE_MAX\n     */\n    size_t positionForOffset(ut64 offset);\n    /**\n     * @brief Updates the view when breakpoints are changed\n     */\n    void updateBreakpoints(RVA addr);\n    /**\n     * @brief Set information about the breakpoints on the line in the context menu\n     */\n    void setInfoForBreakpoints();\n    /**\n     * @brief Find the context-related annotation covering the specified position.\n     * If found, set the variable annotationHere in the decompiler context menu.\n     *\n     * @param pos Position of cursor in the decompiled code.\n     */\n    void setAnnotationsAtCursor(size_t pos);\n    /**\n     * @brief Checks if the specified address is a part of the decompiled function.\n     *\n     * @param addr An offset in the binary.\n     * @return True if the specified is a part of the decompiled function, False otherwise.\n     */\n    bool addressInRange(RVA addr);\n\n    void setCode(RzAnnotatedCode *code);\n\n    void setHighlighter(bool annotationBasedHighlighter);\n};\n\n#endif // DECOMPILERWIDGET_H\n"
  },
  {
    "path": "src/widgets/DecompilerWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DecompilerWidget</class>\n <widget class=\"QDockWidget\" name=\"DecompilerWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>555</width>\n    <height>393</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Decompiler</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QPlainTextEdit\" name=\"textEdit\">\n      <property name=\"lineWrapMode\">\n       <enum>QPlainTextEdit::NoWrap</enum>\n      </property>\n      <property name=\"readOnly\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n      <item>\n       <layout class=\"QHBoxLayout\" name=\"progressLayout\">\n        <property name=\"leftMargin\">\n         <number>8</number>\n        </property>\n        <item>\n         <widget class=\"QLabel\" name=\"progressLabel\">\n          <property name=\"text\">\n           <string>Decompiling...</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <spacer name=\"horizontalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Horizontal</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>40</width>\n          <height>20</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"decompilerLabel\">\n        <property name=\"text\">\n         <string>Decompiler:</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QComboBox\" name=\"decompilerComboBox\"/>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/DisassemblerGraphView.cpp",
    "content": "\n#include \"DisassemblerGraphView.h\"\n#include \"common/CutterSeekable.h\"\n#include \"core/Cutter.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Colors.h\"\n#include \"common/Configuration.h\"\n#include \"common/DisassemblyPreview.h\"\n#include \"common/TempConfig.h\"\n#include \"common/SyntaxHighlighter.h\"\n#include \"common/BasicBlockHighlighter.h\"\n#include \"common/BasicInstructionHighlighter.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include \"common/DisassemblyHelper.h\"\n\n#include <QColorDialog>\n#include <QPainter>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QMouseEvent>\n#include <QPropertyAnimation>\n#include <QShortcut>\n#include <QToolTip>\n#include <QTextDocument>\n#include <QTextEdit>\n#include <QVBoxLayout>\n#include <QRegularExpression>\n#include <QClipboard>\n#include <QApplication>\n#include <QAction>\n\n#include <cmath>\n\nnamespace DH = DisassemblyHelper;\n\nDisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable,\n                                             MainWindow *mainWindow,\n                                             QList<QAction *> additionalMenuActions)\n    : CutterGraphView(parent),\n      blockMenu(new DisassemblyContextMenu(this, mainWindow)),\n      contextMenu(new QMenu(this)),\n      seekable(seekable),\n      actionUnhighlight(this),\n      actionUnhighlightInstruction(this)\n{\n    highlight_token = nullptr;\n    auto *layout = new QVBoxLayout(this);\n    setTooltipStylesheet();\n\n    // Signals that require a refresh all\n    connect(Config(), &Configuration::colorsUpdated, this,\n            &DisassemblerGraphView::setTooltipStylesheet);\n\n    connect(Core(), &CutterCore::refreshAll, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::commentsChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::functionRenamed, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::flagsChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::globalVarsChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::varsChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::instructionChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::functionsChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::asmOptionsChanged, this, &DisassemblerGraphView::refreshView);\n    connect(Core(), &CutterCore::refreshCodeViews, this, &DisassemblerGraphView::refreshView);\n\n    connectSeekChanged(false);\n\n    // ESC for previous\n    QShortcut *shortcut_escape = Shortcuts()->makeQShortcut(\"General.seekPrev\", this);\n    shortcut_escape->setContext(Qt::WidgetShortcut);\n    connect(shortcut_escape, &QShortcut::activated, seekable, &CutterSeekable::seekPrev);\n\n    // Branch shortcuts\n    QShortcut *shortcut_take_true = Shortcuts()->makeQShortcut(\"Graph.takeTrue\", this);\n    shortcut_take_true->setContext(Qt::WidgetShortcut);\n    connect(shortcut_take_true, &QShortcut::activated, this, &DisassemblerGraphView::takeTrue);\n    QShortcut *shortcut_take_false = Shortcuts()->makeQShortcut(\"Graph.takeFalse\", this);\n    shortcut_take_false->setContext(Qt::WidgetShortcut);\n    connect(shortcut_take_false, &QShortcut::activated, this, &DisassemblerGraphView::takeFalse);\n\n    // Navigation shortcuts\n    QShortcut *shortcut_next_instr = Shortcuts()->makeQShortcut(\"Graph.nextInstr\", this);\n    shortcut_next_instr->setContext(Qt::WidgetShortcut);\n    connect(shortcut_next_instr, &QShortcut::activated, this, &DisassemblerGraphView::nextInstr);\n    QShortcut *shortcut_prev_instr = Shortcuts()->makeQShortcut(\"Graph.prevInstr\", this);\n    shortcut_prev_instr->setContext(Qt::WidgetShortcut);\n    connect(shortcut_prev_instr, &QShortcut::activated, this, &DisassemblerGraphView::prevInstr);\n    shortcuts.append(shortcut_escape);\n    shortcuts.append(shortcut_next_instr);\n    shortcuts.append(shortcut_prev_instr);\n\n    // Context menu that applies to everything\n    contextMenu->addAction(&actionExportGraph);\n    contextMenu->addMenu(layoutMenu);\n    contextMenu->addSeparator();\n    contextMenu->addActions(additionalMenuActions);\n\n    QAction *highlightBB = new QAction(this);\n    actionUnhighlight.setVisible(false);\n\n    highlightBB->setText(tr(\"Highlight block\"));\n    connect(highlightBB, &QAction::triggered, this, [this]() {\n        auto bbh = Core()->getBBHighlighter();\n        RVA currBlockEntry = blockForAddress(this->seekable->getOffset())->entry;\n\n        QColor background = disassemblyBackgroundColor;\n        if (auto block = bbh->getBasicBlock(currBlockEntry)) {\n            background = block->color;\n        }\n\n        QColor c = QColorDialog::getColor(background, this, QString(),\n                                          QColorDialog::DontUseNativeDialog);\n        if (c.isValid()) {\n            bbh->highlight(currBlockEntry, c);\n        }\n        emit Config()->colorsUpdated();\n    });\n\n    actionUnhighlight.setText(tr(\"Unhighlight block\"));\n    connect(&actionUnhighlight, &QAction::triggered, this, [this]() {\n        auto bbh = Core()->getBBHighlighter();\n        bbh->clear(blockForAddress(this->seekable->getOffset())->entry);\n        emit Config()->colorsUpdated();\n    });\n\n    QAction *highlightBI = new QAction(this);\n    actionUnhighlightInstruction.setVisible(false);\n\n    highlightBI->setText(tr(\"Highlight instruction\"));\n    connect(highlightBI, &QAction::triggered, this,\n            &DisassemblerGraphView::onActionHighlightBITriggered);\n\n    actionUnhighlightInstruction.setText(tr(\"Unhighlight instruction\"));\n    connect(&actionUnhighlightInstruction, &QAction::triggered, this,\n            &DisassemblerGraphView::onActionUnhighlightBITriggered);\n\n    blockMenu->addAction(highlightBB);\n    blockMenu->addAction(&actionUnhighlight);\n    blockMenu->addAction(highlightBI);\n    blockMenu->addAction(&actionUnhighlightInstruction);\n\n    // Include all actions from generic context menu in block specific menu\n    blockMenu->addSeparator();\n    blockMenu->addActions(contextMenu->actions());\n\n    connect(blockMenu, &DisassemblyContextMenu::copy, this, &DisassemblerGraphView::copySelection);\n\n    // Add header as widget to layout so it stretches to the layout width\n    layout->setContentsMargins(0, 0, 0, 0);\n    layout->setAlignment(Qt::AlignTop);\n\n    this->scale_thickness_multiplier = true;\n    installEventFilter(this);\n}\n\nvoid DisassemblerGraphView::connectSeekChanged(bool disconn)\n{\n    if (disconn) {\n        disconnect(seekable, &CutterSeekable::seekableSeekChanged, this,\n                   &DisassemblerGraphView::onSeekChanged);\n    } else {\n        connect(seekable, &CutterSeekable::seekableSeekChanged, this,\n                &DisassemblerGraphView::onSeekChanged);\n    }\n}\n\nDisassemblerGraphView::~DisassemblerGraphView()\n{\n    qDeleteAll(shortcuts);\n    shortcuts.clear();\n}\n\nvoid DisassemblerGraphView::refreshView()\n{\n    CutterGraphView::refreshView();\n    loadCurrentGraph();\n    breakpoints = Core()->getBreakpointsAddresses();\n    emit viewRefreshed();\n}\n\nvoid DisassemblerGraphView::loadCurrentGraph()\n{\n    TempConfig tempConfig;\n    tempConfig.set(\"scr.color\", COLOR_MODE_16M)\n            .set(\"asm.bb.line\", false)\n            .set(\"asm.lines\", false)\n            .set(\"asm.lines.fcn\", false);\n\n    disassembly_blocks.clear();\n    blocks.clear();\n\n    if (highlight_token) {\n        delete highlight_token;\n        highlight_token = nullptr;\n    }\n\n    RzAnalysisFunction *fcn = Core()->functionIn(seekable->getOffset());\n\n    windowTitle = tr(\"Graph\");\n    if (fcn && RZ_STR_ISNOTEMPTY(fcn->name)) {\n        currentFcnAddr = fcn->addr;\n        auto fcnName = fromOwned(rz_str_escape_utf8_for_json(fcn->name, -1));\n        windowTitle += QString(\"(%0)\").arg(fcnName.get());\n    } else {\n        windowTitle += tr(\"(Empty)\");\n    }\n    emit nameChanged(windowTitle);\n\n    emptyGraph = !fcn;\n    if (emptyGraph) {\n        // If there's no function to print, just add a message\n        if (!emptyText) {\n            emptyText = new QLabel(this);\n            emptyText->setText(tr(\"No function detected. Cannot display graph.\"));\n            emptyText->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);\n            layout()->addWidget(emptyText);\n            layout()->setAlignment(emptyText, Qt::AlignHCenter);\n        }\n        emptyText->setVisible(true);\n    } else if (emptyText) {\n        emptyText->setVisible(false);\n    }\n    // Refresh global \"empty graph\" variable so other widget know there is nothing to show here\n    Core()->setGraphEmpty(emptyGraph);\n    setEntry(fcn ? fcn->addr : RVA_INVALID);\n\n    if (!fcn) {\n        return;\n    }\n\n    for (const auto &bbi : CutterPVector<RzAnalysisBlock>(fcn->bbs)) {\n        RVA bbiFail = bbi->fail;\n        RVA bbiJump = bbi->jump;\n\n        DisassemblyBlock db;\n        GraphBlock gb;\n        gb.entry = bbi->addr;\n        db.entry = bbi->addr;\n        if (Config()->getGraphBlockEntryOffset()) {\n            // QColor(0,0,0,0) is transparent\n            db.header_text = Text(\"[\" + RzAddressString(db.entry) + \"]\", ConfigColor(\"offset\"),\n                                  QColor(0, 0, 0, 0));\n        }\n        db.true_path = RVA_INVALID;\n        db.false_path = RVA_INVALID;\n        if (bbiFail) {\n            db.false_path = bbiFail;\n            gb.edges.emplace_back(bbiFail);\n        }\n        if (bbiJump) {\n            if (bbiFail) {\n                db.true_path = bbiJump;\n            }\n            gb.edges.emplace_back(bbiJump);\n        }\n\n        RzAnalysisSwitchOp *switchOp = bbi->switch_op;\n        if (switchOp) {\n            for (const auto &caseOp : CutterRzList<RzAnalysisCaseOp>(switchOp->cases)) {\n                if (caseOp->jump == RVA_INVALID) {\n                    continue;\n                }\n                gb.edges.emplace_back(caseOp->jump);\n            }\n        }\n\n        RzCoreLocked core(Core());\n        std::unique_ptr<ut8[]> buf { new ut8[bbi->size] };\n        if (!buf) {\n            break;\n        }\n        rz_io_read_at_mapped(core->io, bbi->addr, buf.get(), (int)bbi->size);\n\n        auto vec = fromOwned(\n                rz_pvector_new(reinterpret_cast<RzPVectorFree>(rz_analysis_disasm_text_free)));\n        if (!vec) {\n            break;\n        }\n\n        RzCoreDisasmOptions options = {};\n        options.vec = vec.get();\n        options.cbytes = 1;\n\n        rz_core_print_disasm(core, bbi->addr, buf.get(), (int)bbi->size, (int)bbi->size, NULL,\n                             &options);\n\n        auto vecVisitor = CutterPVector<RzAnalysisDisasmText>(vec.get());\n        auto iter = vecVisitor.begin();\n        while (iter != vecVisitor.end()) {\n            RzAnalysisDisasmText *op = *iter;\n            Instr instr;\n            instr.addr = op->offset;\n\n            ++iter;\n            if (iter != vecVisitor.end()) {\n                // get instruction size from distance to next instruction ...\n                RVA nextOffset = (*iter)->offset;\n                instr.size = nextOffset - instr.addr;\n            } else {\n                // or to the end of the block.\n                instr.size = (bbi->addr + bbi->size) - instr.addr;\n            }\n\n            QTextDocument textDoc;\n            textDoc.setHtml(CutterCore::ansiEscapeToHtml(op->text));\n\n            instr.plainText = textDoc.toPlainText();\n\n            RichTextPainter::List richText = RichTextPainter::fromTextDocument(textDoc);\n            // Colors::colorizeAssembly(richText, textDoc.toPlainText(), 0);\n\n            bool cropped;\n            int blockLength = Config()->getGraphBlockMaxChars()\n                    + Core()->getConfigb(\"asm.bytes\") * 24 + Core()->getConfigb(\"asm.emu\") * 10;\n            instr.text = Text(RichTextPainter::cropped(richText, blockLength, \"...\", &cropped));\n            if (cropped)\n                instr.fullText = richText;\n            else\n                instr.fullText = Text();\n            db.instrs.push_back(instr);\n        }\n        disassembly_blocks[db.entry] = db;\n        prepareGraphNode(gb);\n        addBlock(gb);\n    }\n    cleanupEdges(blocks);\n    computeGraphPlacement();\n}\n\nDisassemblerGraphView::EdgeConfigurationMapping DisassemblerGraphView::getEdgeConfigurations()\n{\n    EdgeConfigurationMapping result;\n    for (auto &block : blocks) {\n        for (const auto &edge : block.second.edges) {\n            result[{ block.first, edge.target }] =\n                    edgeConfiguration(block.second, &blocks[edge.target], false);\n        }\n    }\n    return result;\n}\n\nvoid DisassemblerGraphView::prepareGraphNode(GraphBlock &block)\n{\n    DisassemblyBlock &db = disassembly_blocks[block.entry];\n    int width = 0;\n    int height = 0;\n    for (auto &line : db.header_text.lines) {\n        int lw = 0;\n        for (auto &part : line)\n            lw += mFontMetrics->width(part.text);\n        if (lw > width)\n            width = lw;\n        height += 1;\n    }\n    for (Instr &instr : db.instrs) {\n        for (auto &line : instr.text.lines) {\n            int lw = 0;\n            for (auto &part : line)\n                lw += mFontMetrics->width(part.text);\n            if (lw > width)\n                width = lw;\n            height += 1;\n        }\n    }\n    int extra = static_cast<int>(2 * padding + 4);\n    qreal indent = ACharWidth;\n    block.width = static_cast<int>(width + extra + indent);\n    block.height = (height * charHeight) + extra;\n}\n\nvoid DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive)\n{\n    QRectF blockRect(block.x, block.y, block.width, block.height);\n\n    p.setPen(Qt::black);\n    p.setBrush(Qt::gray);\n    p.setFont(Config()->getFont());\n    p.drawRect(blockRect);\n\n    // Render node\n    DisassemblyBlock &db = disassembly_blocks[block.entry];\n    bool block_selected = false;\n    RVA selected_instruction = RVA_INVALID;\n\n    // Figure out if the current block is selected\n    RVA addr = seekable->getOffset();\n    RVA PCAddr = Core()->getProgramCounterValue();\n    for (const Instr &instr : db.instrs) {\n        if (instr.contains(addr) && interactive) {\n            block_selected = true;\n            selected_instruction = instr.addr;\n        }\n\n        // TODO: L219\n    }\n\n    p.setPen(QColor(0, 0, 0, 0));\n    if (db.terminal) {\n        p.setBrush(retShadowColor);\n    } else if (db.indirectcall) {\n        p.setBrush(indirectcallShadowColor);\n    } else {\n        p.setBrush(QColor(0, 0, 0, 100));\n    }\n\n    p.setPen(QPen(graphNodeColor, 1));\n\n    if (block_selected) {\n        p.setBrush(disassemblySelectedBackgroundColor);\n    } else {\n        p.setBrush(disassemblyBackgroundColor);\n    }\n\n    // Draw basic block background\n    p.drawRect(blockRect);\n    auto bb = Core()->getBBHighlighter()->getBasicBlock(block.entry);\n    if (bb) {\n        QColor color(bb->color);\n        p.setBrush(color);\n        p.drawRect(blockRect);\n    }\n\n    const int firstInstructionY = block.y + getInstructionOffset(db, 0).y();\n\n    // Stop rendering text when it's too small\n    auto transform = p.combinedTransform();\n    QRect screenChar = transform.mapRect(QRect(0, 0, ACharWidth, charHeight));\n\n    if (screenChar.width() < Config()->getGraphMinFontSize()) {\n        return;\n    }\n\n    qreal indent = ACharWidth;\n\n    // Highlight selected tokens\n    if (interactive && highlight_token != nullptr) {\n        int y = firstInstructionY;\n        qreal tokenWidth = mFontMetrics->width(highlight_token->content);\n\n        for (const Instr &instr : db.instrs) {\n            int pos = -1;\n\n            while ((pos = instr.plainText.indexOf(highlight_token->content, pos + 1)) != -1) {\n                int tokenEnd = pos + highlight_token->content.length();\n\n                if ((pos > 0 && instr.plainText[pos - 1].isLetterOrNumber())\n                    || (tokenEnd < instr.plainText.length()\n                        && instr.plainText[tokenEnd].isLetterOrNumber())) {\n                    continue;\n                }\n\n                qreal widthBefore = mFontMetrics->width(instr.plainText.left(pos));\n                qreal textOffset = padding + indent;\n                if (textOffset + widthBefore > block.width - (10 + padding)) {\n                    continue;\n                }\n\n                qreal highlightWidth = tokenWidth;\n                if (textOffset + widthBefore + tokenWidth >= block.width - (10 + padding)) {\n                    highlightWidth = block.width - widthBefore - (10 + 2 * padding);\n                }\n\n                QColor selectionColor = ConfigColor(\"wordHighlight\");\n\n                p.fillRect(\n                        QRectF(block.x + textOffset + widthBefore, y, highlightWidth, charHeight),\n                        selectionColor);\n            }\n\n            y += int(instr.text.lines.size()) * charHeight;\n        }\n    }\n\n    // Render node text\n    auto x = block.x + padding;\n    int y = block.y + getTextOffset(0).y();\n    for (auto &line : db.header_text.lines) {\n        RichTextPainter::paintRichText<qreal>(&p, x, y, block.width, charHeight, 0, line,\n                                              mFontMetrics.get());\n        y += charHeight;\n    }\n\n    auto bih = Core()->getBIHighlighter();\n    for (const Instr &instr : db.instrs) {\n        const QRect instrRect = QRect(static_cast<int>(block.x + indent), y,\n                                      static_cast<int>(block.width - (10 + padding)),\n                                      int(instr.text.lines.size()) * charHeight);\n\n        QColor instrColor;\n        if (Core()->isBreakpoint(breakpoints, instr.addr)) {\n            instrColor = ConfigColor(\"gui.breakpoint_background\");\n        } else if (instr.addr == PCAddr) {\n            instrColor = PCSelectionColor;\n        } else if (auto background = bih->getBasicInstruction(instr.addr)) {\n            instrColor = background->color;\n        }\n\n        if (instrColor.isValid()) {\n            p.fillRect(instrRect, instrColor);\n        }\n\n        if (selected_instruction != RVA_INVALID && selected_instruction == instr.addr) {\n            p.fillRect(instrRect, disassemblySelectionColor);\n        }\n\n        for (auto &line : instr.text.lines) {\n\n            RichTextPainter::paintRichText<qreal>(&p, x + indent, y, block.width - padding,\n                                                  charHeight, 0, line, mFontMetrics.get());\n            y += charHeight;\n        }\n    }\n}\n\nGraphView::EdgeConfiguration DisassemblerGraphView::edgeConfiguration(GraphView::GraphBlock &from,\n                                                                      GraphView::GraphBlock *to,\n                                                                      bool interactive)\n{\n    EdgeConfiguration ec;\n    DisassemblyBlock &db = disassembly_blocks[from.entry];\n    if (to->entry == db.true_path) {\n        ec.color = brtrueColor;\n    } else if (to->entry == db.false_path) {\n        ec.color = brfalseColor;\n    } else {\n        ec.color = jmpColor;\n    }\n    ec.start_arrow = false;\n    ec.end_arrow = true;\n    if (interactive) {\n        if (from.entry == currentBlockAddress) {\n            ec.width_scale = 2.0;\n        } else if (to->entry == currentBlockAddress) {\n            ec.width_scale = 2.0;\n        }\n    }\n    return ec;\n}\n\nbool DisassemblerGraphView::eventFilter(QObject *obj, QEvent *event)\n{\n    if ((Config()->getGraphPreview() || Config()->getShowVarTooltips())\n        && event->type() == QEvent::Type::ToolTip) {\n\n        QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);\n        QPoint point = viewToLogicalCoordinates(helpEvent->pos());\n\n        if (auto block = getBlockContaining(point)) {\n            // Get pos relative to start of block\n            QPoint pos = point - QPoint(block->x, block->y);\n\n            // offsetFrom is the address which on top the cursor triggered this\n            RVA offsetFrom = RVA_INVALID;\n\n            /*\n             * getAddrForMouseEvent() doesn't work for jmps, like\n             * getInstrForMouseEvent() with false as a 3rd argument.\n             */\n            Instr *inst = getInstrForMouseEvent(*block, &pos, true);\n            if (inst != nullptr) {\n                offsetFrom = inst->addr;\n            }\n\n            // Don't preview anything for a small scale\n            if (getViewScale() >= 0.8) {\n                auto token = getToken(inst, pos.x());\n                DH::TargetContext ctx;\n                ctx.offset = offsetFrom;\n                ctx.word = token ? token->content : QString();\n                ctx.line = inst->plainText;\n                ctx.arrow = getTruePathForOffset(offsetFrom);\n                if (DisassemblyPreview::showTooltip(this, helpEvent->globalPos(), ctx,\n                                                    Config()->getGraphPreview())) {\n                    return true;\n                }\n            }\n        }\n    }\n    return CutterGraphView::eventFilter(obj, event);\n}\n\nvoid DisassemblerGraphView::keyPressEvent(QKeyEvent *event)\n{\n    if (event->key() == Qt::Key_Return && seekable) {\n        RVA offset = seekable->getOffset();\n        // pressing enter at last instruction of the block seeks to true path if valid\n        RVA truePath = getTruePathForOffset(offset);\n        if (truePath != RVA_INVALID) {\n            seekable->seek(truePath);\n        } else {\n            seekable->seekToReference(offset);\n        }\n    }\n\n    CutterGraphView::keyPressEvent(event);\n}\n\nRVA DisassemblerGraphView::getAddrForMouseEvent(GraphBlock &block, QPoint *point)\n{\n    DisassemblyBlock &db = disassembly_blocks[block.entry];\n\n    // Remove header and margin\n    int off_y = getInstructionOffset(db, 0).y();\n\n    // Get mouse coordinate over the actual text\n    int text_point_y = point->y() - off_y;\n    int mouse_row = text_point_y / charHeight;\n\n    // If mouse coordinate is in header region or margin above\n    if (mouse_row < 0) {\n        return db.entry;\n    }\n\n    Instr *instr = getInstrForMouseEvent(block, point);\n    if (instr) {\n        return instr->addr;\n    }\n\n    return RVA_INVALID;\n}\n\nDisassemblerGraphView::Instr *\nDisassemblerGraphView::getInstrForMouseEvent(GraphView::GraphBlock &block, QPoint *point,\n                                             bool force)\n{\n    DisassemblyBlock &db = disassembly_blocks[block.entry];\n\n    // Remove header and margin\n    int off_y = getInstructionOffset(db, 0).y();\n\n    // Get mouse coordinate over the actual text\n    int text_point_y = point->y() - off_y;\n    int mouse_row = text_point_y / charHeight;\n\n    // Row in actual text\n    int cur_row = 0;\n\n    for (Instr &instr : db.instrs) {\n        if (mouse_row < cur_row + (int)instr.text.lines.size()) {\n            return &instr;\n        }\n        cur_row += instr.text.lines.size();\n    }\n    if (force && !db.instrs.empty()) {\n        if (mouse_row <= 0) {\n            return &db.instrs.front();\n        } else {\n            return &db.instrs.back();\n        }\n    }\n\n    return nullptr;\n}\n\nQRectF DisassemblerGraphView::getInstrRect(GraphView::GraphBlock &block, RVA addr) const\n{\n    auto blockIt = disassembly_blocks.find(block.entry);\n    if (blockIt == disassembly_blocks.end()) {\n        return QRectF();\n    }\n    auto &db = blockIt->second;\n    if (db.instrs.empty()) {\n        return QRectF();\n    }\n\n    size_t sequenceAddr = db.instrs[0].addr;\n    size_t firstLineWithAddr = 0;\n    size_t currentLine = 0;\n    for (size_t i = 0; i < db.instrs.size(); i++) {\n        auto &instr = db.instrs[i];\n        if (instr.addr != sequenceAddr) {\n            sequenceAddr = instr.addr;\n            firstLineWithAddr = currentLine;\n        }\n        if (instr.contains(addr)) {\n            while (i < db.instrs.size() && db.instrs[i].addr == sequenceAddr) {\n                currentLine += db.instrs[i].text.lines.size();\n                i++;\n            }\n            QPointF topLeft = getInstructionOffset(db, static_cast<int>(firstLineWithAddr));\n            return QRectF(topLeft,\n                          QSizeF(block.width - 2 * padding,\n                                 charHeight * int(currentLine - firstLineWithAddr)));\n        }\n        currentLine += instr.text.lines.size();\n    }\n    return QRectF();\n}\n\nRVA DisassemblerGraphView::getTruePathForOffset(RVA offset)\n{\n    DisassemblyBlock *db = blockForAddress(offset);\n    if (db && !db->instrs.empty()) {\n        Instr lastInstruction = db->instrs.back();\n        if (lastInstruction.addr == offset) {\n            return db->true_path;\n        }\n    }\n    return RVA_INVALID;\n}\n\nvoid DisassemblerGraphView::showInstruction(GraphView::GraphBlock &block, RVA addr)\n{\n    QRectF rect = getInstrRect(block, addr);\n    rect.translate(block.x, block.y);\n    showRectangle(QRect(rect.x(), rect.y(), rect.width(), rect.height()), true);\n}\n\nDisassemblerGraphView::DisassemblyBlock *DisassemblerGraphView::blockForAddress(RVA addr)\n{\n    for (auto &blockIt : disassembly_blocks) {\n        DisassemblyBlock &db = blockIt.second;\n        for (const Instr &i : db.instrs) {\n            if (i.addr == RVA_INVALID || i.size == RVA_INVALID) {\n                continue;\n            }\n\n            if (i.contains(addr)) {\n                return &db;\n            }\n        }\n    }\n    return nullptr;\n}\n\nconst DisassemblerGraphView::Instr *DisassemblerGraphView::instrForAddress(RVA addr)\n{\n    DisassemblyBlock *block = blockForAddress(addr);\n    for (const Instr &i : block->instrs) {\n        if (i.addr == RVA_INVALID || i.size == RVA_INVALID) {\n            continue;\n        }\n\n        if (i.contains(addr)) {\n            return &i;\n        }\n    }\n    return nullptr;\n}\n\nvoid DisassemblerGraphView::onSeekChanged(RVA addr)\n{\n    blockMenu->setOffset(addr);\n    DisassemblyBlock *db = blockForAddress(addr);\n    bool switchFunction = false;\n    if (!db) {\n        // not in this function, try refreshing\n        refreshView();\n        db = blockForAddress(addr);\n        switchFunction = true;\n    }\n    if (db) {\n        // This is a local address! We animated to it.\n        transition_dont_seek = true;\n        showBlock(blocks[db->entry], !switchFunction);\n        showInstruction(blocks[db->entry], addr);\n    }\n}\n\nvoid DisassemblerGraphView::takeTrue()\n{\n    DisassemblyBlock *db = blockForAddress(seekable->getOffset());\n    if (!db) {\n        return;\n    }\n\n    if (db->true_path != RVA_INVALID) {\n        seekable->seek(db->true_path);\n    } else if (!blocks[db->entry].edges.empty()) {\n        seekable->seek(blocks[db->entry].edges[0].target);\n    }\n}\n\nvoid DisassemblerGraphView::takeFalse()\n{\n    DisassemblyBlock *db = blockForAddress(seekable->getOffset());\n    if (!db) {\n        return;\n    }\n\n    if (db->false_path != RVA_INVALID) {\n        seekable->seek(db->false_path);\n    } else if (!blocks[db->entry].edges.empty()) {\n        seekable->seek(blocks[db->entry].edges[0].target);\n    }\n}\n\nvoid DisassemblerGraphView::setTooltipStylesheet()\n{\n    setStyleSheet(DisassemblyPreview::getToolTipStyleSheet());\n}\n\nvoid DisassemblerGraphView::seekInstruction(bool previous_instr)\n{\n    RVA addr = seekable->getOffset();\n    DisassemblyBlock *db = blockForAddress(addr);\n    if (!db) {\n        return;\n    }\n\n    for (size_t i = 0; i < db->instrs.size(); i++) {\n        Instr &instr = db->instrs[i];\n        if (!instr.contains(addr)) {\n            continue;\n        }\n\n        // Found the instruction. Check if a next one exists\n        if (!previous_instr && (i < db->instrs.size() - 1)) {\n            seekable->seek(db->instrs[i + 1].addr);\n        } else if (previous_instr && (i > 0)) {\n            while (i > 0 && db->instrs[i].addr == addr) { // jump over 0 size instructions\n                i--;\n            }\n            seekable->seek(db->instrs[i].addr);\n            break;\n        }\n    }\n}\n\nvoid DisassemblerGraphView::nextInstr()\n{\n    seekInstruction(false);\n}\n\nvoid DisassemblerGraphView::prevInstr()\n{\n    seekInstruction(true);\n}\n\nvoid DisassemblerGraphView::seekLocal(RVA addr, bool update_viewport)\n{\n    RVA curAddr = seekable->getOffset();\n    if (addr == curAddr) {\n        return;\n    }\n    connectSeekChanged(true);\n    seekable->seek(addr);\n    connectSeekChanged(false);\n    if (update_viewport) {\n        viewport()->update();\n    }\n}\n\nvoid DisassemblerGraphView::copySelection()\n{\n    if (!highlight_token)\n        return;\n\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(highlight_token->content);\n}\n\nDisassemblerGraphView::Token *DisassemblerGraphView::getToken(Instr *instr, int x)\n{\n    x -= (int)(padding + ACharWidth); // Ignore left margin\n    if (x < 0) {\n        return nullptr;\n    }\n\n    int clickedCharPos = mFontMetrics->position(instr->plainText, x);\n    if (clickedCharPos > instr->plainText.length()) {\n        return nullptr;\n    }\n\n    static const QRegularExpression tokenRegExp(R\"(\\b(?<!\\.)([^\\s]+)\\b(?!\\.))\");\n    QRegularExpressionMatchIterator i = tokenRegExp.globalMatch(instr->plainText);\n\n    while (i.hasNext()) {\n        QRegularExpressionMatch match = i.next();\n\n        if (match.capturedStart() <= clickedCharPos && match.capturedEnd() > clickedCharPos) {\n            auto t = new Token;\n            t->start = match.capturedStart();\n            t->length = match.capturedLength();\n            t->content = match.captured();\n            t->instr = instr;\n\n            return t;\n        }\n    }\n\n    return nullptr;\n}\n\nQPoint DisassemblerGraphView::getInstructionOffset(const DisassemblyBlock &block, int line) const\n{\n    return getTextOffset(line + static_cast<int>(block.header_text.lines.size()));\n}\n\nvoid DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event,\n                                         QPoint pos)\n{\n    Instr *instr = getInstrForMouseEvent(block, &pos, event->button() == Qt::RightButton);\n    if (!instr) {\n        return;\n    }\n\n    currentBlockAddress = block.entry;\n\n    highlight_token = getToken(instr, pos.x());\n\n    RVA addr = instr->addr;\n    seekLocal(addr);\n\n    blockMenu->setOffset(addr);\n    blockMenu->setCanCopy(highlight_token);\n    if (highlight_token) {\n        blockMenu->setCurHighlightedWord(highlight_token->content);\n    }\n    viewport()->update();\n}\n\nvoid DisassemblerGraphView::blockContextMenuRequested(GraphView::GraphBlock &block,\n                                                      QContextMenuEvent *event, QPoint /*pos*/)\n{\n    const RVA offset = this->seekable->getOffset();\n    actionUnhighlight.setVisible(Core()->getBBHighlighter()->getBasicBlock(block.entry));\n    actionUnhighlightInstruction.setVisible(\n            Core()->getBIHighlighter()->getBasicInstruction(offset));\n    event->accept();\n    blockMenu->exec(event->globalPos());\n}\n\nvoid DisassemblerGraphView::contextMenuEvent(QContextMenuEvent *event)\n{\n    if (event->reason() == QContextMenuEvent::Keyboard) {\n        auto *db = blockForAddress(seekable->getOffset());\n        if (db) {\n            auto gb = blocks[db->entry];\n            blockContextMenuRequested(gb, event, QPoint());\n            return;\n        }\n    }\n\n    GraphView::contextMenuEvent(event);\n    if (!event->isAccepted()) {\n        contextMenu->exec(event->globalPos());\n        event->accept();\n    }\n}\n\nvoid DisassemblerGraphView::showExportDialog()\n{\n    if (currentFcnAddr == RVA_INVALID) {\n        qWarning() << \"Cannot find current function.\";\n        return;\n    }\n\n    QString defaultName = \"graph\";\n    if (auto f = Core()->functionIn(currentFcnAddr)) {\n        QString functionName = f->name;\n        // don't confuse image type guessing and make c++ names somewhat usable\n        functionName.replace(QRegularExpression(\"[.:]\"), \"_\");\n        functionName.remove(QRegularExpression(\"[^a-zA-Z0-9_].*\"));\n        if (!functionName.isEmpty()) {\n            defaultName = functionName;\n        }\n    }\n    showExportGraphDialog(defaultName, RZ_CORE_GRAPH_TYPE_BLOCK_FUN, currentFcnAddr);\n}\n\nvoid DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,\n                                               QPoint pos)\n{\n    Q_UNUSED(event);\n\n    Instr *instr = getInstrForMouseEvent(block, &pos);\n    if (!instr) {\n        return;\n    }\n\n    DH::TargetContext ctx;\n    ctx.word = highlight_token ? highlight_token->content : QString();\n    ctx.line = instr->plainText;\n    ctx.offset = getAddrForMouseEvent(block, &pos);\n    ctx.arrow = getTruePathForOffset(ctx.offset);\n\n    DH::TargetAction ta = DH::resolveTarget(ctx);\n    switch (ta.type) {\n    case DH::TargetType::TypeName:\n        Core()->showTypeInTypesWidget(ctx.word);\n        break;\n    case DH::TargetType::XRefComment:\n    case DH::TargetType::VariableName:\n    case DH::TargetType::Arrow:\n        if (ta.offset != RVA_INVALID) {\n            seekable->seek(ta.offset);\n        }\n        break;\n    case DH::TargetType::None:\n        seekable->seekToReference(ctx.offset);\n        break;\n    }\n}\n\nvoid DisassemblerGraphView::blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event,\n                                           QPoint pos)\n{\n    Instr *instr = getInstrForMouseEvent(block, &pos);\n    if (!instr || instr->fullText.lines.empty()) {\n        QToolTip::hideText();\n        event->ignore();\n        return;\n    }\n\n    QToolTip::showText(event->globalPos(), instr->fullText.ToQString());\n}\n\nbool DisassemblerGraphView::helpEvent(QHelpEvent *event)\n{\n    if (!GraphView::helpEvent(event)) {\n        QToolTip::hideText();\n        event->ignore();\n    }\n\n    return true;\n}\n\nvoid DisassemblerGraphView::blockTransitionedTo(GraphView::GraphBlock *to)\n{\n    currentBlockAddress = to->entry;\n    if (transition_dont_seek) {\n        transition_dont_seek = false;\n        return;\n    }\n    seekLocal(to->entry);\n}\n\nvoid DisassemblerGraphView::onActionHighlightBITriggered()\n{\n    const RVA offset = this->seekable->getOffset();\n    const Instr *instr = instrForAddress(offset);\n\n    if (!instr) {\n        return;\n    }\n\n    auto bih = Core()->getBIHighlighter();\n    QColor background = ConfigColor(\"linehl\");\n    if (auto currentColor = bih->getBasicInstruction(offset)) {\n        background = currentColor->color;\n    }\n\n    QColor c =\n            QColorDialog::getColor(background, this, QString(), QColorDialog::DontUseNativeDialog);\n    if (c.isValid()) {\n        bih->highlight(instr->addr, instr->size, c);\n    }\n    Config()->colorsUpdated();\n}\n\nvoid DisassemblerGraphView::onActionUnhighlightBITriggered()\n{\n    const RVA offset = this->seekable->getOffset();\n    const Instr *instr = instrForAddress(offset);\n\n    if (!instr) {\n        return;\n    }\n\n    auto bih = Core()->getBIHighlighter();\n    bih->clear(instr->addr, instr->size);\n    Config()->colorsUpdated();\n}\n\nvoid DisassemblerGraphView::restoreCurrentBlock()\n{\n    onSeekChanged(this->seekable->getOffset()); // try to keep the view on current block\n}\n\nvoid DisassemblerGraphView::paintEvent(QPaintEvent *event)\n{\n    // DisassemblerGraphView is always dirty\n    setCacheDirty();\n    GraphView::paintEvent(event);\n}\n\nbool DisassemblerGraphView::Instr::contains(ut64 addr) const\n{\n    return this->addr <= addr && (addr - this->addr) < size;\n}\n"
  },
  {
    "path": "src/widgets/DisassemblerGraphView.h",
    "content": "#ifndef DISASSEMBLERGRAPHVIEW_H\n#define DISASSEMBLERGRAPHVIEW_H\n\n// Based on the DisassemblerGraphView from x64dbg\n\n#include <QWidget>\n#include <QPainter>\n#include <QShortcut>\n#include <QLabel>\n\n#include \"widgets/CutterGraphView.h\"\n#include \"menus/DisassemblyContextMenu.h\"\n#include \"common/RichTextPainter.h\"\n#include \"common/CutterSeekable.h\"\n\nclass QTextEdit;\nclass FallbackSyntaxHighlighter;\n\nclass DisassemblerGraphView : public CutterGraphView\n{\n    Q_OBJECT\n\n    struct Text\n    {\n        std::vector<RichTextPainter::List> lines;\n\n        Text() {}\n\n        Text(const QString &text, QColor color, QColor background)\n        {\n            RichTextPainter::List richText;\n            RichTextPainter::CustomRichText_t rt;\n            rt.highlight = false;\n            rt.text = text;\n            rt.textColor = color;\n            rt.textBackground = background;\n            rt.flags = rt.textBackground.alpha() ? RichTextPainter::FlagAll\n                                                 : RichTextPainter::FlagColor;\n            richText.push_back(rt);\n            lines.push_back(richText);\n        }\n\n        Text(const RichTextPainter::List &richText) { lines.push_back(richText); }\n\n        QString ToQString() const\n        {\n            QString result;\n            for (const auto &line : lines) {\n                for (const auto &t : line) {\n                    result += t.text;\n                }\n            }\n            return result;\n        }\n    };\n\n    struct Instr\n    {\n        ut64 addr = 0;\n        ut64 size = 0;\n        Text text;\n        Text fullText;\n        QString plainText;\n        std::vector<unsigned char> opcode; // instruction bytes\n\n        bool empty() const { return size == 0; }\n        bool contains(ut64 addr) const;\n    };\n\n    struct Token\n    {\n        int start;\n        int length;\n        QString type;\n        Instr *instr;\n        QString name;\n        QString content;\n    };\n\n    struct DisassemblyBlock\n    {\n        Text header_text;\n        std::vector<Instr> instrs;\n        ut64 entry = 0;\n        ut64 true_path = 0;\n        ut64 false_path = 0;\n        bool terminal = false;\n        bool indirectcall = false;\n    };\n\npublic:\n    DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable, MainWindow *mainWindow,\n                          QList<QAction *> additionalMenuAction);\n    ~DisassemblerGraphView() override;\n    std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks;\n    virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override;\n    virtual void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event,\n                              QPoint pos) override;\n    virtual void blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,\n                                    QPoint pos) override;\n    virtual bool helpEvent(QHelpEvent *event) override;\n    virtual void blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event,\n                                QPoint pos) override;\n    virtual GraphView::EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from,\n                                                           GraphView::GraphBlock *to,\n                                                           bool interactive) override;\n    virtual void blockTransitionedTo(GraphView::GraphBlock *to) override;\n\n    void loadCurrentGraph();\n    QString windowTitle;\n\n    int getWidth() { return width; }\n    int getHeight() { return height; }\n    std::unordered_map<ut64, GraphBlock> getBlocks() { return blocks; }\n    using EdgeConfigurationMapping = std::map<std::pair<ut64, ut64>, EdgeConfiguration>;\n    EdgeConfigurationMapping getEdgeConfigurations();\n\n    /**\n     * @brief keep the current addr of the fcn of Graph\n     * Everytime overview updates its contents, it compares this value with the one in Graph\n     * if they aren't same, then Overview needs to update the pixmap cache.\n     */\n    ut64 currentFcnAddr = RVA_INVALID; // TODO: make this less public\npublic slots:\n    void refreshView() override;\n\n    void onSeekChanged(RVA addr);\n    void takeTrue();\n    void takeFalse();\n\n    void nextInstr();\n    void prevInstr();\n\n    void copySelection();\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n    void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,\n                                   QPoint pos) override;\n    void contextMenuEvent(QContextMenuEvent *event) override;\n    void restoreCurrentBlock() override;\n    bool eventFilter(QObject *obj, QEvent *event) override;\n    void keyPressEvent(QKeyEvent *event) override;\n\nprivate slots:\n    void showExportDialog() override;\n    void onActionHighlightBITriggered();\n    void onActionUnhighlightBITriggered();\n    void setTooltipStylesheet();\n\nprivate:\n    bool transition_dont_seek = false;\n\n    Token *highlight_token;\n    bool emptyGraph;\n    ut64 currentBlockAddress = RVA_INVALID;\n\n    DisassemblyContextMenu *blockMenu;\n    QMenu *contextMenu;\n\n    void connectSeekChanged(bool disconnect);\n\n    void prepareGraphNode(GraphBlock &block);\n    Token *getToken(Instr *instr, int x);\n\n    QPoint getInstructionOffset(const DisassemblyBlock &block, int line) const;\n    RVA getAddrForMouseEvent(GraphBlock &block, QPoint *point);\n    Instr *getInstrForMouseEvent(GraphBlock &block, QPoint *point, bool force = false);\n    /**\n     * @brief Get instructions placement and size relative to block.\n     * Inefficient don't use this function when iterating over all instructions.\n     * @param block\n     * @param addr\n     * @return\n     */\n    QRectF getInstrRect(GraphView::GraphBlock &block, RVA addr) const;\n    /**\n     * @brief Get the true path address if the offset represents the last instruction of block\n     * @param offset Offset to check\n     * @return RVA Offset for true path\n     */\n    RVA getTruePathForOffset(RVA offset);\n    void showInstruction(GraphView::GraphBlock &block, RVA addr);\n    const Instr *instrForAddress(RVA addr);\n    DisassemblyBlock *blockForAddress(RVA addr);\n    void seekLocal(RVA addr, bool update_viewport = true);\n    void seekInstruction(bool previous_instr);\n\n    CutterSeekable *seekable = nullptr;\n    QList<QShortcut *> shortcuts;\n    QList<RVA> breakpoints;\n\n    QAction actionUnhighlight;\n    QAction actionUnhighlightInstruction;\n\n    QLabel *emptyText = nullptr;\n\nsignals:\n    void nameChanged(const QString &name);\n\npublic:\n    bool isGraphEmpty() { return emptyGraph; }\n};\n\n#endif // DISASSEMBLERGRAPHVIEW_H\n"
  },
  {
    "path": "src/widgets/DisassemblyWidget.cpp",
    "content": "#include \"DisassemblyWidget.h\"\n#include \"menus/DisassemblyContextMenu.h\"\n#include \"common/Configuration.h\"\n#include \"common/DisassemblyPreview.h\"\n#include \"common/Helpers.h\"\n#include \"common/TempConfig.h\"\n#include \"common/SelectionHighlight.h\"\n#include \"common/BinaryTrees.h\"\n#include \"core/MainWindow.h\"\n#include \"widgets/AddressRangeScrollBar.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include \"DisassemblyHelper.h\"\n\n#include <QApplication>\n#include <QScrollBar>\n#include <QJsonArray>\n#include <QJsonObject>\n#include <QVBoxLayout>\n#include <QRegularExpression>\n#include <QtMath>\n#include <QTextBlockUserData>\n#include <QPainter>\n#include <QPainterPath>\n#include <QSplitter>\n\n#include <algorithm>\n#include <cmath>\n#include <cstring>\n\nnamespace DH = DisassemblyHelper;\n\nDisassemblyWidget::DisassemblyWidget(MainWindow *main)\n    : MemoryDockWidget(MemoryWidgetType::Disassembly, main),\n      mCtxMenu(new DisassemblyContextMenu(this, main)),\n      mDisasScrollArea(new DisassemblyScrollArea(this)),\n      mDisasTextEdit(new DisassemblyTextEdit(this))\n{\n    setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());\n    updateWindowTitle();\n\n    topOffset = bottomOffset = RVA_INVALID;\n    cursorLineOffset = 0;\n    cursorCharOffset = 0;\n    seekFromCursor = false;\n\n    // Instantiate the window layout\n    auto *splitter = new QSplitter;\n\n    // Setup the left frame that contains breakpoints and jumps\n    leftPanel = new DisassemblyLeftPanel(this);\n    splitter->addWidget(leftPanel);\n\n    // Setup the disassembly content\n    auto *layout = new QHBoxLayout;\n    layout->addWidget(mDisasTextEdit);\n    layout->setContentsMargins(0, 0, 0, 0);\n    mDisasScrollArea->viewport()->setLayout(layout);\n    splitter->addWidget(mDisasScrollArea);\n    connect(mDisasScrollArea->verticalScrollBar(), &QScrollBar::valueChanged, this,\n            [this](int) { refreshDisasm(mDisasScrollArea->verticalScrollBar()->address()); });\n    // Use stylesheet instead of QWidget::setFrameShape(QFrame::NoShape) to avoid\n    // issues with dark and light interface themes\n    mDisasScrollArea->setStyleSheet(\"QAbstractScrollArea { border: 0px transparent black; }\");\n    mDisasTextEdit->setStyleSheet(\"QPlainTextEdit { border: 0px transparent black; }\");\n    mDisasTextEdit->setFocusProxy(this);\n    mDisasTextEdit->setFocusPolicy(Qt::ClickFocus);\n    mDisasScrollArea->setFocusProxy(this);\n    mDisasScrollArea->setFocusPolicy(Qt::ClickFocus);\n\n    setFocusPolicy(Qt::ClickFocus);\n\n    // Behave like all widgets: highlight on focus and hover\n    connect(qApp, &QApplication::focusChanged, this, [this](QWidget *, QWidget *now) {\n        QColor borderColor = this == now ? palette().color(QPalette::Highlight)\n                                         : palette().color(QPalette::WindowText).darker();\n        widget()->setStyleSheet(QString(\"QSplitter { border: %1px solid %2 } \\n\"\n                                        \"QSplitter:hover { border: %1px solid %3 } \\n\")\n                                        .arg(devicePixelRatio())\n                                        .arg(borderColor.name())\n                                        .arg(palette().color(QPalette::Highlight).name()));\n    });\n\n    splitter->setFrameShape(QFrame::Box);\n    // Set current widget to the splitted layout we just created\n    setWidget(splitter);\n\n    // Resize properly\n    QList<int> sizes;\n    sizes.append(3);\n    sizes.append(1);\n    splitter->setSizes(sizes);\n\n    setAllowedAreas(Qt::AllDockWidgetAreas);\n\n    setupFonts();\n    setupColors();\n\n    disasmRefresh = createReplacingRefreshDeferrer<RVA>(\n            false, [this](const RVA *offset) { refreshDisasm(offset ? *offset : RVA_INVALID); });\n\n    maxLines = 0;\n    updateMaxLines();\n\n    mDisasTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n    mDisasTextEdit->setFont(Config()->getFont());\n    mDisasTextEdit->setReadOnly(true);\n    mDisasTextEdit->setLineWrapMode(QPlainTextEdit::WidgetWidth);\n    // wrapping breaks readCurrentDisassemblyOffset() at the moment :-(\n    mDisasTextEdit->setWordWrapMode(QTextOption::NoWrap);\n\n    // Increase asm text edit margin\n    QTextDocument *asm_docu = mDisasTextEdit->document();\n    asm_docu->setDocumentMargin(10);\n\n    // Event filter to intercept double clicks in the textbox\n    // and showing tooltips when hovering above those offsets\n    mDisasTextEdit->viewport()->installEventFilter(this);\n\n    // Set Disas context menu\n    mDisasTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(mDisasTextEdit, &QWidget::customContextMenuRequested, this,\n            &DisassemblyWidget::showDisasContextMenu);\n\n    connect(mDisasScrollArea, &DisassemblyScrollArea::scrollLines, this,\n            &DisassemblyWidget::scrollInstructions);\n    connect(mDisasScrollArea, &DisassemblyScrollArea::disassemblyResized, this,\n            &DisassemblyWidget::updateMaxLines);\n    connect(mDisasScrollArea, &DisassemblyScrollArea::wheelEventTriggered, this,\n            &DisassemblyWidget::showTransientScrollBar);\n\n    connectCursorPositionChanged(false);\n\n    connect(Core(), &CutterCore::commentsChanged, this, [this]() { refreshDisasm(); });\n    connect(Core(), SIGNAL(flagsChanged()), this, SLOT(refreshDisasm()));\n    connect(Core(), SIGNAL(globalVarsChanged()), this, SLOT(refreshDisasm()));\n    connect(Core(), SIGNAL(functionsChanged()), this, SLOT(refreshDisasm()));\n    connect(Core(), &CutterCore::functionRenamed, this, [this]() { refreshDisasm(); });\n    connect(Core(), SIGNAL(varsChanged()), this, SLOT(refreshDisasm()));\n    connect(Core(), SIGNAL(asmOptionsChanged()), this, SLOT(refreshDisasm()));\n    connect(Core(), &CutterCore::instructionChanged, this, &DisassemblyWidget::instructionChanged);\n    connect(Core(), &CutterCore::breakpointsChanged, this, &DisassemblyWidget::refreshIfInRange);\n    connect(Core(), SIGNAL(refreshCodeViews()), this, SLOT(refreshDisasm()));\n\n    connect(Config(), &Configuration::fontsUpdated, this, &DisassemblyWidget::fontsUpdatedSlot);\n    connect(Config(), &Configuration::colorsUpdated, this, &DisassemblyWidget::colorsUpdatedSlot);\n\n    connect(Core(), &CutterCore::refreshAll, this,\n            [this]() { refreshDisasm(seekable->getOffset()); });\n    refreshDisasm(seekable->getOffset());\n\n    connect(mCtxMenu, &DisassemblyContextMenu::copy, mDisasTextEdit, &QPlainTextEdit::copy);\n\n    mCtxMenu->addSeparator();\n    mCtxMenu->addAction(&syncAction);\n    connect(seekable, &CutterSeekable::seekableSeekChanged, this,\n            &DisassemblyWidget::on_seekChanged);\n\n    addActions(mCtxMenu->actions());\n\n#define ADD_ACTION(id, ctx, slot)                                                                  \\\n    {                                                                                              \\\n        QAction *a = Shortcuts()->makeAction(id, this);                                            \\\n        a->setShortcutContext(ctx);                                                                \\\n        addAction(a);                                                                              \\\n        connect(a, &QAction::triggered, this, (slot));                                             \\\n    }\n\n    // Space to switch to graph\n    ADD_ACTION(\"Disassembly.switchToGraph\", Qt::WidgetWithChildrenShortcut,\n               [this] { mainWindow->showMemoryWidget(MemoryWidgetType::Graph); })\n\n    ADD_ACTION(\"General.seekPrev\", Qt::WidgetWithChildrenShortcut, &DisassemblyWidget::seekPrev)\n\n    ADD_ACTION(\"Disassembly.moveDown\", Qt::WidgetWithChildrenShortcut,\n               [this]() { moveCursorRelative(false, false); })\n    ADD_ACTION(\"Disassembly.moveUp\", Qt::WidgetWithChildrenShortcut,\n               [this]() { moveCursorRelative(true, false); })\n    ADD_ACTION(\"Disassembly.pageDown\", Qt::WidgetWithChildrenShortcut,\n               [this]() { moveCursorRelative(false, true); })\n    ADD_ACTION(\"Disassembly.pageUp\", Qt::WidgetWithChildrenShortcut,\n               [this]() { moveCursorRelative(true, true); })\n#undef ADD_ACTION\n}\n\nvoid DisassemblyWidget::setPreviewMode(bool previewMode)\n{\n    mDisasTextEdit->setContextMenuPolicy(previewMode ? Qt::NoContextMenu : Qt::CustomContextMenu);\n    mCtxMenu->setEnabled(!previewMode);\n    for (auto action : mCtxMenu->actions()) {\n        action->setEnabled(!previewMode);\n    }\n    for (auto action : actions()) {\n        if (action->shortcut() == Qt::Key_Space || action->shortcut() == Qt::Key_Escape) {\n            action->setEnabled(!previewMode);\n        }\n    }\n    if (previewMode) {\n        seekable->setSynchronization(false);\n    }\n}\n\nQWidget *DisassemblyWidget::getTextWidget()\n{\n    return mDisasTextEdit;\n}\n\nQString DisassemblyWidget::getWidgetType()\n{\n    return \"Disassembly\";\n}\n\nQFontMetricsF DisassemblyWidget::getFontMetrics()\n{\n    return QFontMetricsF(mDisasTextEdit->font());\n}\n\nQList<DisassemblyLine> DisassemblyWidget::getLines()\n{\n    return lines;\n}\n\nvoid DisassemblyWidget::showTransientScrollBar()\n{\n    mDisasScrollArea->verticalScrollBar()->showTransientScrollBar();\n}\n\nvoid DisassemblyWidget::refreshIfInRange(RVA offset)\n{\n    if (offset >= topOffset && offset <= bottomOffset) {\n        refreshDisasm();\n    }\n}\n\nvoid DisassemblyWidget::instructionChanged(RVA offset)\n{\n    leftPanel->clearArrowFrom(offset);\n    refreshDisasm();\n}\n\nvoid DisassemblyWidget::refreshDisasm(RVA offset)\n{\n    if (!disasmRefresh->attemptRefresh(offset == RVA_INVALID ? nullptr : new RVA(offset))) {\n        return;\n    }\n\n    if (offset != RVA_INVALID) {\n        topOffset = offset;\n    }\n\n    if (topOffset == RVA_INVALID) {\n        return;\n    }\n\n    if (maxLines <= 0) {\n        connectCursorPositionChanged(true);\n        mDisasTextEdit->clear();\n        connectCursorPositionChanged(false);\n        return;\n    }\n\n    breakpoints = Core()->getBreakpointsAddresses();\n    int horizontalScrollValue = mDisasTextEdit->horizontalScrollBar()->value();\n    mDisasTextEdit->setLockScroll(true); // avoid flicker\n\n    // Retrieve disassembly lines\n    {\n        TempConfig tempConfig;\n        tempConfig.set(\"scr.color\", COLOR_MODE_16M).set(\"asm.lines\", false);\n        lines = Core()->disassembleLines(topOffset, maxLines);\n    }\n\n    connectCursorPositionChanged(true);\n\n    mDisasTextEdit->document()->clear();\n    QTextCursor cursor(mDisasTextEdit->document());\n    QTextBlockFormat regular = cursor.blockFormat();\n    for (const DisassemblyLine &line : lines) {\n        if (line.offset < topOffset) { // overflow\n            break;\n        }\n        cursor.insertHtml(line.text);\n        if (Core()->isBreakpoint(breakpoints, line.offset)) {\n            QTextBlockFormat f;\n            f.setBackground(ConfigColor(\"gui.breakpoint_background\"));\n            cursor.setBlockFormat(f);\n        }\n        auto a = new DisassemblyTextBlockUserData(line);\n        cursor.block().setUserData(a);\n        cursor.insertBlock();\n        cursor.setBlockFormat(regular);\n    }\n\n    if (!lines.isEmpty()) {\n        bottomOffset = lines[qMin(lines.size(), maxLines) - 1].offset;\n        if (bottomOffset < topOffset) {\n            bottomOffset = RVA_MAX;\n        }\n    } else {\n        bottomOffset = topOffset;\n    }\n\n    connectCursorPositionChanged(false);\n\n    updateCursorPosition();\n\n    // remove additional lines\n    QTextCursor tc = mDisasTextEdit->textCursor();\n    tc.movePosition(QTextCursor::Start);\n    tc.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, maxLines - 1);\n    tc.movePosition(QTextCursor::EndOfLine);\n    tc.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);\n    tc.removeSelectedText();\n\n    mDisasTextEdit->setLockScroll(false);\n    mDisasTextEdit->horizontalScrollBar()->setValue(horizontalScrollValue);\n    mDisasScrollArea->verticalScrollBar()->setPosition(topOffset);\n\n    // Refresh the left panel (trigger paintEvent)\n    leftPanel->update();\n}\n\nvoid DisassemblyWidget::scrollInstructions(int count, bool clampToScrollBarRange)\n{\n    if (count == 0) {\n        return;\n    }\n\n    RVA offset;\n    if (count > 0) {\n        offset = Core()->nextOpAddr(topOffset, count);\n        if (offset < topOffset) {\n            offset = RVA_MAX;\n        }\n    } else {\n        offset = Core()->prevOpAddr(topOffset, -count);\n        if (offset > topOffset) {\n            offset = 0;\n        }\n    }\n\n    if (clampToScrollBarRange) {\n        offset = mDisasScrollArea->verticalScrollBar()->clampAddressToRange(offset);\n    }\n\n    refreshDisasm(offset);\n    topOffsetHistory[topOffsetHistoryPos] = offset;\n}\n\nbool DisassemblyWidget::updateMaxLines()\n{\n    int currentMaxLines = qhelpers::getMaxFullyDisplayedLines(mDisasTextEdit);\n\n    if (currentMaxLines != maxLines) {\n        maxLines = currentMaxLines;\n        refreshDisasm();\n        return true;\n    }\n\n    return false;\n}\n\nvoid DisassemblyWidget::highlightCurrentLine()\n{\n    QList<QTextEdit::ExtraSelection> extraSelections;\n    QColor highlightColor = ConfigColor(\"lineHighlight\");\n\n    // Highlight the current word\n    QTextCursor cursor = mDisasTextEdit->textCursor();\n    auto clickedCharPos = cursor.positionInBlock();\n    // Select the line (BlockUnderCursor matches a line with current implementation)\n    cursor.select(QTextCursor::BlockUnderCursor);\n    // Remove any non-breakable space from the current line\n    QString searchString = cursor.selectedText().replace(\"\\xc2\\xa0\", \" \");\n    // Cut the line in \"tokens\" that can be highlighted\n    static const QRegularExpression tokenRegExp(R\"(\\b(?<!\\.)([^\\s]+)\\b(?!\\.))\");\n    QRegularExpressionMatchIterator i = tokenRegExp.globalMatch(searchString);\n    while (i.hasNext()) {\n        QRegularExpressionMatch match = i.next();\n        // Current token is under our cursor, select this one\n        if (match.capturedStart() <= clickedCharPos && match.capturedEnd() > clickedCharPos) {\n            curHighlightedWord = match.captured();\n            break;\n        }\n    }\n\n    // Highlight the current line\n    QTextEdit::ExtraSelection highlightSelection;\n    highlightSelection.cursor = cursor;\n    highlightSelection.cursor.movePosition(QTextCursor::Start);\n    while (true) {\n        RVA lineOffset = DH::readDisassemblyOffset(highlightSelection.cursor);\n        if (lineOffset == seekable->getOffset()) {\n            highlightSelection.format.setBackground(highlightColor);\n            highlightSelection.format.setProperty(QTextFormat::FullWidthSelection, true);\n            highlightSelection.cursor.clearSelection();\n            extraSelections.append(highlightSelection);\n        } else if (lineOffset != RVA_INVALID && lineOffset > seekable->getOffset()) {\n            break;\n        }\n        highlightSelection.cursor.movePosition(QTextCursor::EndOfLine);\n        if (highlightSelection.cursor.atEnd()) {\n            break;\n        }\n\n        highlightSelection.cursor.movePosition(QTextCursor::Down);\n    }\n\n    // Highlight all the words in the document same as the current one\n    extraSelections.append(createSameWordsSelections(mDisasTextEdit, curHighlightedWord));\n\n    mDisasTextEdit->setExtraSelections(extraSelections);\n}\n\nvoid DisassemblyWidget::highlightPCLine()\n{\n    RVA PCAddr = Core()->getProgramCounterValue();\n\n    QColor highlightPCColor = ConfigColor(\"highlightPC\");\n\n    QList<QTextEdit::ExtraSelection> pcSelections;\n    QTextEdit::ExtraSelection highlightSelection;\n    highlightSelection.cursor = mDisasTextEdit->textCursor();\n    highlightSelection.cursor.movePosition(QTextCursor::Start);\n    if (PCAddr != RVA_INVALID) {\n        while (true) {\n            RVA lineOffset = DH::readDisassemblyOffset(highlightSelection.cursor);\n            if (lineOffset == PCAddr) {\n                highlightSelection.format.setBackground(highlightPCColor);\n                highlightSelection.format.setProperty(QTextFormat::FullWidthSelection, true);\n                highlightSelection.cursor.clearSelection();\n                pcSelections.append(highlightSelection);\n            } else if (lineOffset != RVA_INVALID && lineOffset > PCAddr) {\n                break;\n            }\n            highlightSelection.cursor.movePosition(QTextCursor::EndOfLine);\n            if (highlightSelection.cursor.atEnd()) {\n                break;\n            }\n\n            highlightSelection.cursor.movePosition(QTextCursor::Down);\n        }\n    }\n\n    // Don't override any extraSelections already set\n    QList<QTextEdit::ExtraSelection> currentSelections = mDisasTextEdit->extraSelections();\n    currentSelections.append(pcSelections);\n\n    mDisasTextEdit->setExtraSelections(currentSelections);\n}\n\nvoid DisassemblyWidget::showDisasContextMenu(const QPoint &pt)\n{\n    mCtxMenu->exec(mDisasTextEdit->mapToGlobal(pt));\n}\n\nRVA DisassemblyWidget::readCurrentDisassemblyOffset()\n{\n    QTextCursor tc = mDisasTextEdit->textCursor();\n    return DH::readDisassemblyOffset(tc);\n}\n\nvoid DisassemblyWidget::updateCursorPosition()\n{\n    RVA offset = seekable->getOffset();\n\n    // already fine where it is?\n    RVA currentLineOffset = readCurrentDisassemblyOffset();\n    if (currentLineOffset == offset) {\n        return;\n    }\n\n    connectCursorPositionChanged(true);\n\n    if (offset < topOffset || (offset > bottomOffset && bottomOffset != RVA_INVALID)) {\n        mDisasTextEdit->moveCursor(QTextCursor::Start);\n        mDisasTextEdit->setExtraSelections(\n                createSameWordsSelections(mDisasTextEdit, curHighlightedWord));\n    } else {\n        RVA currentCursorOffset = readCurrentDisassemblyOffset();\n        QTextCursor originalCursor = mDisasTextEdit->textCursor();\n\n        QTextCursor cursor = originalCursor;\n        cursor.movePosition(QTextCursor::Start);\n\n        while (true) {\n            RVA lineOffset = DH::readDisassemblyOffset(cursor);\n            if (lineOffset == offset) {\n                if (cursorLineOffset > 0) {\n                    cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor,\n                                        cursorLineOffset);\n                }\n                if (cursorCharOffset > 0) {\n                    cursor.movePosition(QTextCursor::StartOfLine);\n                    cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor,\n                                        cursorCharOffset);\n                }\n\n                mDisasTextEdit->setTextCursor(cursor);\n                highlightCurrentLine();\n                break;\n            } else if (lineOffset != RVA_INVALID && lineOffset > offset) {\n                mDisasTextEdit->moveCursor(QTextCursor::Start);\n                mDisasTextEdit->setExtraSelections({});\n                break;\n            }\n\n            cursor.movePosition(QTextCursor::EndOfLine);\n            if (cursor.atEnd()) {\n                break;\n            }\n\n            cursor.movePosition(QTextCursor::Down);\n        }\n\n        // this is true if a seek came from the user clicking on a line.\n        // then the cursor should be restored 1:1 to retain selection and cursor position.\n        if (currentCursorOffset == offset) {\n            mDisasTextEdit->setTextCursor(originalCursor);\n        }\n    }\n\n    highlightPCLine();\n\n    connectCursorPositionChanged(false);\n}\n\nvoid DisassemblyWidget::connectCursorPositionChanged(bool disconnect)\n{\n    if (disconnect) {\n        QObject::disconnect(mDisasTextEdit, &QPlainTextEdit::cursorPositionChanged, this,\n                            &DisassemblyWidget::cursorPositionChanged);\n    } else {\n        connect(mDisasTextEdit, &QPlainTextEdit::cursorPositionChanged, this,\n                &DisassemblyWidget::cursorPositionChanged);\n    }\n}\n\nvoid DisassemblyWidget::cursorPositionChanged()\n{\n    RVA offset = readCurrentDisassemblyOffset();\n\n    cursorLineOffset = 0;\n    QTextCursor c = mDisasTextEdit->textCursor();\n    cursorCharOffset = c.positionInBlock();\n    while (c.blockNumber() > 0) {\n        c.movePosition(QTextCursor::PreviousBlock);\n        if (DH::readDisassemblyOffset(c) != offset) {\n            break;\n        }\n        cursorLineOffset++;\n    }\n\n    seekFromCursor = true;\n    seekable->seek(offset);\n    seekFromCursor = false;\n    highlightCurrentLine();\n    highlightPCLine();\n    mCtxMenu->setCanCopy(mDisasTextEdit->textCursor().hasSelection());\n    if (mDisasTextEdit->textCursor().hasSelection()) {\n        // A word is selected so use it\n        mCtxMenu->setCurHighlightedWord(mDisasTextEdit->textCursor().selectedText());\n    } else {\n        // No word is selected so use the word under the cursor\n        mCtxMenu->setCurHighlightedWord(curHighlightedWord);\n    }\n    leftPanel->update();\n}\n\nvoid DisassemblyWidget::moveCursorRelative(bool up, bool page)\n{\n    if (page) {\n        RVA offset;\n        if (!up) {\n            offset = Core()->nextOpAddr(bottomOffset, 1);\n            if (offset < bottomOffset) {\n                offset = RVA_MAX;\n            }\n        } else {\n            offset = Core()->prevOpAddr(topOffset, maxLines);\n            if (offset > topOffset) {\n                offset = 0;\n            } else {\n                // disassembly from calculated offset may have more than maxLines lines\n                // move some instructions down if necessary.\n\n                auto lines = Core()->disassembleLines(offset, maxLines).toVector();\n                int oldTopLine;\n                for (oldTopLine = lines.length(); oldTopLine > 0; oldTopLine--) {\n                    if (lines[oldTopLine - 1].offset < topOffset) {\n                        break;\n                    }\n                }\n\n                int overflowLines = oldTopLine - maxLines;\n                if (overflowLines > 0) {\n                    while (lines[overflowLines - 1].offset == lines[overflowLines].offset\n                           && overflowLines < lines.length() - 1) {\n                        overflowLines++;\n                    }\n                    offset = lines[overflowLines].offset;\n                }\n            }\n        }\n        refreshDisasm(offset);\n    } else { // normal arrow keys\n        int blockCount = mDisasTextEdit->blockCount();\n        if (blockCount < 1) {\n            return;\n        }\n\n        int blockNumber = mDisasTextEdit->textCursor().blockNumber();\n\n        if (blockNumber == blockCount - 1 && !up) {\n            scrollInstructions(1);\n        } else if (blockNumber == 0 && up) {\n            scrollInstructions(-1);\n        }\n\n        mDisasTextEdit->moveCursor(up ? QTextCursor::Up : QTextCursor::Down);\n\n        // handle cases where top instruction offsets change\n        RVA offset = readCurrentDisassemblyOffset();\n        if (offset != seekable->getOffset()) {\n            seekable->seek(offset);\n            highlightCurrentLine();\n            highlightPCLine();\n        }\n    }\n}\n\nvoid DisassemblyWidget::jumpToOffsetUnderCursor(const QTextCursor &cursor)\n{\n    RVA offset = DH::readDisassemblyOffset(cursor);\n    seekable->seekToReference(offset);\n}\n\nbool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)\n{\n    if (event->type() == QEvent::MouseButtonDblClick\n        && (obj == mDisasTextEdit || obj == mDisasTextEdit->viewport())) {\n        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);\n\n        if (mouseEvent->button() == Qt::LeftButton) {\n            auto ctx =\n                    DH::getContextFromCursor(mDisasTextEdit->cursorForPosition(mouseEvent->pos()));\n\n            DH::TargetAction ta = DH::resolveTarget(ctx);\n            switch (ta.type) {\n            case DH::TargetType::TypeName:\n                Core()->showTypeInTypesWidget(ctx.word);\n                break;\n            case DH::TargetType::XRefComment:\n            case DH::TargetType::VariableName:\n            case DH::TargetType::Arrow:\n                if (ta.offset != RVA_INVALID) {\n                    seekable->seek(ta.offset);\n                }\n                break;\n            case DH::TargetType::None:\n                seekable->seekToReference(ctx.offset);\n                break;\n            }\n            return true;\n        }\n    } else if ((Config()->getPreviewValue() || Config()->getShowVarTooltips())\n               && event->type() == QEvent::ToolTip && obj == mDisasTextEdit->viewport()) {\n        QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);\n\n        auto ctx = DH::getContextFromCursor(mDisasTextEdit->cursorForPosition(helpEvent->pos()));\n\n        return DisassemblyPreview::showTooltip(this, helpEvent->globalPos(), ctx,\n                                               Config()->getPreviewValue());\n    }\n\n    return MemoryDockWidget::eventFilter(obj, event);\n}\n\nvoid DisassemblyWidget::keyPressEvent(QKeyEvent *event)\n{\n    if (event->key() == Qt::Key_Return) {\n        const QTextCursor cursor = mDisasTextEdit->textCursor();\n        auto ta = DH::resolveTarget(DH::getContextFromCursor(cursor), DH::TargetFilter::Arrows);\n        if (ta.type == DH::TargetType::Arrow) {\n            seekable->seek(ta.offset);\n        } else {\n            jumpToOffsetUnderCursor(cursor);\n        }\n    }\n\n    MemoryDockWidget::keyPressEvent(event);\n}\n\nvoid DisassemblyWidget::contextMenuEvent(QContextMenuEvent *event)\n{\n    if (event->reason() == QContextMenuEvent::Keyboard) {\n        showDisasContextMenu(event->pos());\n        event->accept();\n        return;\n    }\n\n    MemoryDockWidget::contextMenuEvent(event);\n}\n\nQString DisassemblyWidget::getWindowTitle() const\n{\n    return tr(\"Disassembly\");\n}\n\nvoid DisassemblyWidget::on_seekChanged(RVA offset, CutterCore::SeekHistoryType type)\n{\n    if (type == CutterCore::SeekHistoryType::New) {\n        // Erase previous history past this point.\n        if (topOffsetHistory.size() > topOffsetHistoryPos + 1) {\n            topOffsetHistory.erase(topOffsetHistory.begin() + topOffsetHistoryPos + 1,\n                                   topOffsetHistory.end());\n        }\n        topOffsetHistory.push_back(offset);\n        topOffsetHistoryPos = topOffsetHistory.size() - 1;\n    } else if (type == CutterCore::SeekHistoryType::Undo) {\n        --topOffsetHistoryPos;\n    } else if (type == CutterCore::SeekHistoryType::Redo) {\n        ++topOffsetHistoryPos;\n    }\n    if (!seekFromCursor) {\n        cursorLineOffset = 0;\n        cursorCharOffset = 0;\n    }\n\n    if (topOffset != RVA_INVALID && offset >= topOffset && offset <= bottomOffset\n        && type == CutterCore::SeekHistoryType::New) {\n        // if the line with the seek offset is currently visible, just move the cursor there\n        updateCursorPosition();\n        topOffsetHistory[topOffsetHistoryPos] = topOffset;\n    } else {\n        // otherwise scroll there\n        refreshDisasm(topOffsetHistory[topOffsetHistoryPos]);\n    }\n    mCtxMenu->setOffset(offset);\n    // after seek it will select curret instruction and updates renaming options\n    mCtxMenu->setCurHighlightedWord(curHighlightedWord);\n}\n\nvoid DisassemblyWidget::fontsUpdatedSlot()\n{\n    setupFonts();\n\n    if (!updateMaxLines()) { // updateMaxLines() returns true if it already refreshed.\n        refreshDisasm();\n    }\n}\n\nvoid DisassemblyWidget::colorsUpdatedSlot()\n{\n    setupColors();\n    refreshDisasm();\n}\n\nvoid DisassemblyWidget::setupFonts()\n{\n    mDisasTextEdit->setFont(Config()->getFont());\n}\n\nvoid DisassemblyWidget::setupColors()\n{\n    mDisasTextEdit->setStyleSheet(QString(\"QPlainTextEdit { background-color: %1; color: %2; }\")\n                                          .arg(ConfigColor(\"gui.background\").name())\n                                          .arg(ConfigColor(\"btext\").name()));\n\n    // Read and set a stylesheet for the QToolTip too\n    setStyleSheet(DisassemblyPreview::getToolTipStyleSheet());\n}\n\nDisassemblyScrollArea::DisassemblyScrollArea(QWidget *parent) : QAbstractScrollArea(parent)\n{\n    vScrollBar = new AddressRangeScrollBar(this);\n    setVerticalScrollBar(vScrollBar);\n    accumScrollWheelDeltaY = 0;\n    vScrollBar->setPageStep(40);\n    vScrollBar->setSingleStep(1);\n    connect(vScrollBar, &AddressRangeScrollBar::scrolled, this,\n            [this](int lines) { emit scrollLines(-lines, true); });\n    connect(vScrollBar, &AddressRangeScrollBar::hideScrollBar, this,\n            [this]() { setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); });\n    connect(vScrollBar, &AddressRangeScrollBar::showScrollBar, this,\n            [this]() { setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); });\n    vScrollBar->refreshRange();\n}\n\nAddressRangeScrollBar *DisassemblyScrollArea::verticalScrollBar()\n{\n    return vScrollBar;\n}\n\nbool DisassemblyScrollArea::viewportEvent(QEvent *event)\n{\n    if (event->type() == QEvent::Resize) {\n        emit disassemblyResized();\n    }\n\n    return QAbstractScrollArea::viewportEvent(event);\n}\n\nvoid DisassemblyScrollArea::wheelEvent(QWheelEvent *event)\n{\n    if (event->angleDelta().isNull() || !event->angleDelta().y()) {\n        QAbstractScrollArea::wheelEvent(event);\n        return;\n    }\n\n    accumScrollWheelDeltaY += event->angleDelta().y();\n    // Delta is reported in 1/8 of a degree\n    // eg. 120 units * 1/8 = 15 degrees\n    // Typical scroll speed is 1 line per 5 degrees\n    const int lineDelta = 5 * 8;\n    if (accumScrollWheelDeltaY >= lineDelta || accumScrollWheelDeltaY <= -lineDelta) {\n        int lineCount = accumScrollWheelDeltaY / lineDelta;\n        accumScrollWheelDeltaY -= lineDelta * lineCount;\n        emit scrollLines(-lineCount);\n    }\n    emit wheelEventTriggered();\n}\n\nqreal DisassemblyTextEdit::textOffset() const\n{\n    return (blockBoundingGeometry(document()->begin()).topLeft() + contentOffset()).y();\n}\n\nbool DisassemblyTextEdit::viewportEvent(QEvent *event)\n{\n    switch (event->type()) {\n    case QEvent::Type::Wheel:\n        return false;\n    default:\n        return QAbstractScrollArea::viewportEvent(event);\n    }\n}\n\nvoid DisassemblyTextEdit::scrollContentsBy(int dx, int dy)\n{\n    if (!lockScroll) {\n        QPlainTextEdit::scrollContentsBy(dx, dy);\n    }\n}\n\nvoid DisassemblyTextEdit::keyPressEvent(QKeyEvent *event)\n{\n    Q_UNUSED(event)\n    // QPlainTextEdit::keyPressEvent(event);\n}\n\nvoid DisassemblyTextEdit::mousePressEvent(QMouseEvent *event)\n{\n    QPlainTextEdit::mousePressEvent(event);\n\n    if (event->button() == Qt::RightButton && !textCursor().hasSelection()) {\n        setTextCursor(cursorForPosition(event->pos()));\n    }\n}\n\nvoid DisassemblyWidget::seekPrev()\n{\n    Core()->seekPrev();\n}\n\n/*********************\n * Left panel\n *********************/\n\nDisassemblyLeftPanel::DisassemblyLeftPanel(DisassemblyWidget *disas)\n{\n    this->disas = disas;\n    arrows.reserve((arrowsSize * 3) / 2);\n}\n\nvoid DisassemblyLeftPanel::wheelEvent(QWheelEvent *event)\n{\n\n    int count = -(event->angleDelta() / 15).y();\n    count -= (count > 0 ? 5 : -5);\n\n    this->disas->showTransientScrollBar();\n    this->disas->scrollInstructions(count);\n}\n\nvoid DisassemblyLeftPanel::paintEvent(QPaintEvent *event)\n{\n    Q_UNUSED(event)\n\n    constexpr int penSizePix = 1;\n    constexpr int distanceBetweenLines = 10;\n    constexpr int arrowWidth = 5;\n    int rightOffset = size().rwidth();\n    auto tEdit = qobject_cast<DisassemblyTextEdit *>(disas->getTextWidget());\n    int lineHeight = qCeil(disas->getFontMetrics().lineSpacing());\n    QColor arrowColorDown = ConfigColor(\"flow\");\n    QColor arrowColorUp = ConfigColor(\"cflow\");\n    QPainter p(this);\n    QPen penDown(arrowColorDown, penSizePix, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);\n    QPen penUp(arrowColorUp, penSizePix, Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);\n    // Fill background\n    p.fillRect(event->rect(), Config()->getColor(\"gui.background\").darker(115));\n\n    QList<DisassemblyLine> lines = disas->getLines();\n    if (lines.size() == 0) {\n        // No line to print, abort early\n        return;\n    }\n\n    std::vector<int> lineY(tEdit->document()->lineCount());\n    QTextCursor cursor(tEdit->document());\n    for (auto &line : lineY) {\n        auto rect = tEdit->cursorRect(cursor);\n        line = rect.top();\n        cursor.movePosition(QTextCursor::Down);\n    }\n\n    using LineInfo = std::pair<RVA, int>;\n    std::vector<LineInfo> lineOffsets;\n    lineOffsets.reserve(lines.size() + arrows.size());\n\n    RVA minViewOffset = 0, maxViewOffset = 0;\n    minViewOffset = maxViewOffset = lines[0].offset;\n\n    for (int i = 0; i < lines.size(); i++) {\n        lineOffsets.emplace_back(lines[i].offset, i);\n        minViewOffset = std::min(minViewOffset, lines[i].offset);\n        maxViewOffset = std::max(maxViewOffset, lines[i].offset);\n        if (lines[i].arrow != RVA_INVALID) {\n            Arrow a { lines[i].offset, lines[i].arrow };\n            bool contains = std::find_if(std::begin(arrows), std::end(arrows),\n                                         [&](const Arrow &it) {\n                                             return it.min == a.min && it.max == a.max;\n                                         })\n                    != std::end(arrows);\n            if (!contains) {\n                arrows.emplace_back(lines[i].offset, lines[i].arrow);\n            }\n        }\n    }\n\n    auto addOffsetOutsideScreen = [&](RVA offset) {\n        if (offset < minViewOffset || offset > maxViewOffset) {\n            lineOffsets.emplace_back(offset, -1);\n        }\n    };\n\n    // Assign sequential numbers to offsets outside screen while preserving their relative order.\n    // Preserving relative order helps reducing reordering while scrolling. Using sequential numbers\n    // allows using data structures designed for dense ranges.\n    for (auto &arrow : arrows) {\n        addOffsetOutsideScreen(arrow.min);\n        addOffsetOutsideScreen(arrow.max);\n    }\n    std::sort(lineOffsets.begin(), lineOffsets.end());\n    lineOffsets.erase(std::unique(lineOffsets.begin(), lineOffsets.end()), lineOffsets.end());\n    size_t firstVisibleLine = std::find_if(lineOffsets.begin(), lineOffsets.end(),\n                                           [](const LineInfo &line) { return line.second == 0; })\n            - lineOffsets.begin();\n    for (int i = int(firstVisibleLine) - 1; i >= 0; i--) {\n        // -1 to ensure end of arrrow is drawn outside screen\n        lineOffsets[i].second = i - firstVisibleLine - 1;\n    }\n    size_t firstLineAfter =\n            std::find_if(lineOffsets.begin(), lineOffsets.end(),\n                         [&](const LineInfo &line) { return line.first > maxViewOffset; })\n            - lineOffsets.begin();\n    for (size_t i = firstLineAfter; i < lineOffsets.size(); i++) {\n        lineOffsets[i].second = lines.size() + (i - firstLineAfter)\n                + 1; // +1 to ensure end of arrrow is drawn outside screen\n    }\n\n    auto offsetToLine = [&](RVA offset) -> int {\n        // binary search because linesPixPosition is sorted by offset\n        if (lineOffsets.empty()) {\n            return 0;\n        }\n        if (offset < lineOffsets[0].first) {\n            return lineOffsets[0].second - 1;\n        }\n        auto res = lower_bound(std::begin(lineOffsets), std::end(lineOffsets), offset,\n                               [](const LineInfo &it, RVA offset) { return it.first < offset; });\n        if (res == std::end(lineOffsets)) {\n            return lineOffsets.back().second + 1;\n        }\n        return res->second;\n    };\n\n    auto fitsInScreen = [&](const Arrow &a) { return maxViewOffset - minViewOffset < a.length(); };\n\n    std::sort(std::begin(arrows), std::end(arrows), [&](const Arrow &l, const Arrow &r) {\n        int lScreen = fitsInScreen(l), rScreen = fitsInScreen(r);\n        if (lScreen != rScreen) {\n            return lScreen < rScreen;\n        }\n        return l.max != r.max ? l.max < r.max : l.min > r.min;\n    });\n\n    int minLine = 0, maxLine = 0;\n    for (auto &it : arrows) {\n        minLine = std::min(offsetToLine(it.min), minLine);\n        maxLine = std::max(offsetToLine(it.max), maxLine);\n        it.level = 0;\n    }\n\n    const int MAX_ARROW_LINES = 1 << 18;\n    uint32_t maxLevel = 0;\n    if (!arrows.empty() && maxLine - minLine < MAX_ARROW_LINES) {\n        // Limit maximum tree range to MAX_ARROW_LINES as sanity check, since the tree is designed\n        // for dense ranges. Under normal conditions due to amount lines fitting screen and number\n        // of arrows remembered should be few hundreds at most.\n        MinMaxAccumulateTree<uint32_t> maxLevelTree(maxLine - minLine + 2);\n        for (Arrow &arrow : arrows) {\n            int top = offsetToLine(arrow.min) - minLine;\n            int bottom = offsetToLine(arrow.max) - minLine + 1;\n            auto minMax = maxLevelTree.rangeMinMax(top, bottom);\n            if (minMax.first > 1) {\n                arrow.level = 1; // place below existing lines\n            } else {\n                arrow.level = minMax.second + 1; // place on top of existing lines\n                maxLevel = std::max(maxLevel, arrow.level);\n            }\n            maxLevelTree.updateRange(top, bottom, arrow.level);\n        }\n    }\n\n    const RVA currOffset = disas->getSeekable()->getOffset();\n    const qreal pixelRatio = qhelpers::devicePixelRatio(p.device());\n    const Arrow visibleRange { lines.first().offset, lines.last().offset };\n    // Draw the lines\n    for (const auto &arrow : arrows) {\n        if (!visibleRange.intersects(arrow)) {\n            continue;\n        }\n        int lineOffset =\n                int((distanceBetweenLines * arrow.level + distanceBetweenLines) * pixelRatio);\n\n        p.setPen(arrow.up ? penUp : penDown);\n        if (arrow.min == currOffset || arrow.max == currOffset) {\n            QPen pen = p.pen();\n            pen.setWidthF((penSizePix * 3) / 2.0);\n            p.setPen(pen);\n        }\n\n        auto lineToPixels = [&](int i) {\n            int offset = int(arrow.up ? std::floor(pixelRatio) : -std::floor(pixelRatio));\n            int clampedLine = std::max(0, std::min(i, ((int)lineY.size()) - 1));\n            int pos0 = 0;\n            if (lineY.size() > 0) {\n                pos0 = lineY[clampedLine];\n            }\n            return pos0 + (i - clampedLine) * lineHeight + lineHeight / 2 + offset;\n        };\n\n        int lineStartNumber = offsetToLine(arrow.jmpFromOffset());\n        int currentLineYPos = lineToPixels(lineStartNumber);\n\n        int arrowLineNumber = offsetToLine(arrow.jmpToffset());\n        int lineArrowY = lineToPixels(arrowLineNumber);\n\n        if (lineStartNumber == arrowLineNumber) {\n            currentLineYPos += lineHeight / 4;\n            lineArrowY -= lineHeight / 4;\n        }\n\n        // Draw the lines\n        p.drawLine(rightOffset, currentLineYPos, rightOffset - lineOffset, currentLineYPos); // left\n        p.drawLine(rightOffset - lineOffset, currentLineYPos, rightOffset - lineOffset,\n                   lineArrowY); // vertical\n\n        p.drawLine(rightOffset - lineOffset, lineArrowY, rightOffset, lineArrowY); // right\n\n        { // triangle\n            QPainterPath arrow;\n            arrow.moveTo(rightOffset - arrowWidth, lineArrowY + arrowWidth);\n            arrow.lineTo(rightOffset - arrowWidth, lineArrowY - arrowWidth);\n            arrow.lineTo(rightOffset, lineArrowY);\n            p.fillPath(arrow, p.pen().brush());\n        }\n    }\n\n    if (maxLevel > maxLevelBeforeFlush) {\n        arrows.clear();\n    }\n\n    const size_t eraseN = arrows.size() > arrowsSize ? arrows.size() - arrowsSize : 0;\n    if (eraseN > 0) {\n        const bool scrolledDown = lastBeginOffset > lines.first().offset;\n        std::sort(std::begin(arrows), std::end(arrows), [&](const Arrow &l, const Arrow &r) {\n            if (scrolledDown) {\n                return l.jmpFromOffset() < r.jmpFromOffset();\n            } else {\n                return l.jmpFromOffset() > r.jmpFromOffset();\n            }\n        });\n        arrows.erase(std::end(arrows) - eraseN, std::end(arrows));\n    }\n\n    lastBeginOffset = lines.first().offset;\n}\n\nvoid DisassemblyLeftPanel::clearArrowFrom(RVA offset)\n{\n    auto it = std::find_if(arrows.begin(), arrows.end(),\n                           [&](const Arrow &it) { return it.jmpFromOffset() == offset; });\n    if (it != arrows.end()) {\n        arrows.erase(it);\n    }\n}\n"
  },
  {
    "path": "src/widgets/DisassemblyWidget.h",
    "content": "#ifndef DISASSEMBLYWIDGET_H\n#define DISASSEMBLYWIDGET_H\n\n#include \"core/Cutter.h\"\n#include \"MemoryDockWidget.h\"\n#include \"common/CutterSeekable.h\"\n#include \"common/RefreshDeferrer.h\"\n#include \"common/CachedFontMetrics.h\"\n\n#include <QTextEdit>\n#include <QPlainTextEdit>\n#include <QShortcut>\n#include <QAction>\n\n#include <vector>\n\nclass DisassemblyTextEdit;\nclass DisassemblyScrollArea;\nclass DisassemblyContextMenu;\nclass DisassemblyLeftPanel;\nclass AddressRangeScrollBar;\n\nclass DisassemblyWidget : public MemoryDockWidget\n{\n    Q_OBJECT\npublic:\n    explicit DisassemblyWidget(MainWindow *main);\n    QWidget *getTextWidget();\n\n    static QString getWidgetType();\n\npublic slots:\n    /**\n     * @brief Highlights the currently selected line and updates the\n     * highlighting of the same words under the cursor in the visible screen.\n     * This overrides all previous highlighting.\n     */\n    void highlightCurrentLine();\n    /**\n     * @brief Adds the PC line highlighting to the other current highlighting.\n     * This should be called after highlightCurrentLine since that function\n     * overrides all previous highlighting.\n     */\n    void highlightPCLine();\n    void showDisasContextMenu(const QPoint &pt);\n    void fontsUpdatedSlot();\n    void colorsUpdatedSlot();\n    void scrollInstructions(int count, bool clampToScrollBarRange = false);\n    void seekPrev();\n    void setPreviewMode(bool previewMode);\n    QFontMetricsF getFontMetrics();\n    QList<DisassemblyLine> getLines();\n\n    /**\n     * @brief Forces the transient vertical scrollbar to appear on scroll\n     */\n    void showTransientScrollBar();\nprotected slots:\n    void on_seekChanged(RVA offset, CutterCore::SeekHistoryType type);\n    void refreshIfInRange(RVA offset);\n    void instructionChanged(RVA offset);\n    void refreshDisasm(RVA offset = RVA_INVALID);\n\n    bool updateMaxLines();\n\n    void cursorPositionChanged();\n\nprotected:\n    DisassemblyContextMenu *mCtxMenu;\n    DisassemblyScrollArea *mDisasScrollArea;\n    DisassemblyTextEdit *mDisasTextEdit;\n    DisassemblyLeftPanel *leftPanel;\n    QList<DisassemblyLine> lines;\n\nprivate:\n    RVA topOffset;\n    RVA bottomOffset;\n    int maxLines;\n\n    QString curHighlightedWord;\n\n    /**\n     * offset of lines below the first line of the current seek\n     */\n    int cursorLineOffset;\n    int cursorCharOffset;\n    bool seekFromCursor;\n\n    RefreshDeferrer *disasmRefresh;\n\n    RVA readCurrentDisassemblyOffset();\n    bool eventFilter(QObject *obj, QEvent *event) override;\n    void keyPressEvent(QKeyEvent *event) override;\n    void contextMenuEvent(QContextMenuEvent *event) override;\n    QString getWindowTitle() const override;\n\n    int topOffsetHistoryPos = 0;\n    QList<RVA> topOffsetHistory;\n\n    QList<RVA> breakpoints;\n\n    void setupFonts();\n    void setupColors();\n\n    void updateCursorPosition();\n\n    void connectCursorPositionChanged(bool disconnect);\n\n    void moveCursorRelative(bool up, bool page);\n\n    void jumpToOffsetUnderCursor(const QTextCursor &);\n};\n\nclass DisassemblyScrollArea : public QAbstractScrollArea\n{\n    Q_OBJECT\n\npublic:\n    explicit DisassemblyScrollArea(QWidget *parent = nullptr);\n    AddressRangeScrollBar *verticalScrollBar();\n\nsignals:\n    void scrollLines(int lines, bool clampToScrollBarRange = false);\n    void disassemblyResized();\n    void wheelEventTriggered();\n\nprotected:\n    bool viewportEvent(QEvent *event) override;\n    void wheelEvent(QWheelEvent *event) override;\n\nprivate:\n    AddressRangeScrollBar *vScrollBar;\n    int accumScrollWheelDeltaY;\n};\n\nclass DisassemblyTextEdit : public QPlainTextEdit\n{\n    Q_OBJECT\n\npublic:\n    explicit DisassemblyTextEdit(QWidget *parent = nullptr)\n        : QPlainTextEdit(parent), lockScroll(false)\n    {\n    }\n\n    void setLockScroll(bool lock) { this->lockScroll = lock; }\n\n    qreal textOffset() const;\n\nprotected:\n    bool viewportEvent(QEvent *event) override;\n    void scrollContentsBy(int dx, int dy) override;\n    void keyPressEvent(QKeyEvent *event) override;\n    void mousePressEvent(QMouseEvent *event) override;\n\nprivate:\n    bool lockScroll;\n};\n\n/**\n * This class is used to draw the left pane of the disassembly\n * widget. Its goal is to draw proper arrows for the jumps of the disassembly.\n */\nclass DisassemblyLeftPanel : public QFrame\n{\npublic:\n    DisassemblyLeftPanel(DisassemblyWidget *disas);\n    void paintEvent(QPaintEvent *event) override;\n    void wheelEvent(QWheelEvent *event) override;\n    void clearArrowFrom(RVA offset);\n\nprivate:\n    DisassemblyWidget *disas;\n\n    struct Arrow\n    {\n        Arrow(RVA v1, RVA v2) : min(v1), max(v2), level(0), up(false)\n        {\n            if (min > max) {\n                std::swap(min, max);\n                up = true;\n            }\n        }\n\n        inline bool contains(RVA point) const { return min <= point && max >= point; }\n\n        inline bool intersects(const Arrow &other) const\n        {\n            return std::max(min, other.min) <= std::min(max, other.max);\n        }\n\n        ut64 length() const { return max - min; }\n\n        RVA jmpFromOffset() const { return up ? max : min; }\n\n        RVA jmpToffset() const { return up ? min : max; }\n\n        RVA min;\n        RVA max;\n        uint32_t level;\n        bool up;\n    };\n\n    const size_t arrowsSize = 128;\n    const uint32_t maxLevelBeforeFlush = 32;\n    RVA lastBeginOffset = 0;\n    std::vector<Arrow> arrows;\n};\n\n#endif // DISASSEMBLYWIDGET_H\n"
  },
  {
    "path": "src/widgets/EntrypointWidget.cpp",
    "content": "#include \"EntrypointWidget.h\"\n#include \"ui_EntrypointWidget.h\"\n\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n\n#include <QTreeWidget>\n#include <QPen>\n\n/*\n * Entrypoint Widget\n */\n\nEntrypointWidget::EntrypointWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::EntrypointWidget)\n{\n    ui->setupUi(this);\n\n    setScrollMode();\n\n    connect(Core(), &CutterCore::codeRebased, this, &EntrypointWidget::fillEntrypoint);\n    connect(Core(), &CutterCore::refreshAll, this, &EntrypointWidget::fillEntrypoint);\n}\n\nEntrypointWidget::~EntrypointWidget() {}\n\nvoid EntrypointWidget::fillEntrypoint()\n{\n    ui->entrypointTreeWidget->clear();\n    for (const EntrypointDescription &i : Core()->getAllEntrypoint()) {\n        QTreeWidgetItem *item = new QTreeWidgetItem();\n        item->setText(0, RzAddressString(i.vaddr));\n        item->setText(1, i.type);\n        item->setData(0, Qt::UserRole, QVariant::fromValue(i));\n        ui->entrypointTreeWidget->addTopLevelItem(item);\n    }\n\n    qhelpers::adjustColumns(ui->entrypointTreeWidget, 0, 10);\n}\n\nvoid EntrypointWidget::setScrollMode()\n{\n    qhelpers::setVerticalScrollMode(ui->entrypointTreeWidget);\n}\n\nvoid EntrypointWidget::on_entrypointTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)\n{\n    if (column < 0)\n        return;\n\n    EntrypointDescription ep = item->data(0, Qt::UserRole).value<EntrypointDescription>();\n    Core()->seekAndShow(ep.vaddr);\n}\n"
  },
  {
    "path": "src/widgets/EntrypointWidget.h",
    "content": "#ifndef ENTRYPOINTWIDGET_H\n#define ENTRYPOINTWIDGET_H\n\n#include <memory>\n#include <QStyledItemDelegate>\n#include <QTreeWidgetItem>\n\n#include \"CutterDockWidget.h\"\n\nclass MainWindow;\nclass QTreeWidget;\n\nnamespace Ui {\nclass EntrypointWidget;\n}\n\nclass EntrypointWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit EntrypointWidget(MainWindow *main);\n    ~EntrypointWidget();\n\nprivate slots:\n    void on_entrypointTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);\n\n    void fillEntrypoint();\n\nprivate:\n    std::unique_ptr<Ui::EntrypointWidget> ui;\n\n    void setScrollMode();\n};\n\n#endif // ENTRYPOINTWIDGET_H\n"
  },
  {
    "path": "src/widgets/EntrypointWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>EntrypointWidget</class>\n <widget class=\"QDockWidget\" name=\"EntrypointWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Entry Points</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QTreeWidget\" name=\"entrypointTreeWidget\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">QTreeWidget::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n      <property name=\"columnCount\">\n       <number>2</number>\n      </property>\n      <column>\n       <property name=\"text\">\n        <string>Address</string>\n       </property>\n      </column>\n      <column>\n       <property name=\"text\">\n        <string>Type</string>\n       </property>\n      </column>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/ExportsWidget.cpp",
    "content": "#include \"ExportsWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QShortcut>\n\nExportsModel::ExportsModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint ExportsModel::rowCount(const QModelIndex &) const\n{\n    return exports.count();\n}\n\nint ExportsModel::columnCount(const QModelIndex &) const\n{\n    return ExportsModel::ColumnCount;\n}\n\nQVariant ExportsModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= exports.count())\n        return QVariant();\n\n    const ExportDescription &exp = exports.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case ExportsModel::OffsetColumn:\n            return RzAddressString(exp.vaddr);\n        case ExportsModel::SizeColumn:\n            return RzSizeString(exp.size);\n        case ExportsModel::TypeColumn:\n            return exp.type;\n        case ExportsModel::NameColumn:\n            return exp.name;\n        case ExportsModel::CommentColumn:\n            return Core()->getCommentAt(exp.vaddr);\n        default:\n            return QVariant();\n        }\n    case ExportsModel::ExportDescriptionRole:\n        return QVariant::fromValue(exp);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant ExportsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case ExportsModel::OffsetColumn:\n            return tr(\"Address\");\n        case ExportsModel::SizeColumn:\n            return tr(\"Size\");\n        case ExportsModel::TypeColumn:\n            return tr(\"Type\");\n        case ExportsModel::NameColumn:\n            return tr(\"Name\");\n        case ExportsModel::CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA ExportsModel::address(const QModelIndex &index) const\n{\n    const ExportDescription &exp = exports.at(index.row());\n    return exp.vaddr;\n}\n\nQString ExportsModel::name(const QModelIndex &index) const\n{\n    const ExportDescription &exp = exports.at(index.row());\n    return exp.name;\n}\n\nExportsProxyModel::ExportsProxyModel(ExportsModel *source_model, QObject *parent)\n    : AddressableFilterProxyModel(source_model, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool ExportsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    auto exp = index.data(ExportsModel::ExportDescriptionRole).value<ExportDescription>();\n\n    return qhelpers::filterStringContains(exp.name, this);\n}\n\nbool ExportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    auto leftExp = left.data(ExportsModel::ExportDescriptionRole).value<ExportDescription>();\n    auto rightExp = right.data(ExportsModel::ExportDescriptionRole).value<ExportDescription>();\n\n    switch (left.column()) {\n    case ExportsModel::SizeColumn:\n        if (leftExp.size != rightExp.size)\n            return leftExp.size < rightExp.size;\n    // fallthrough\n    case ExportsModel::OffsetColumn:\n        if (leftExp.vaddr != rightExp.vaddr)\n            return leftExp.vaddr < rightExp.vaddr;\n    // fallthrough\n    case ExportsModel::NameColumn:\n        if (leftExp.name != rightExp.name)\n            return leftExp.name < rightExp.name;\n    // fallthrough\n    case ExportsModel::TypeColumn:\n        if (leftExp.type != rightExp.type)\n            return leftExp.type < rightExp.type;\n    // fallthrough\n    case ExportsModel::CommentColumn:\n        return Core()->getCommentAt(leftExp.vaddr) < Core()->getCommentAt(rightExp.vaddr);\n    default:\n        break;\n    }\n\n    // fallback\n    return leftExp.vaddr < rightExp.vaddr;\n}\n\nExportsWidget::ExportsWidget(MainWindow *main) : ListDockWidget(main)\n{\n    setWindowTitle(tr(\"Exports\"));\n    setObjectName(\"ExportsWidget\");\n\n    exportsModel = new ExportsModel(this);\n    exportsProxyModel = new ExportsProxyModel(exportsModel, this);\n    setModels(exportsProxyModel);\n\n    ui->treeView->sortByColumn(ExportsModel::OffsetColumn, Qt::AscendingOrder);\n    connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() {\n        AddressableItemContextMenu *contextMenu = ui->treeView->getItemContextMenu();\n        QModelIndex index = ui->treeView->selectionModel()->currentIndex();\n        if (index.isValid()) {\n            QVariant variant = exportsProxyModel->data(index, ExportsModel::ExportDescriptionRole);\n            if (variant.canConvert<ExportDescription>()) {\n                auto exp = variant.value<ExportDescription>();\n                contextMenu->toggleBreakpointAction(exp.type == \"FUNC\");\n                return;\n            }\n        }\n        contextMenu->toggleBreakpointAction(false);\n    });\n\n    QShortcut *toggle_shortcut = Shortcuts()->makeQShortcut(\"Exports.toggle\", main);\n    connect(toggle_shortcut, &QShortcut::activated, this, [=]() { toggleDockWidget(true); });\n\n    connect(Core(), &CutterCore::codeRebased, this, &ExportsWidget::refreshExports);\n    connect(Core(), &CutterCore::refreshAll, this, &ExportsWidget::refreshExports);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(exportsModel, ExportsModel::CommentColumn); });\n}\n\nExportsWidget::~ExportsWidget() {}\n\nvoid ExportsWidget::refreshExports()\n{\n    exportsModel->beginResetModel();\n    exportsModel->exports = Core()->getAllExports();\n    exportsModel->endResetModel();\n\n    qhelpers::adjustColumns(ui->treeView, 3, 0);\n}\n"
  },
  {
    "path": "src/widgets/ExportsWidget.h",
    "content": "#ifndef EXPORTSWIDGET_H\n#define EXPORTSWIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"widgets/ListDockWidget.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidget;\nclass ExportsWidget;\n\nnamespace Ui {\nclass ExportsWidget;\n}\n\nclass ExportsModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend ExportsWidget;\n\nprivate:\n    QList<ExportDescription> exports;\n\npublic:\n    enum Column {\n        OffsetColumn = 0,\n        SizeColumn,\n        TypeColumn,\n        NameColumn,\n        CommentColumn,\n        ColumnCount\n    };\n    enum Role { ExportDescriptionRole = Qt::UserRole };\n\n    ExportsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n};\n\nclass ExportsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    ExportsProxyModel(ExportsModel *source_model, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass ExportsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit ExportsWidget(MainWindow *main);\n    ~ExportsWidget();\n\nprivate slots:\n    void refreshExports();\n\nprivate:\n    ExportsModel *exportsModel;\n    ExportsProxyModel *exportsProxyModel;\n};\n\n#endif // EXPORTSWIDGET_H\n"
  },
  {
    "path": "src/widgets/FlagsWidget.cpp",
    "content": "#include \"FlagsWidget.h\"\n#include \"ui_FlagsWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QComboBox>\n#include <QMenu>\n#include <QShortcut>\n#include <QTreeWidget>\n#include <QStandardItemModel>\n#include <QInputDialog>\n\nFlagsModel::FlagsModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint FlagsModel::rowCount(const QModelIndex &) const\n{\n    return flags.count();\n}\n\nint FlagsModel::columnCount(const QModelIndex &) const\n{\n    return Columns::COUNT;\n}\n\nQVariant FlagsModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= flags.count())\n        return QVariant();\n\n    const FlagDescription &flag = flags.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case SIZE:\n            return RzSizeString(flag.size);\n        case OFFSET:\n            return RzAddressString(flag.offset);\n        case NAME:\n            return flag.name;\n        case REALNAME:\n            return flag.realname;\n        case COMMENT:\n            return Core()->getCommentAt(flag.offset);\n        default:\n            return QVariant();\n        }\n    case FlagDescriptionRole:\n        return QVariant::fromValue(flag);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant FlagsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case SIZE:\n            return tr(\"Size\");\n        case OFFSET:\n            return tr(\"Offset\");\n        case NAME:\n            return tr(\"Name\");\n        case REALNAME:\n            return tr(\"Real Name\");\n        case COMMENT:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA FlagsModel::address(const QModelIndex &index) const\n{\n    const FlagDescription &flag = flags.at(index.row());\n    return flag.offset;\n}\n\nQString FlagsModel::name(const QModelIndex &index) const\n{\n    const FlagDescription &flag = flags.at(index.row());\n    return flag.name;\n}\n\nconst FlagDescription *FlagsModel::description(QModelIndex index) const\n{\n    if (index.row() < flags.size()) {\n        return &flags.at(index.row());\n    }\n    return nullptr;\n}\n\nFlagsSortFilterProxyModel::FlagsSortFilterProxyModel(FlagsModel *source_model, QObject *parent)\n    : AddressableFilterProxyModel(source_model, parent)\n{\n}\n\nbool FlagsSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    FlagDescription flag = index.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>();\n    return qhelpers::filterStringContains(flag.name, this);\n}\n\nbool FlagsSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    auto source = static_cast<FlagsModel *>(sourceModel());\n    auto left_flag = source->description(left);\n    auto right_flag = source->description(right);\n\n    switch (left.column()) {\n    case FlagsModel::SIZE:\n        if (left_flag->size != right_flag->size)\n            return left_flag->size < right_flag->size;\n    // fallthrough\n    case FlagsModel::OFFSET:\n        if (left_flag->offset != right_flag->offset)\n            return left_flag->offset < right_flag->offset;\n    // fallthrough\n    case FlagsModel::NAME:\n        return left_flag->name < right_flag->name;\n\n    case FlagsModel::REALNAME:\n        return left_flag->realname < right_flag->realname;\n\n    case FlagsModel::COMMENT:\n        return Core()->getCommentAt(left_flag->offset) < Core()->getCommentAt(right_flag->offset);\n\n    default:\n        break;\n    }\n\n    // fallback\n    return left_flag->offset < right_flag->offset;\n}\n\nFlagsWidget::FlagsWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::FlagsWidget), main(main), tree(new CutterTreeWidget(this))\n{\n    ui->setupUi(this);\n\n    // Add Status Bar footer\n    tree->addStatusBar(ui->verticalLayout);\n\n    flags_model = new FlagsModel(this);\n    flags_proxy_model = new FlagsSortFilterProxyModel(flags_model, this);\n    connect(ui->filterLineEdit, &QLineEdit::textChanged, flags_proxy_model,\n            &QSortFilterProxyModel::setFilterWildcard);\n    ui->flagsTreeView->setMainWindow(mainWindow);\n    ui->flagsTreeView->setModel(flags_proxy_model);\n    ui->flagsTreeView->sortByColumn(FlagsModel::OFFSET, Qt::AscendingOrder);\n\n    // Ctrl-F to move the focus to the Filter search box\n    QShortcut *searchShortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(searchShortcut, &QShortcut::activated, ui->filterLineEdit,\n            [this]() { ui->filterLineEdit->setFocus(); });\n    searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    // Esc to clear the filter entry\n    QShortcut *clearShortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n    connect(clearShortcut, &QShortcut::activated, [this] {\n        if (ui->filterLineEdit->text().isEmpty()) {\n            ui->flagsTreeView->setFocus();\n        } else {\n            ui->filterLineEdit->setText(\"\");\n        }\n    });\n    clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    connect(ui->filterLineEdit, &QLineEdit::textChanged, this,\n            [this] { tree->showItemsNumber(flags_proxy_model->rowCount()); });\n\n    setScrollMode();\n\n    connect(Core(), &CutterCore::flagsChanged, this, &FlagsWidget::flagsChanged);\n    connect(Core(), &CutterCore::codeRebased, this, &FlagsWidget::flagsChanged);\n    connect(Core(), &CutterCore::refreshAll, this, &FlagsWidget::refreshFlagspaces);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(flags_model, FlagsModel::COMMENT); });\n\n    auto menu = ui->flagsTreeView->getItemContextMenu();\n    menu->addSeparator();\n    menu->addAction(ui->actionRename);\n    menu->addAction(ui->actionDelete);\n    addAction(ui->actionRename);\n    addAction(ui->actionDelete);\n}\n\nFlagsWidget::~FlagsWidget() {}\n\nvoid FlagsWidget::on_flagspaceCombo_currentTextChanged(const QString &arg1)\n{\n    Q_UNUSED(arg1);\n\n    refreshFlags();\n}\n\nvoid FlagsWidget::on_actionRename_triggered()\n{\n    FlagDescription flag = ui->flagsTreeView->selectionModel()\n                                   ->currentIndex()\n                                   .data(FlagsModel::FlagDescriptionRole)\n                                   .value<FlagDescription>();\n\n    bool ok;\n    QString newName = QInputDialog::getText(this, tr(\"Rename flag %1\").arg(flag.name),\n                                            tr(\"Flag name:\"), QLineEdit::Normal, flag.name, &ok);\n    if (ok && !newName.isEmpty()) {\n        Core()->renameFlag(flag.name, newName);\n    }\n}\n\nvoid FlagsWidget::on_actionDelete_triggered()\n{\n    FlagDescription flag = ui->flagsTreeView->selectionModel()\n                                   ->currentIndex()\n                                   .data(FlagsModel::FlagDescriptionRole)\n                                   .value<FlagDescription>();\n    Core()->delFlag(flag.name);\n}\n\nvoid FlagsWidget::flagsChanged()\n{\n    refreshFlagspaces();\n}\n\nvoid FlagsWidget::refreshFlagspaces()\n{\n    int cur_idx = ui->flagspaceCombo->currentIndex();\n    if (cur_idx < 0)\n        cur_idx = 0;\n\n    disableFlagRefresh =\n            true; // prevent duplicate flag refresh caused by flagspaceCombo modifications\n    ui->flagspaceCombo->clear();\n    ui->flagspaceCombo->addItem(tr(\"(all)\"));\n\n    for (const FlagspaceDescription &i : Core()->getAllFlagspaces()) {\n        ui->flagspaceCombo->addItem(i.name, QVariant::fromValue(i));\n    }\n\n    if (cur_idx > 0)\n        ui->flagspaceCombo->setCurrentIndex(cur_idx);\n    disableFlagRefresh = false;\n\n    refreshFlags();\n}\n\nvoid FlagsWidget::refreshFlags()\n{\n    if (disableFlagRefresh) {\n        return;\n    }\n    QString flagspace;\n\n    QVariant flagspace_data = ui->flagspaceCombo->currentData();\n    if (flagspace_data.isValid())\n        flagspace = flagspace_data.value<FlagspaceDescription>().name;\n\n    flags_model->beginResetModel();\n    flags_model->flags = Core()->getAllFlags(flagspace);\n    flags_model->endResetModel();\n\n    tree->showItemsNumber(flags_proxy_model->rowCount());\n\n    // TODO: this is not a very good place for the following:\n    QStringList flagNames;\n    for (const FlagDescription &i : flags_model->flags)\n        flagNames.append(i.name);\n    main->refreshOmniBar(flagNames);\n}\n\nvoid FlagsWidget::setScrollMode()\n{\n    qhelpers::setVerticalScrollMode(ui->flagsTreeView);\n}\n"
  },
  {
    "path": "src/widgets/FlagsWidget.h",
    "content": "#ifndef FLAGSWIDGET_H\n#define FLAGSWIDGET_H\n\n#include <memory>\n\n#include <QAbstractItemModel>\n#include <QSortFilterProxyModel>\n#include <QStandardItemModel>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeWidget.h\"\n#include \"AddressableItemList.h\"\n#include \"AddressableItemModel.h\"\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass FlagsWidget;\n\nclass FlagsModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n    friend FlagsWidget;\n\nprivate:\n    QList<FlagDescription> flags;\n\npublic:\n    enum Columns { OFFSET = 0, SIZE, NAME, REALNAME, COMMENT, COUNT };\n    static const int FlagDescriptionRole = Qt::UserRole;\n\n    FlagsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n\n    const FlagDescription *description(QModelIndex index) const;\n};\n\nclass FlagsSortFilterProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    FlagsSortFilterProxyModel(FlagsModel *source_model, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nnamespace Ui {\nclass FlagsWidget;\n}\n\nclass FlagsWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit FlagsWidget(MainWindow *main);\n    ~FlagsWidget();\n\nprivate slots:\n    void on_flagspaceCombo_currentTextChanged(const QString &arg1);\n\n    void on_actionRename_triggered();\n    void on_actionDelete_triggered();\n\n    void flagsChanged();\n    void refreshFlagspaces();\n\nprivate:\n    std::unique_ptr<Ui::FlagsWidget> ui;\n    MainWindow *main;\n\n    bool disableFlagRefresh = false;\n    FlagsModel *flags_model;\n    FlagsSortFilterProxyModel *flags_proxy_model;\n    CutterTreeWidget *tree;\n\n    void refreshFlags();\n    void setScrollMode();\n};\n\n#endif // FLAGSWIDGET_H\n"
  },
  {
    "path": "src/widgets/FlagsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>FlagsWidget</class>\n <widget class=\"QDockWidget\" name=\"FlagsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>463</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Flags</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"AddressableItemList&lt;&gt;\" name=\"flagsTreeView\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"dragEnabled\">\n       <bool>true</bool>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n      <property name=\"animated\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_17\">\n      <property name=\"spacing\">\n       <number>10</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLineEdit\" name=\"filterLineEdit\">\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"placeholderText\">\n         <string>Quick Filter</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"flagspaceLabel\">\n        <property name=\"text\">\n         <string>Flagspace:</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QComboBox\" name=\"flagspaceCombo\"/>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n  <action name=\"actionRename\">\n   <property name=\"text\">\n    <string>Rename</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>N</string>\n   </property>\n   <property name=\"shortcutContext\">\n    <enum>Qt::WidgetWithChildrenShortcut</enum>\n   </property>\n  </action>\n  <action name=\"actionDelete\">\n   <property name=\"text\">\n    <string>Delete</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Del</string>\n   </property>\n   <property name=\"shortcutContext\">\n    <enum>Qt::WidgetWithChildrenShortcut</enum>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>AddressableItemList&lt;&gt;</class>\n   <extends>QTreeView</extends>\n   <header>widgets/AddressableItemList.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/FlirtWidget.cpp",
    "content": "#include \"FlirtWidget.h\"\n#include \"ui_FlirtWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n\nFlirtModel::FlirtModel(QObject *parent) : QAbstractListModel(parent) {}\n\nint FlirtModel::rowCount(const QModelIndex &) const\n{\n    return sigdb.count();\n}\n\nint FlirtModel::columnCount(const QModelIndex &) const\n{\n    return FlirtModel::ColumnCount;\n}\n\nQVariant FlirtModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= sigdb.count())\n        return QVariant();\n\n    const FlirtDescription &entry = sigdb.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case BinTypeColumn:\n            return entry.bin_name;\n        case ArchNameColumn:\n            return entry.arch_name;\n        case ArchBitsColumn:\n            return entry.arch_bits;\n        case NumModulesColumn:\n            return entry.n_modules;\n        case NameColumn:\n            return entry.base_name;\n        case DetailsColumn:\n            return entry.details;\n        default:\n            return QVariant();\n        }\n\n    case FlirtDescriptionRole:\n        return QVariant::fromValue(entry);\n\n    case Qt::ToolTipRole: {\n        return entry.short_path;\n    }\n\n    default:\n        return QVariant();\n    }\n}\n\nQVariant FlirtModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case BinTypeColumn:\n            return tr(\"Bin\");\n        case ArchNameColumn:\n            return tr(\"Arch\");\n        case ArchBitsColumn:\n            return tr(\"Bits\");\n        case NumModulesColumn:\n            return tr(\"# Funcs\");\n        case NameColumn:\n            return tr(\"Name\");\n        case DetailsColumn:\n            return tr(\"Details\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nFlirtProxyModel::FlirtProxyModel(FlirtModel *sourceModel, QObject *parent)\n    : QSortFilterProxyModel(parent)\n{\n    setSourceModel(sourceModel);\n}\n\nbool FlirtProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    FlirtDescription entry = index.data(FlirtModel::FlirtDescriptionRole).value<FlirtDescription>();\n    return qhelpers::filterStringContains(entry.base_name, this);\n}\n\nbool FlirtProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    FlirtDescription leftEntry =\n            left.data(FlirtModel::FlirtDescriptionRole).value<FlirtDescription>();\n    FlirtDescription rightEntry =\n            right.data(FlirtModel::FlirtDescriptionRole).value<FlirtDescription>();\n\n    switch (left.column()) {\n    case FlirtModel::BinTypeColumn:\n        return leftEntry.bin_name < rightEntry.bin_name;\n    case FlirtModel::ArchNameColumn:\n        return leftEntry.arch_name < rightEntry.arch_name;\n    case FlirtModel::ArchBitsColumn:\n        return leftEntry.arch_bits.toULongLong() < rightEntry.arch_bits.toULongLong();\n    case FlirtModel::NumModulesColumn:\n        return leftEntry.n_modules.toULongLong() < rightEntry.n_modules.toULongLong();\n    case FlirtModel::NameColumn:\n        return leftEntry.base_name < rightEntry.base_name;\n    case FlirtModel::DetailsColumn:\n        return leftEntry.details < rightEntry.details;\n    default:\n        break;\n    }\n\n    return leftEntry.bin_name < rightEntry.bin_name;\n}\n\nFlirtWidget::FlirtWidget(MainWindow *main)\n    : CutterDockWidget(main),\n      ui(new Ui::FlirtWidget),\n      blockMenu(new FlirtContextMenu(this, mainWindow))\n{\n    ui->setupUi(this);\n\n    model = new FlirtModel(this);\n    proxyModel = new FlirtProxyModel(model, this);\n    ui->flirtTreeView->setModel(proxyModel);\n    ui->flirtTreeView->sortByColumn(FlirtModel::BinTypeColumn, Qt::AscendingOrder);\n\n    setScrollMode();\n\n    this->connect(this, &QWidget::customContextMenuRequested, this,\n                  &FlirtWidget::showItemContextMenu);\n    this->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    this->connect(ui->flirtTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this,\n                  &FlirtWidget::onSelectedItemChanged);\n    connect(Core(), &CutterCore::refreshAll, this, &FlirtWidget::refreshFlirt);\n\n    this->addActions(this->blockMenu->actions());\n}\n\nFlirtWidget::~FlirtWidget() {}\n\nvoid FlirtWidget::refreshFlirt()\n{\n    model->beginResetModel();\n    model->sigdb = Core()->getSignaturesDB();\n    model->endResetModel();\n\n    ui->flirtTreeView->resizeColumnToContents(0);\n    ui->flirtTreeView->resizeColumnToContents(1);\n    ui->flirtTreeView->resizeColumnToContents(2);\n    ui->flirtTreeView->resizeColumnToContents(3);\n    ui->flirtTreeView->resizeColumnToContents(4);\n    ui->flirtTreeView->resizeColumnToContents(5);\n}\n\nvoid FlirtWidget::setScrollMode()\n{\n    qhelpers::setVerticalScrollMode(ui->flirtTreeView);\n}\n\nvoid FlirtWidget::onSelectedItemChanged(const QModelIndex &index)\n{\n    if (index.isValid()) {\n        const FlirtDescription &entry = model->sigdb.at(index.row());\n        blockMenu->setTarget(entry);\n    } else {\n        blockMenu->clearTarget();\n    }\n}\n\nvoid FlirtWidget::showItemContextMenu(const QPoint &pt)\n{\n    auto index = ui->flirtTreeView->currentIndex();\n    if (index.isValid()) {\n        const FlirtDescription &entry = model->sigdb.at(index.row());\n        blockMenu->setTarget(entry);\n        blockMenu->exec(this->mapToGlobal(pt));\n    }\n}\n"
  },
  {
    "path": "src/widgets/FlirtWidget.h",
    "content": "#ifndef FLIRT_WIDGET_H\n#define FLIRT_WIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"menus/FlirtContextMenu.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidget;\nclass QTreeWidgetItem;\nclass FlirtWidget;\n\nnamespace Ui {\nclass FlirtWidget;\n}\n\nclass FlirtModel : public QAbstractListModel\n{\n    Q_OBJECT\n\n    friend FlirtWidget;\n\npublic:\n    enum Column {\n        BinTypeColumn = 0,\n        ArchNameColumn,\n        ArchBitsColumn,\n        NumModulesColumn,\n        NameColumn,\n        DetailsColumn,\n        ColumnCount\n    };\n    enum Role { FlirtDescriptionRole = Qt::UserRole };\n\n    FlirtModel(QObject *parent = 0);\n\n    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;\n    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const;\n\nprivate:\n    QList<FlirtDescription> sigdb;\n};\n\nclass FlirtProxyModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    FlirtProxyModel(FlirtModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass FlirtWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit FlirtWidget(MainWindow *main);\n    ~FlirtWidget();\n\nprivate slots:\n    void refreshFlirt();\n    void onSelectedItemChanged(const QModelIndex &index);\n    void showItemContextMenu(const QPoint &pt);\n\nprivate:\n    std::unique_ptr<Ui::FlirtWidget> ui;\n\n    FlirtModel *model;\n    FlirtProxyModel *proxyModel;\n    FlirtContextMenu *blockMenu;\n\n    void setScrollMode();\n};\n\n#endif // FLIRT_WIDGET_H\n"
  },
  {
    "path": "src/widgets/FlirtWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>FlirtWidget</class>\n <widget class=\"QDockWidget\" name=\"FlirtWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Signatures</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"CutterTreeView\" name=\"flirtTreeView\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"horizontalScrollBarPolicy\">\n       <enum>Qt::ScrollBarAsNeeded</enum>\n      </property>\n      <property name=\"sizeAdjustPolicy\">\n       <enum>QAbstractScrollArea::AdjustToContents</enum>\n      </property>\n      <property name=\"autoScroll\">\n       <bool>true</bool>\n      </property>\n      <property name=\"horizontalScrollMode\">\n       <enum>QAbstractItemView::ScrollPerPixel</enum>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>CutterTreeView</class>\n   <extends>QTreeView</extends>\n   <header>widgets/CutterTreeView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/FunctionsWidget.cpp",
    "content": "#include \"FunctionsWidget.h\"\n#include \"ui_ListDockWidget.h\"\n\n#include \"core/MainWindow.h\"\n#include \"common/DisassemblyPreview.h\"\n#include \"common/Helpers.h\"\n#include \"common/FunctionsTask.h\"\n#include \"common/TempConfig.h\"\n#include \"menus/AddressableItemContextMenu.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <algorithm>\n#include <QMenu>\n#include <QDebug>\n#include <QString>\n#include <QResource>\n#include <QShortcut>\n#include <QJsonArray>\n#include <QJsonObject>\n#include <QInputDialog>\n#include <QActionGroup>\n#include <QBitmap>\n#include <QPainter>\n\nnamespace {\n\nstatic const int kMaxTooltipWidth = 400;\nstatic const int kMaxTooltipDisasmPreviewLines = 10;\nstatic const int kMaxTooltipHighlightsLines = 5;\n\n}\n\nFunctionModel::FunctionModel(bool nested, QFont default_font, QFont highlight_font, QObject *parent)\n    : AddressableItemModel<>(parent),\n      highlightFont(highlight_font),\n      defaultFont(default_font),\n      nested(nested),\n      currentIndex(-1),\n      iconFuncImpDark(\":/img/icons/function_import_dark.svg\"),\n      iconFuncImpLight(\":/img/icons/function_import_light.svg\"),\n      iconFuncMainDark(\":/img/icons/function_main_dark.svg\"),\n      iconFuncMainLight(\":/img/icons/function_main_light.svg\"),\n      iconFuncDark(\":/img/icons/function_dark.svg\"),\n      iconFuncLight(\":/img/icons/function_light.svg\")\n\n{\n    connect(Core(), &CutterCore::seekChanged, this, &FunctionModel::seekChanged);\n    connect(Core(), &CutterCore::functionRenamed, this, &FunctionModel::functionRenamed);\n}\n\nQModelIndex FunctionModel::index(int row, int column, const QModelIndex &parent) const\n{\n    if (!parent.isValid())\n        return createIndex(row, column, (quintptr)0); // root function nodes have id = 0\n\n    return createIndex(row, column,\n                       (quintptr)(parent.row() + 1)); // sub-nodes have id = function index + 1\n}\n\nQModelIndex FunctionModel::parent(const QModelIndex &index) const\n{\n    if (!index.isValid() || index.column() != 0)\n        return QModelIndex();\n\n    if (index.internalId() == 0) // root function node\n        return QModelIndex();\n    else // sub-node\n        return this->index((int)(index.internalId() - 1), 0);\n}\n\nint FunctionModel::rowCount(const QModelIndex &parent) const\n{\n    if (!parent.isValid())\n        return functions.count();\n\n    if (nested) {\n        if (parent.internalId() == 0)\n            return ColumnCount - 1; // sub-nodes for nested functions\n        return 0;\n    } else\n        return 0;\n}\n\nint FunctionModel::columnCount(const QModelIndex & /*parent*/) const\n{\n    if (nested)\n        return 1;\n    else\n        return ColumnCount;\n}\n\nbool FunctionModel::functionIsImport(ut64 addr) const\n{\n    return importAddresses.contains(addr);\n}\n\nbool FunctionModel::functionIsMain(ut64 addr) const\n{\n    return mainAdress == addr;\n}\n\nQVariant FunctionModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid())\n        return QVariant();\n\n    int function_index;\n    bool subnode;\n    bool is_dark;\n\n    if (index.internalId() != 0) { // sub-node\n        function_index = index.parent().row();\n        subnode = true;\n    } else { // root function node\n        function_index = index.row();\n        subnode = false;\n    }\n\n    const FunctionDescription &function = functions.at(function_index);\n\n    if (function_index >= functions.count())\n        return QVariant();\n\n    switch (role) {\n    case Qt::DisplayRole:\n        if (nested) {\n            if (subnode) {\n                switch (index.row()) {\n                case 0:\n                    return tr(\"Offset: %1\").arg(RzAddressString(function.offset));\n                case 1:\n                    return tr(\"Size: %1\").arg(RzSizeString(function.linearSize));\n                case 2:\n                    return tr(\"Import: %1\")\n                            .arg(functionIsImport(function.offset) ? tr(\"true\") : tr(\"false\"));\n                case 3:\n                    return tr(\"Nargs: %1\").arg(RzSizeString(function.nargs));\n                case 4:\n                    return tr(\"Nbbs: %1\").arg(RzSizeString(function.nbbs));\n                case 5:\n                    return tr(\"Nlocals: %1\").arg(RzSizeString(function.nlocals));\n                case 6:\n                    return tr(\"Call type: %1\").arg(function.calltype);\n                case 7:\n                    return tr(\"Edges: %1\").arg(function.edges);\n                case 8:\n                    return tr(\"StackFrame: %1\").arg(function.stackframe);\n                case 9:\n                    return tr(\"Comment: %1\").arg(Core()->getCommentAt(function.offset));\n                default:\n                    return QVariant();\n                }\n            } else\n                return function.name;\n        } else {\n            switch (index.column()) {\n            case NameColumn:\n                return function.name;\n            case SizeColumn:\n                return QString::number(function.linearSize);\n            case ImportColumn:\n                return functionIsImport(function.offset) ? tr(\"true\") : tr(\"false\");\n            case OffsetColumn:\n                return RzAddressString(function.offset);\n            case NargsColumn:\n                return QString::number(function.nargs);\n            case NlocalsColumn:\n                return QString::number(function.nlocals);\n            case NbbsColumn:\n                return QString::number(function.nbbs);\n            case CalltypeColumn:\n                return function.calltype;\n            case EdgesColumn:\n                return QString::number(function.edges);\n            case FrameColumn:\n                return QString::number(function.stackframe);\n            case CommentColumn:\n                return Core()->getCommentAt(function.offset);\n            default:\n                return QVariant();\n            }\n        }\n\n    case Qt::DecorationRole: {\n\n        // Check if we aren't inside a tree view\n        if (nested && subnode) {\n            return QVariant();\n        }\n\n        if (index.column() == NameColumn) {\n            is_dark = Config()->windowColorIsDark();\n\n            if (functionIsImport(function.offset)) {\n                if (is_dark) {\n                    return iconFuncImpDark;\n                }\n                return iconFuncImpLight;\n\n            } else if (functionIsMain(function.offset)) {\n                if (is_dark) {\n                    return iconFuncMainDark;\n                }\n                return iconFuncMainLight;\n            }\n\n            if (is_dark) {\n                return iconFuncDark;\n            }\n            return iconFuncLight;\n        }\n\n        return QVariant();\n    }\n\n    case Qt::FontRole:\n        if (currentIndex == function_index)\n            return highlightFont;\n        return defaultFont;\n\n    case Qt::TextAlignmentRole:\n        if (index.column() == 1)\n            return static_cast<int>(Qt::AlignRight | Qt::AlignVCenter);\n        return static_cast<int>(Qt::AlignLeft | Qt::AlignVCenter);\n\n    case Qt::ToolTipRole: {\n\n        QStringList disasmPreview =\n                Core()->getDisassemblyPreview(function.offset, kMaxTooltipDisasmPreviewLines);\n        QStringList summary {};\n        {\n            auto core = Core()->lock();\n            auto seeker = Core()->seekTemp(function.offset);\n            auto strings = fromOwnedCharPtr(rz_core_print_disasm_strings(\n                    core, RZ_CORE_DISASM_STRINGS_MODE_FUNCTION, 0, NULL));\n            summary = strings.split('\\n', CUTTER_QT_SKIP_EMPTY_PARTS);\n        }\n\n        const QFont &fnt = Config()->getFont();\n        QFontMetrics fm { fnt };\n\n        // elide long strings using current disasm font metrics\n        QStringList highlights;\n        for (const QString &s : summary) {\n            highlights << fm.elidedText(s, Qt::ElideRight, kMaxTooltipWidth);\n            if (highlights.length() > kMaxTooltipHighlightsLines) {\n                highlights << \"...\";\n                break;\n            }\n        }\n        if (disasmPreview.isEmpty() && highlights.isEmpty())\n            return {};\n\n        QString toolTipContent =\n                QString(\"<html><div style=\\\"font-family: %1; font-size: %2pt; white-space: \"\n                        \"nowrap;\\\">\")\n                        .arg(fnt.family())\n                        .arg(qMax(6, fnt.pointSize() - 1)); // slightly decrease font size, to\n                                                            // keep more text in the same box\n\n        if (!disasmPreview.isEmpty())\n            toolTipContent += tr(\"<div style=\\\"margin-bottom: 10px;\\\"><strong>Disassembly \"\n                                 \"preview</strong>:<br>%1</div>\")\n                                      .arg(disasmPreview.join(\"<br>\"));\n\n        if (!highlights.isEmpty()) {\n            toolTipContent += tr(\"<div><strong>Highlights</strong>:<br>%1</div>\")\n                                      .arg(highlights.join(QLatin1Char('\\n'))\n                                                   .toHtmlEscaped()\n                                                   .replace(QLatin1Char('\\n'), \"<br>\"));\n        }\n        toolTipContent += \"</div></html>\";\n        return toolTipContent;\n    }\n\n    case Qt::ForegroundRole:\n        if (functionIsImport(function.offset)) {\n            return QVariant(ConfigColor(\"gui.imports\"));\n        } else if (functionIsMain(function.offset)) {\n            return QVariant(ConfigColor(\"gui.main\"));\n        } else if (function.name.startsWith(\"flirt.\")) {\n            return QVariant(ConfigColor(\"gui.flirt\"));\n        }\n\n        return QVariant(this->property(\"color\"));\n\n    case FunctionDescriptionRole:\n        return QVariant::fromValue(function);\n\n    case IsImportRole:\n        return importAddresses.contains(function.offset);\n\n    default:\n        return {};\n    }\n}\n\nQVariant FunctionModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n    if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {\n        if (nested) {\n            return tr(\"Name\");\n        } else {\n            switch (section) {\n            case NameColumn:\n                return tr(\"Name\");\n            case SizeColumn:\n                return tr(\"Size\");\n            case ImportColumn:\n                return tr(\"Import\");\n            case OffsetColumn:\n                return tr(\"Offset\");\n            case NargsColumn:\n                return tr(\"Nargs\");\n            case NlocalsColumn:\n                return tr(\"Nlocals\");\n            case NbbsColumn:\n                return tr(\"Nbbs\");\n            case CalltypeColumn:\n                return tr(\"Call type\");\n            case EdgesColumn:\n                return tr(\"Edges\");\n            case FrameColumn:\n                return tr(\"StackFrame\");\n            case CommentColumn:\n                return tr(\"Comment\");\n            default:\n                return QVariant();\n            }\n        }\n    }\n\n    return QVariant();\n}\n\nvoid FunctionModel::setNested(bool nested)\n{\n    beginResetModel();\n    this->nested = nested;\n    updateCurrentIndex();\n    endResetModel();\n}\n\nRVA FunctionModel::address(const QModelIndex &index) const\n{\n    auto function = data(index, FunctionDescriptionRole).value<FunctionDescription>();\n    return function.offset;\n}\n\nQString FunctionModel::name(const QModelIndex &index) const\n{\n    auto function = data(index, FunctionDescriptionRole).value<FunctionDescription>();\n    return function.name;\n}\n\nvoid FunctionModel::seekChanged(RVA)\n{\n    int previousIndex = currentIndex;\n    if (updateCurrentIndex()) {\n        if (previousIndex >= 0) {\n            emit dataChanged(index(previousIndex, 0), index(previousIndex, columnCount() - 1));\n        }\n        if (currentIndex >= 0) {\n            emit dataChanged(index(currentIndex, 0), index(currentIndex, columnCount() - 1));\n        }\n    }\n}\n\nbool FunctionModel::updateCurrentIndex()\n{\n    int index = -1;\n    RVA offset = 0;\n\n    RVA seek = Core()->getOffset();\n\n    for (int i = 0; i < functions.count(); i++) {\n        const FunctionDescription &function = functions.at(i);\n\n        if (function.contains(seek) && function.offset >= offset) {\n            offset = function.offset;\n            index = i;\n        }\n    }\n\n    bool changed = currentIndex != index;\n\n    currentIndex = index;\n\n    return changed;\n}\n\nvoid FunctionModel::functionRenamed(const RVA offset, const QString &new_name)\n{\n    for (int i = 0; i < functions.count(); i++) {\n        FunctionDescription &function = functions[i];\n        if (function.offset == offset) {\n            function.name = new_name;\n            emit dataChanged(index(i, 0), index(i, columnCount() - 1));\n        }\n    }\n}\n\nFunctionSortFilterProxyModel::FunctionSortFilterProxyModel(FunctionModel *source_model,\n                                                           QObject *parent)\n    : AddressableFilterProxyModel(source_model, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool FunctionSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    FunctionDescription function =\n            index.data(FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();\n\n    return qhelpers::filterStringContains(function.name, this);\n}\n\nbool FunctionSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    if (!left.isValid() || !right.isValid())\n        return false;\n\n    if (left.parent().isValid() || right.parent().isValid())\n        return false;\n\n    FunctionDescription left_function =\n            left.data(FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();\n    FunctionDescription right_function =\n            right.data(FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();\n\n    if (static_cast<FunctionModel *>(sourceModel())->isNested()) {\n        return left_function.name < right_function.name;\n    } else {\n        switch (left.column()) {\n        case FunctionModel::OffsetColumn:\n            return left_function.offset < right_function.offset;\n        case FunctionModel::SizeColumn:\n            if (left_function.linearSize != right_function.linearSize)\n                return left_function.linearSize < right_function.linearSize;\n            break;\n        case FunctionModel::ImportColumn: {\n            bool left_is_import = left.data(FunctionModel::IsImportRole).toBool();\n            bool right_is_import = right.data(FunctionModel::IsImportRole).toBool();\n            if (!left_is_import && right_is_import)\n                return true;\n            break;\n        }\n        case FunctionModel::NameColumn:\n            return left_function.name < right_function.name;\n        case FunctionModel::NargsColumn:\n            if (left_function.nargs != right_function.nargs)\n                return left_function.nargs < right_function.nargs;\n            break;\n        case FunctionModel::NlocalsColumn:\n            if (left_function.nlocals != right_function.nlocals)\n                return left_function.nlocals < right_function.nlocals;\n            break;\n        case FunctionModel::NbbsColumn:\n            if (left_function.nbbs != right_function.nbbs)\n                return left_function.nbbs < right_function.nbbs;\n            break;\n        case FunctionModel::CalltypeColumn:\n            return left_function.calltype < right_function.calltype;\n        case FunctionModel::EdgesColumn:\n            if (left_function.edges != right_function.edges)\n                return left_function.edges < right_function.edges;\n            break;\n        case FunctionModel::FrameColumn:\n            if (left_function.stackframe != right_function.stackframe)\n                return left_function.stackframe < right_function.stackframe;\n            break;\n        case FunctionModel::CommentColumn:\n            return Core()->getCommentAt(left_function.offset)\n                    < Core()->getCommentAt(right_function.offset);\n        default:\n            return false;\n        }\n\n        return left_function.offset < right_function.offset;\n    }\n}\n\nFunctionsWidget::FunctionsWidget(MainWindow *main)\n    : ListDockWidget(main),\n      actionRename(tr(\"Rename\"), this),\n      actionUndefine(tr(\"Undefine\"), this),\n      actionHorizontal(tr(\"Horizontal\"), this),\n      actionVertical(tr(\"Vertical\"), this)\n{\n    setWindowTitle(tr(\"Functions\"));\n    setObjectName(\"FunctionsWidget\");\n\n    setTooltipStylesheet();\n    connect(Config(), &Configuration::colorsUpdated, this, &FunctionsWidget::setTooltipStylesheet);\n\n    QFontInfo font_info = ui->treeView->fontInfo();\n    QFont default_font = QFont(font_info.family(), font_info.pointSize());\n    QFont highlight_font = QFont(font_info.family(), font_info.pointSize(), QFont::Bold);\n\n    functionModel = new FunctionModel(false, default_font, highlight_font, this);\n    functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this);\n    setModels(functionProxyModel);\n    ui->treeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder);\n    ui->treeView->setExpandsOnDoubleClick(false);\n\n    titleContextMenu = new QMenu(this);\n    auto viewTypeGroup = new QActionGroup(titleContextMenu);\n    actionHorizontal.setCheckable(true);\n    actionHorizontal.setActionGroup(viewTypeGroup);\n    connect(&actionHorizontal, &QAction::toggled, this,\n            &FunctionsWidget::onActionHorizontalToggled);\n    actionVertical.setCheckable(true);\n    actionVertical.setActionGroup(viewTypeGroup);\n    connect(&actionVertical, &QAction::toggled, this, &FunctionsWidget::onActionVerticalToggled);\n    titleContextMenu->addActions(viewTypeGroup->actions());\n\n    Shortcuts()->setupAction(actionRename, \"Functions.rename\");\n    actionRename.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n    connect(&actionRename, &QAction::triggered, this,\n            &FunctionsWidget::onActionFunctionsRenameTriggered);\n    connect(&actionUndefine, &QAction::triggered, this,\n            &FunctionsWidget::onActionFunctionsUndefineTriggered);\n\n    auto itemContextMenu = ui->treeView->getItemContextMenu();\n    itemContextMenu->toggleBreakpointAction(true);\n    itemContextMenu->addSeparator();\n    itemContextMenu->addAction(&actionRename);\n    itemContextMenu->addAction(&actionUndefine);\n    itemContextMenu->setWholeFunction(true);\n\n    ui->treeView->addActions(itemContextMenu->actions());\n\n    // Use a custom context menu on the dock title bar\n    if (Config()->getFunctionsWidgetLayout() == \"horizontal\") {\n        actionHorizontal.setChecked(true);\n    } else {\n        actionVertical.setChecked(true);\n    }\n    this->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(this, &QWidget::customContextMenuRequested, this,\n            &FunctionsWidget::showTitleContextMenu);\n\n    connect(Core(), &CutterCore::functionsChanged, this, &FunctionsWidget::refreshTree);\n    connect(Core(), &CutterCore::codeRebased, this, &FunctionsWidget::refreshTree);\n    connect(Core(), &CutterCore::refreshAll, this, &FunctionsWidget::refreshTree);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(functionModel, FunctionModel::CommentColumn); });\n}\n\nFunctionsWidget::~FunctionsWidget() {}\n\nvoid FunctionsWidget::refreshTree()\n{\n    if (task) {\n        task->wait();\n    }\n\n    task = QSharedPointer<FunctionsTask>(new FunctionsTask());\n    connect(task.data(), &FunctionsTask::fetchFinished, this,\n            [this](const QList<FunctionDescription> &functions) {\n                functionModel->beginResetModel();\n\n                functionModel->functions = functions;\n\n                functionModel->importAddresses.clear();\n                for (const ImportDescription &import : Core()->getAllImports()) {\n                    functionModel->importAddresses.insert(import.plt);\n                }\n\n                functionModel->mainAdress = RVA_INVALID;\n                RzCoreLocked core(Core());\n                RzBinFile *bf = rz_bin_cur(core->bin);\n                if (bf) {\n                    const RzBinAddr *binmain =\n                            rz_bin_object_get_special_symbol(bf->o, RZ_BIN_SPECIAL_SYMBOL_MAIN);\n                    if (binmain) {\n                        int va = core->io->va || core->bin->is_debugger;\n                        functionModel->mainAdress = va\n                                ? rz_bin_object_addr_with_base(bf->o, binmain->vaddr)\n                                : binmain->paddr;\n                    }\n                }\n\n                functionModel->updateCurrentIndex();\n                functionModel->endResetModel();\n\n                // resize offset and size columns\n                qhelpers::adjustColumns(ui->treeView, 3, 0);\n            });\n    Core()->getAsyncTaskManager()->start(task);\n}\n\nvoid FunctionsWidget::changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver)\n{\n    ui->dockWidgetContents->setSizePolicy(hor, ver);\n}\n\nvoid FunctionsWidget::onActionFunctionsRenameTriggered()\n{\n    // Get selected item in functions tree view\n    FunctionDescription function = ui->treeView->selectionModel()\n                                           ->currentIndex()\n                                           .data(FunctionModel::FunctionDescriptionRole)\n                                           .value<FunctionDescription>();\n\n    bool ok;\n    // Create dialog\n    QString newName =\n            QInputDialog::getText(this, tr(\"Rename function %1\").arg(function.name),\n                                  tr(\"Function name:\"), QLineEdit::Normal, function.name, &ok);\n    // If user accepted\n    if (ok && !newName.isEmpty()) {\n        // Rename function in rizin core\n        Core()->renameFunction(function.offset, newName);\n\n        // Seek to new renamed function\n        Core()->seekAndShow(function.offset);\n    }\n}\n\nvoid FunctionsWidget::onActionFunctionsUndefineTriggered()\n{\n    const auto selection = ui->treeView->selectionModel()->selection().indexes();\n    QSet<RVA> offsets;\n    for (const auto &index : selection) {\n        offsets.insert(functionProxyModel->address(index));\n    }\n    for (RVA offset : offsets) {\n        Core()->delFunction(offset);\n    }\n}\n\nvoid FunctionsWidget::showTitleContextMenu(const QPoint &pt)\n{\n    titleContextMenu->exec(this->mapToGlobal(pt));\n}\n\nvoid FunctionsWidget::onActionHorizontalToggled(bool enable)\n{\n    if (enable) {\n        Config()->setFunctionsWidgetLayout(\"horizontal\");\n        functionModel->setNested(false);\n        ui->treeView->setIndentation(8);\n    }\n}\n\nvoid FunctionsWidget::onActionVerticalToggled(bool enable)\n{\n    if (enable) {\n        Config()->setFunctionsWidgetLayout(\"vertical\");\n        functionModel->setNested(true);\n        ui->treeView->setIndentation(20);\n    }\n}\n\n/**\n * @brief a SLOT to set the stylesheet for a tooltip\n */\nvoid FunctionsWidget::setTooltipStylesheet()\n{\n    setStyleSheet(DisassemblyPreview::getToolTipStyleSheet());\n}\n"
  },
  {
    "path": "src/widgets/FunctionsWidget.h",
    "content": "#ifndef FUNCTIONSWIDGET_H\n#define FUNCTIONSWIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass MainWindow;\nclass FunctionsTask;\nclass FunctionsWidget;\n\nclass FunctionModel : public AddressableItemModel<>\n{\n    Q_OBJECT\n\n    friend FunctionsWidget;\n\nprivate:\n    QList<FunctionDescription> functions;\n    QSet<RVA> importAddresses;\n    ut64 mainAdress;\n\n    QFont highlightFont;\n    QFont defaultFont;\n    bool nested;\n\n    int currentIndex;\n\n    QIcon iconFuncImpDark;\n    QIcon iconFuncImpLight;\n    QIcon iconFuncMainDark;\n    QIcon iconFuncMainLight;\n    QIcon iconFuncDark;\n    QIcon iconFuncLight;\n\n    bool functionIsImport(ut64 addr) const;\n\n    bool functionIsMain(ut64 addr) const;\n\npublic:\n    static const int FunctionDescriptionRole = Qt::UserRole;\n    static const int IsImportRole = Qt::UserRole + 1;\n\n    enum Column {\n        NameColumn = 0,\n        SizeColumn,\n        ImportColumn,\n        OffsetColumn,\n        NargsColumn,\n        NlocalsColumn,\n        NbbsColumn,\n        CalltypeColumn,\n        EdgesColumn,\n        FrameColumn,\n        CommentColumn,\n        ColumnCount\n    };\n\n    FunctionModel(bool nested, QFont defaultFont, QFont highlightFont, QObject *parent = nullptr);\n\n    QModelIndex index(int row, int column,\n                      const QModelIndex &parent = QModelIndex()) const override;\n    QModelIndex parent(const QModelIndex &index) const override;\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    /**\n     * @return true if the index changed\n     */\n    bool updateCurrentIndex();\n\n    void setNested(bool nested);\n    bool isNested() { return nested; }\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\nprivate slots:\n    void seekChanged(RVA addr);\n    void functionRenamed(const RVA offset, const QString &new_name);\n};\n\nclass FunctionSortFilterProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    FunctionSortFilterProxyModel(FunctionModel *source_model, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass FunctionsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit FunctionsWidget(MainWindow *main);\n    ~FunctionsWidget() override;\n    void changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver);\n\nprivate slots:\n    void onActionFunctionsRenameTriggered();\n    void onActionFunctionsUndefineTriggered();\n    void onActionHorizontalToggled(bool enable);\n    void onActionVerticalToggled(bool enable);\n    void showTitleContextMenu(const QPoint &pt);\n    void setTooltipStylesheet();\n    void refreshTree();\n\nprivate:\n    QSharedPointer<FunctionsTask> task;\n    FunctionModel *functionModel;\n    FunctionSortFilterProxyModel *functionProxyModel;\n\n    QMenu *titleContextMenu;\n\n    QAction actionRename;\n    QAction actionUndefine;\n    QAction actionHorizontal;\n    QAction actionVertical;\n};\n\n#endif // FUNCTIONSWIDGET_H\n"
  },
  {
    "path": "src/widgets/GlibcHeapWidget.cpp",
    "content": "#include <dialogs/GlibcHeapBinsDialog.h>\n#include <dialogs/ArenaInfoDialog.h>\n#include \"GlibcHeapWidget.h\"\n#include \"ui_GlibcHeapWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"QHeaderView\"\n#include \"dialogs/GlibcHeapInfoDialog.h\"\n\nGlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent)\n    : QWidget(parent),\n      ui(new Ui::GlibcHeapWidget),\n      addressableItemContextMenu(this, main),\n      main(main)\n{\n    ui->setupUi(this);\n    viewHeap = ui->tableView;\n    arenaSelectorView = ui->arenaSelector;\n\n    viewHeap->setFont(Config()->getFont());\n    viewHeap->setModel(modelHeap);\n    viewHeap->verticalHeader()->hide();\n    // change the scroll mode to ScrollPerPixel\n    viewHeap->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);\n    viewHeap->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);\n    viewHeap->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    chunkInfoAction = new QAction(tr(\"Detailed Chunk Info\"), this);\n    binInfoAction = new QAction(tr(\"Bins Info\"), this);\n\n    connect(Core(), &CutterCore::refreshAll, this, &GlibcHeapWidget::updateContents);\n    connect(Core(), &CutterCore::debugTaskStateChanged, this, &GlibcHeapWidget::updateContents);\n    connect(viewHeap, &QAbstractItemView::doubleClicked, this, &GlibcHeapWidget::onDoubleClicked);\n    connect<void (QComboBox::*)(int)>(arenaSelectorView, &QComboBox::currentIndexChanged, this,\n                                      &GlibcHeapWidget::onArenaSelected);\n    connect(viewHeap, &QWidget::customContextMenuRequested, this,\n            &GlibcHeapWidget::customMenuRequested);\n    connect(viewHeap->selectionModel(), &QItemSelectionModel::currentChanged, this,\n            &GlibcHeapWidget::onCurrentChanged);\n    connect(chunkInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewChunkInfo);\n    connect(binInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewBinInfo);\n    connect(ui->binsButton, &QPushButton::clicked, this, &GlibcHeapWidget::viewBinInfo);\n    connect(ui->arenaButton, &QPushButton::clicked, this, &GlibcHeapWidget::viewArenaInfo);\n\n    addressableItemContextMenu.addAction(chunkInfoAction);\n    addressableItemContextMenu.addAction(binInfoAction);\n    addActions(addressableItemContextMenu.actions());\n\n    refreshDeferrer = dynamic_cast<CutterDockWidget *>(parent)->createRefreshDeferrer(\n            [this]() { updateContents(); });\n}\n\nGlibcHeapWidget::~GlibcHeapWidget()\n{\n    delete ui;\n}\n\nGlibcHeapModel::GlibcHeapModel(QObject *parent) : QAbstractTableModel(parent) {}\n\nvoid GlibcHeapWidget::updateArenas()\n{\n    arenas = Core()->getArenas();\n\n    // store the currently selected arena's index\n    int currentIndex = arenaSelectorView->currentIndex();\n    arenaSelectorView->clear();\n\n    // add the new arenas to the arena selector\n    for (auto &arena : arenas) {\n        arenaSelectorView->addItem(RzAddressString(arena.offset)\n                                   + QString(\" (\" + arena.type + \" Arena)\"));\n    }\n\n    // check if arenas reduced or invalid index and restore the previously selected arena\n    if (arenaSelectorView->count() <= currentIndex || currentIndex == -1) {\n        currentIndex = 0;\n    }\n    arenaSelectorView->setCurrentIndex(currentIndex);\n}\n\nvoid GlibcHeapWidget::onArenaSelected(int index)\n{\n    if (index == -1) {\n        modelHeap->arena_addr = 0;\n    } else {\n        modelHeap->arena_addr = arenas[index].offset;\n    }\n\n    updateChunks();\n}\n\nvoid GlibcHeapWidget::updateContents()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr) || Core()->isDebugTaskInProgress()) {\n        return;\n    }\n\n    updateArenas();\n    updateChunks();\n}\n\nvoid GlibcHeapWidget::updateChunks()\n{\n    modelHeap->reload();\n    viewHeap->resizeColumnsToContents();\n}\n\nvoid GlibcHeapWidget::customMenuRequested(QPoint pos)\n{\n    addressableItemContextMenu.exec(viewHeap->viewport()->mapToGlobal(pos));\n}\n\nvoid GlibcHeapModel::reload()\n{\n    beginResetModel();\n    values.clear();\n    values = Core()->getHeapChunks(arena_addr);\n    endResetModel();\n}\n\nint GlibcHeapModel::columnCount(const QModelIndex &) const\n{\n    return ColumnCount;\n}\n\nint GlibcHeapModel::rowCount(const QModelIndex &) const\n{\n    return this->values.size();\n}\n\nQVariant GlibcHeapModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid() || index.row() >= values.count())\n        return QVariant();\n\n    const auto &item = values.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case OffsetColumn:\n            return RzAddressString(item.offset);\n        case SizeColumn:\n            return RzHexString(item.size);\n        case StatusColumn:\n            return item.status;\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nQVariant GlibcHeapModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n    Q_UNUSED(orientation);\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case OffsetColumn:\n            return tr(\"Offset\");\n        case SizeColumn:\n            return tr(\"Size\");\n        case StatusColumn:\n            return tr(\"Status\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nvoid GlibcHeapWidget::onDoubleClicked(const QModelIndex &index)\n{\n    if (!index.isValid()) {\n        return;\n    }\n\n    int column = index.column();\n    if (column == GlibcHeapModel::OffsetColumn) {\n        QString item = index.data().toString();\n        Core()->seek(item);\n        main->showMemoryWidget(MemoryWidgetType::Hexdump);\n    }\n}\n\nvoid GlibcHeapWidget::onCurrentChanged(const QModelIndex &current, const QModelIndex &prev)\n{\n    Q_UNUSED(current)\n    Q_UNUSED(prev)\n\n    auto currentIndex = viewHeap->selectionModel()->currentIndex();\n    QString offsetString = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::OffsetColumn)\n                                   .data()\n                                   .toString();\n    addressableItemContextMenu.setTarget(Core()->math(offsetString));\n}\n\nvoid GlibcHeapWidget::viewChunkInfo()\n{\n    auto currentIndex = viewHeap->selectionModel()->currentIndex();\n    QString offsetString = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::OffsetColumn)\n                                   .data()\n                                   .toString();\n    QString status = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::StatusColumn)\n                             .data()\n                             .toString();\n\n    GlibcHeapInfoDialog heapInfoDialog(Core()->math(offsetString), status, this);\n    heapInfoDialog.exec();\n}\n\nvoid GlibcHeapWidget::viewBinInfo()\n{\n    GlibcHeapBinsDialog heapBinsDialog(modelHeap->arena_addr, main, this);\n    heapBinsDialog.exec();\n}\n\nvoid GlibcHeapWidget::viewArenaInfo()\n{\n    // find the active arena\n    Arena currentArena;\n    for (auto &arena : arenas) {\n        if (arena.offset == modelHeap->arena_addr) {\n            currentArena = arena;\n            break;\n        }\n    }\n\n    ArenaInfoDialog arenaInfoDialog(currentArena, this);\n    arenaInfoDialog.exec();\n}"
  },
  {
    "path": "src/widgets/GlibcHeapWidget.h",
    "content": "#ifndef GLIBCHEAPWIDGET_H\n#define GLIBCHEAPWIDGET_H\n\n#include <QDockWidget>\n#include \"CutterDockWidget.h\"\n#include \"core/Cutter.h\"\n#include <QTableView>\n#include <QComboBox>\n#include <AddressableItemContextMenu.h>\n\nnamespace Ui {\nclass GlibcHeapWidget;\n}\n\nclass GlibcHeapModel : public QAbstractTableModel\n{\n    Q_OBJECT\npublic:\n    explicit GlibcHeapModel(QObject *parent = nullptr);\n    enum Column { OffsetColumn = 0, SizeColumn, StatusColumn, ColumnCount };\n    void reload();\n    int rowCount(const QModelIndex &parent) const override;\n    int columnCount(const QModelIndex &parent) const override;\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;\n    RVA arena_addr = 0;\n\nprivate:\n    QVector<Chunk> values;\n};\n\nclass GlibcHeapWidget : public QWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit GlibcHeapWidget(MainWindow *main, QWidget *parent);\n    ~GlibcHeapWidget();\nprivate slots:\n    void updateContents();\n    void onDoubleClicked(const QModelIndex &index);\n    void onArenaSelected(int index);\n    void customMenuRequested(QPoint pos);\n    void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);\n    void viewChunkInfo();\n    void viewBinInfo();\n    void viewArenaInfo();\n\nprivate:\n    void updateArenas();\n    void updateChunks();\n    Ui::GlibcHeapWidget *ui;\n    QTableView *viewHeap;\n    QComboBox *arenaSelectorView;\n    GlibcHeapModel *modelHeap = new GlibcHeapModel(this);\n    QVector<Arena> arenas;\n    QAction *chunkInfoAction;\n    QAction *binInfoAction;\n    AddressableItemContextMenu addressableItemContextMenu;\n    RefreshDeferrer *refreshDeferrer {};\n    MainWindow *main;\n};\n\n#endif // GLIBCHEAPWIDGET_H\n"
  },
  {
    "path": "src/widgets/GlibcHeapWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GlibcHeapWidget</class>\n <widget class=\"QWidget\" name=\"GlibcHeapWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTableView\" name=\"tableView\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n        <item>\n            <widget class=\"QComboBox\" name=\"arenaSelector\"/>\n        </item>\n        <item>\n            <widget class=\"QPushButton\" name=\"arenaButton\">\n                <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                    </sizepolicy>\n                </property>\n                <property name=\"text\">\n                    <string>Arena</string>\n                </property>\n            </widget>\n        </item>\n        <item>\n            <widget class=\"QPushButton\" name=\"binsButton\">\n                <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                    </sizepolicy>\n                </property>\n                <property name=\"toolTip\">\n                    <string>View bins info for an arena</string>\n                </property>\n                <property name=\"text\">\n                    <string>Bins</string>\n                </property>\n            </widget>\n        </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/GlobalsWidget.cpp",
    "content": "#include \"GlobalsWidget.h\"\n#include \"ui_GlobalsWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"dialogs/GlobalVariableDialog.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QMenu>\n#include <QShortcut>\n\nGlobalsModel::GlobalsModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint GlobalsModel::rowCount(const QModelIndex &) const\n{\n    return globals.count();\n}\n\nint GlobalsModel::columnCount(const QModelIndex &) const\n{\n    return GlobalsModel::ColumnCount;\n}\n\nQVariant GlobalsModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= globals.count()) {\n        return QVariant();\n    }\n\n    const GlobalDescription &global = globals.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case GlobalsModel::AddressColumn:\n            return RzAddressString(global.addr);\n        case GlobalsModel::TypeColumn:\n            return QString(global.type).trimmed();\n        case GlobalsModel::NameColumn:\n            return global.name;\n        case GlobalsModel::CommentColumn:\n            return Core()->getCommentAt(global.addr);\n        default:\n            return QVariant();\n        }\n    case GlobalsModel::GlobalDescriptionRole:\n        return QVariant::fromValue(global);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant GlobalsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case GlobalsModel::AddressColumn:\n            return tr(\"Address\");\n        case GlobalsModel::TypeColumn:\n            return tr(\"Type\");\n        case GlobalsModel::NameColumn:\n            return tr(\"Name\");\n        case GlobalsModel::CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA GlobalsModel::address(const QModelIndex &index) const\n{\n    const GlobalDescription &global = globals.at(index.row());\n    return global.addr;\n}\n\nQString GlobalsModel::name(const QModelIndex &index) const\n{\n    const GlobalDescription &global = globals.at(index.row());\n    return global.name;\n}\n\nGlobalsProxyModel::GlobalsProxyModel(GlobalsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool GlobalsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    auto global = index.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();\n\n    return qhelpers::filterStringContains(global.name, this);\n}\n\nbool GlobalsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    auto leftGlobal = left.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();\n    auto rightGlobal = right.data(GlobalsModel::GlobalDescriptionRole).value<GlobalDescription>();\n\n    switch (left.column()) {\n    case GlobalsModel::AddressColumn:\n        return leftGlobal.addr < rightGlobal.addr;\n    case GlobalsModel::TypeColumn:\n        return leftGlobal.type < rightGlobal.type;\n    case GlobalsModel::NameColumn:\n        return leftGlobal.name < rightGlobal.name;\n    case GlobalsModel::CommentColumn:\n        return Core()->getCommentAt(leftGlobal.addr) < Core()->getCommentAt(rightGlobal.addr);\n    default:\n        break;\n    }\n\n    return false;\n}\n\nvoid GlobalsWidget::editGlobal()\n{\n    QModelIndex index = ui->treeView->currentIndex();\n\n    if (!index.isValid()) {\n        return;\n    }\n\n    RVA globalVariableAddress = globalsProxyModel->address(index);\n\n    GlobalVariableDialog dialog(globalVariableAddress, parentWidget());\n    dialog.exec();\n}\n\nvoid GlobalsWidget::deleteGlobal()\n{\n    QModelIndex index = ui->treeView->currentIndex();\n\n    if (!index.isValid()) {\n        return;\n    }\n\n    RVA globalVariableAddress = globalsProxyModel->address(index);\n    Core()->delGlobalVariable(globalVariableAddress);\n}\n\nGlobalsWidget::GlobalsWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::GlobalsWidget), tree(new CutterTreeWidget(this))\n{\n    ui->setupUi(this);\n    ui->quickFilterView->setLabelText(tr(\"Category\"));\n\n    setWindowTitle(tr(\"Globals\"));\n    setObjectName(\"GlobalsWidget\");\n\n    // Add status bar which displays the count\n    tree->addStatusBar(ui->verticalLayout);\n\n    // Set single select mode\n    ui->treeView->setSelectionMode(QAbstractItemView::SingleSelection);\n\n    ui->treeView->setMainWindow(mainWindow);\n\n    // Setup up the model and the proxy model\n    globalsModel = new GlobalsModel(this);\n    globalsProxyModel = new GlobalsProxyModel(globalsModel, this);\n    ui->treeView->setModel(globalsProxyModel);\n    ui->treeView->sortByColumn(GlobalsModel::AddressColumn, Qt::AscendingOrder);\n\n    // Setup custom context menu\n    ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, globalsProxyModel,\n            &QSortFilterProxyModel::setFilterWildcard);\n\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, this,\n            [this] { tree->showItemsNumber(globalsProxyModel->rowCount()); });\n\n    QShortcut *searchShortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,\n            &ComboQuickFilterView::showFilter);\n    searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    QShortcut *clearShortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n    connect(clearShortcut, &QShortcut::activated, ui->quickFilterView,\n            &ComboQuickFilterView::clearFilter);\n    clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    actionEditGlobal = new QAction(tr(\"Edit Global Variable\"), this);\n    actionDeleteGlobal = new QAction(tr(\"Delete Global Variable\"), this);\n\n    auto menu = ui->treeView->getItemContextMenu();\n    menu->addAction(actionEditGlobal);\n    menu->addAction(actionDeleteGlobal);\n\n    connect(actionEditGlobal, &QAction::triggered, this, [this]() { editGlobal(); });\n    connect(actionDeleteGlobal, &QAction::triggered, this, [this]() { deleteGlobal(); });\n\n    connect(Core(), &CutterCore::globalVarsChanged, this, &GlobalsWidget::refreshGlobals);\n    connect(Core(), &CutterCore::codeRebased, this, &GlobalsWidget::refreshGlobals);\n    connect(Core(), &CutterCore::refreshAll, this, &GlobalsWidget::refreshGlobals);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(globalsModel, GlobalsModel::CommentColumn); });\n}\n\nGlobalsWidget::~GlobalsWidget() {}\n\nvoid GlobalsWidget::refreshGlobals()\n{\n    globalsModel->beginResetModel();\n    globalsModel->globals = Core()->getAllGlobals();\n    globalsModel->endResetModel();\n\n    qhelpers::adjustColumns(ui->treeView, GlobalsModel::ColumnCount, 0);\n}\n"
  },
  {
    "path": "src/widgets/GlobalsWidget.h",
    "content": "#ifndef GLOBALSWIDGET_H\n#define GLOBALSWIDGET_H\n\n#include <memory>\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass MainWindow;\nclass QTreeWidget;\nclass GlobalsWidget;\n\nnamespace Ui {\nclass GlobalsWidget;\n}\n\nclass MainWindow;\nclass QTreeWidgetItem;\n\nclass GlobalsModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend GlobalsWidget;\n\nprivate:\n    QList<GlobalDescription> globals;\n\npublic:\n    enum Column { AddressColumn = 0, TypeColumn, NameColumn, CommentColumn, ColumnCount };\n    enum Role { GlobalDescriptionRole = Qt::UserRole };\n\n    GlobalsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n};\n\nclass GlobalsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    GlobalsProxyModel(GlobalsModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass GlobalsWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit GlobalsWidget(MainWindow *main);\n    ~GlobalsWidget();\n\nprivate slots:\n    void refreshGlobals();\n\n    void editGlobal();\n    void deleteGlobal();\n\nprivate:\n    std::unique_ptr<Ui::GlobalsWidget> ui;\n\n    GlobalsModel *globalsModel;\n    GlobalsProxyModel *globalsProxyModel;\n    CutterTreeWidget *tree;\n\n    QAction *actionEditGlobal;\n    QAction *actionDeleteGlobal;\n};\n\n#endif // GLOBALSWIDGET_H\n"
  },
  {
    "path": "src/widgets/GlobalsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GlobalsWidget</class>\n <widget class=\"QDockWidget\" name=\"GlobalsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Global Variables</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"AddressableItemList&lt;&gt;\" name=\"treeView\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::Shape::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"ComboQuickFilterView\" name=\"quickFilterView\" native=\"true\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <action name=\"actionEditGlobal\">\n   <property name=\"text\">\n    <string>Edit Global Variable</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Edit Global Variable</string>\n   </property>\n  </action>\n  <action name=\"actionDeleteGlobal\">\n   <property name=\"text\">\n    <string>Delete Global Variable</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Delete Global Variable</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ComboQuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/ComboQuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>AddressableItemList&lt;&gt;</class>\n   <extends>QTreeView</extends>\n   <header>widgets/AddressableItemList.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/GraphGridLayout.cpp",
    "content": "#include \"GraphGridLayout.h\"\n\n#include <unordered_set>\n#include <unordered_map>\n#include <queue>\n#include <stack>\n#include <cassert>\n#include <queue>\n\n#include \"common/BinaryTrees.h\"\n\n/** @class GraphGridLayout\n\nBasic familiarity with graph algorithms is recommended.\n\n# Terms used:\n- **Vertex**, **node**, **block** - see the definition of graph. Within this text\nvertex/node/block are used interchangeably due to the code being purposed for visualizing basic\nblock control flow graph.\n- **edge** - see the definition of graph.\n- **DAG** - directed acyclic graph, a graph using directed edges which doesn't have cycles. A DAG\nmay contain loops if following them would require going in both directions of edges. Example 1->2\n1->3 3->2 is a DAG, 2->1 1->3 3->2 isn't a DAG.\n- **DFS** - depth first search, a graph traversal algorithm\n- **toposort** - topological sorting, the process of ordering a DAG vertices that results in all\nedges going from vertices earlier in the toposort order to vertices later in toposort order. There\nare multiple algorithms implementing toposort. A single DAG can have multiple valid topological\norderings, a toposort algorithm can be designed to prioritize a specific one from all valid toposort\norders. Example: for graph 1->4, 2->1, 2->3, 3->4 valid topological orders are [2,1,3,4] and\n[2,3,1,4].\n\n# High level algorithm structure\n1. Select a subset of edges that form a DAG (remove cycles)\n2. Toposort the DAG\n3. Choose a subset of edges that form a tree and assign layers\n4. Assign node positions within grid using tree structure, child subtrees are placed side by side\nwith parent on top\n5. Perform edge routing\n6. Calculate column and row pixel positions based on node sizes and amount edges between the rows\n7. [optional] Layout compacting\n\n\nContrary to many other layered graph-drawing algorithms this implementation doesn't perform node\nreordering to minimize edge crossing. This simplifies the implementation, and preserves the original\ncontrol-flow structure for conditional jumps ( true jump on one side, false jump on other). Due to\nmost of the control flow resulting from structured programming constructs like if/then/else and\nloops, the resulting layout is usually readable without node reordering within layers.\n\n\n# Grid\nTo simplify the layout algorithm, its initial steps assume that all nodes have the same size and\nthat edges are zero-width. After nodes placement and edges rounting, the row/column of nodes is\nknown as well as the amount of edges between each pair of rows. Using this information, positions\nare converted from grid cells to pixel coordinates. Routing zero-width edges between rows can also\nbe interpreted as every second row and column being reserved for edges. The row numbers in code are\nusing the first interpretation. To allow better centering of nodes one above other, each node is 2\ncolumns wide and 1 row high.\n\n\\image html graph_grid.svg\n\n# 1-2 Cycle removal and toposort\n\nCycle removal and toposort are done in a single DFS traversal. In case the entrypoint\nis part of a loop, the DFS starts from the entrypoint. This ensures that the entrypoint is at the\ntop of resulting layout, if possible. The resulting toposort order is used in many of the following\nlayout steps that require calculating some property of a vertex based on a child property or the\nother way around. Using toposort order, such operations can be implemented by array iteration in\neither forward/backward direction. To prevent running out of stack memory when processing large\ngraphs, DFS is implemented non-recursively.\n\n# Row assignment\n\nRows are assigned in toposort order from top to bottom, with nodes row being max(predecessor.row)+1.\nThis ensures that loop back-edges are the only edges going from lower to higher layers.\n\nTo further simply node placement, a subset of edges is selected which forms a tree. This turns a DAG\ndrawing problem into a tree drawing problem. For each node in level n the following nodes with\nlevel exactly n+1 are greedily assigned as child nodes in the tree. If a node already has a parent\nassigned then the corresponding edge is not part of the tree.\n\n# Node placement\n\nSince the graph has been reduced to a tree, node placement is more or less putting subtrees side by\nside with parent on top. There is some room for interpretation as to what exactly 'side by side'\nmeans and where exactly 'on top' is: drawing the graph either too dense or too sparse may make it\nless readable, so there are configuration options which allow choosing these things resulting in\nmore or less dense layout.\n\nOnce the subtrees are placed side by side, the parent node can be placed either in the middle of\nthe horizontal bounds or in the middle of its direct children. The first option results in narrower\nlayout and more vertical columns, while the second option results in more spread out layout which\nmay help seeing where each edge goes.\n\nIn compact mode two subtrees are placed side by side accounting for their shape. In wider\nmode the bounding box of the shorter subtree is used instead of its exact shape. This gives slightly\nsparser layout without being too wide.\n\n\\image html graph_parent_placement.svg\n\n# Edge routing\nEdge routing can be split into: main column selection, rough routing, and segment offset\ncalculation.\n\nTransition from source to target row is done using a single vertical segment. This segment is called\nthe 'main column'.\n\nMain columns are computed using a sweep line: blocks and edges are processed as events top to\nbottom based off their row (max(start row, end row) for edges). Blocked columns are tracked in a\ntree structure which allows searching nearest column with at least last N rows empty. The column\nof the starting block is favored for the main column, otherwise the target block's column is chosen\nif it is not blocked. If both the source and target columns are blocked, nearest unblocked column\nis chosen. An empty column can always be found, in the worst case there are empty columns at the\nsides of drawing. If two columns are equally close, the tie is broken based on whether the edge is a\ntrue or false branch. In case of upward edges it is allowed to choose a column on the outside which\nis slightly further than nearest empty to reduce the chance of producing tilted figure 8 shaped\ncrossing between two blocks.\n\nDue to nodes being placed in a grid, horizontal segments of edges can't intersect with any nodes.\nThe path for edges is chosen so that it consists of at most 5 segments, typically resulting in\nsideways U shape or square Z shape:\n- short vertical segment from node to horizontal line\n- move to empty column\n- vertical segment between starting row and end row\n- horizontal segment to target node column\n- short vertical segment connecting to target node\n\nThere are 3 special cases:\n- source and target nodes are in the same column with no nodes between - single vertical segment\n- column bellow stating node is empty - segments 1-3 are merged\n- column above target node is empty - segments 3-5 are merged\n\nAfter rough routing segment offsets are calculated relative to their corresponding edge column. This\nensures that two segments don't overlap. Segment offsets within each column are assigned greedily\nwith some heuristics for assignment order to reduce amount of edge crossings and result in more\nvisually pleasing output for a typical CFG graph. Each segment gets assigned an offset that is\nmaximum of previously assigned offsets overlapping with current segment + segment spacing.\n\nAssignment order is based on:\n- direction of previous and last segment - helps reducing crossings and place the segments between\nnodes\n- segment length - reduces crossing when segment endpoints have the same structure as valid\nparentheses expression\n- edge length - establishes some kind of order when single node is connected to many edges,\ntypically a block with switch statement or block after switch statement.\n\n# Layout compacting\n\nDoing the layout on a grid limits the minimal spacing to the widest block within a column and\ntallest block within a row. One common case is a function-entry block being wider due to the\nfunction name, causing wide horizontal space between branching blocks. Another case is rows in two\nparallel columns being aligned.\n\n\\image html layout_compacting.svg\n\nBoth problems are mitigated by squishing the graph. Compressing in each of the two direction is done\nseparately. The process is defined as liner program. Each variable represents a position of edge\nsegment or node in the direction being optimized.\n\nThe following constraints are used:\n- Keep the order with nearest segments.\n- If a node has two outgoing edges, one to the left and one to the right, keep them\non the corresponding side of the node's center.\n- Equality constraint to keep relative position between nodes and and segments directly connected to\nthem.\n- For all blocks connected by forward edge, keep the vertical distance at least as big as configured\nblock vertical spacing. This helps when vertical block-spacing is set bigger than double edge\nspacing and an edge shadows relationship between two blocks.\n- Equality constraint to keep a node centered when control flow merges.\n\nIn the vertical direction the objective function minimizes y positions of nodes and lengths of\nvertical segments. In the horizontal direction the objective function minimizes the lengths of\nhorizontal segments.\n\nIn the resulting linear program all constraints besides x_i >= 0 consist of exactly two variables:\neither x_i - x_j <= c_k or x_i = x_j + c_k.\n\nSince a perfect solution isn't necessary and to avoid worst case performance, the current\nimplementation isn't using a general purpose linear solver. Instead, each variable is modified\nuntil a constraint is satisfied and afterwards variables are grouped and modified together.\n\n*/\n\nGraphGridLayout::GraphGridLayout(GraphGridLayout::LayoutType layoutType) : GraphLayout({})\n{\n    switch (layoutType) {\n    case LayoutType::Narrow:\n        tightSubtreePlacement = true;\n        parentBetweenDirectChild = false;\n        useLayoutOptimization = true;\n        break;\n    case LayoutType::Medium:\n        tightSubtreePlacement = false;\n        parentBetweenDirectChild = true;\n        useLayoutOptimization = true;\n        break;\n    case LayoutType::Wide:\n        tightSubtreePlacement = false;\n        parentBetweenDirectChild = true;\n        useLayoutOptimization = false;\n        break;\n    }\n}\n\nstd::vector<ut64> GraphGridLayout::topoSort(LayoutState &state, ut64 entry)\n{\n    auto &blocks = *state.blocks;\n\n    // Run DFS to:\n    // * select backwards/loop edges\n    // * perform toposort\n    std::vector<ut64> blockOrder;\n    enum class State : uint8_t { NotVisited = 0, InStack, Visited };\n    std::unordered_map<ut64, State> visited;\n    visited.reserve(state.blocks->size());\n    std::stack<std::pair<ut64, size_t>> stack;\n    auto dfsFragment = [&visited, &blocks, &state, &stack, &blockOrder](ut64 first) {\n        visited[first] = State::InStack;\n        stack.push({ first, 0 });\n        while (!stack.empty()) {\n            auto v = stack.top().first;\n            auto edge_index = stack.top().second;\n            const auto &block = blocks[v];\n            if (edge_index < block.edges.size()) {\n                ++stack.top().second;\n                auto target = block.edges[edge_index].target;\n                auto &targetState = visited[target];\n                if (targetState == State::NotVisited) {\n                    targetState = State::InStack;\n                    stack.push({ target, 0 });\n                    state.grid_blocks[v].dag_edge.push_back(target);\n                } else if (targetState == State::Visited) {\n                    state.grid_blocks[v].dag_edge.push_back(target);\n                } // else {  targetState == 1 in stack, loop edge }\n            } else {\n                stack.pop();\n                visited[v] = State::Visited;\n                blockOrder.push_back(v);\n            }\n        }\n    };\n\n    // Start with entry so that if start of function block is part of loop it\n    // is still kept at top unless it's impossible to do while maintaining\n    // topological order.\n    dfsFragment(entry);\n    for (auto &blockIt : blocks) {\n        if (visited[blockIt.first] == State::NotVisited) {\n            dfsFragment(blockIt.first);\n        }\n    }\n\n    return blockOrder;\n}\n\nvoid GraphGridLayout::assignRows(GraphGridLayout::LayoutState &state,\n                                 const std::vector<unsigned long long> &blockOrder)\n{\n    for (auto it = blockOrder.rbegin(), end = blockOrder.rend(); it != end; it++) {\n        auto &block = state.grid_blocks[*it];\n        int nextLevel = block.row + 1;\n        for (auto target : block.dag_edge) {\n            auto &targetBlock = state.grid_blocks[target];\n            targetBlock.row = std::max(targetBlock.row, nextLevel);\n        }\n    }\n}\n\nvoid GraphGridLayout::selectTree(GraphGridLayout::LayoutState &state)\n{\n    for (auto &blockIt : state.grid_blocks) {\n        auto &block = blockIt.second;\n        for (auto targetId : block.dag_edge) {\n            auto &targetBlock = state.grid_blocks[targetId];\n            if (!targetBlock.has_parent && targetBlock.row == block.row + 1) {\n                block.tree_edge.push_back(targetId);\n                targetBlock.has_parent = true;\n            }\n        }\n    }\n}\n\nvoid GraphGridLayout::CalculateLayout(GraphLayout::Graph &blocks, ut64 entry, int &width,\n                                      int &height) const\n{\n    LayoutState layoutState;\n    layoutState.blocks = &blocks;\n    if (blocks.empty()) {\n        return;\n    }\n    if (blocks.find(entry) == blocks.end()) {\n        entry = blocks.begin()->first;\n    }\n\n    for (auto &it : blocks) {\n        GridBlock block;\n        block.id = it.first;\n        layoutState.grid_blocks[it.first] = block;\n    }\n\n    auto blockOrder = topoSort(layoutState, entry);\n    computeAllBlockPlacement(blockOrder, layoutState);\n\n    for (auto &blockIt : blocks) {\n        layoutState.edge[blockIt.first].resize(blockIt.second.edges.size());\n        for (size_t i = 0; i < blockIt.second.edges.size(); i++) {\n            layoutState.edge[blockIt.first][i].dest = blockIt.second.edges[i].target;\n            blockIt.second.edges[i].arrow = GraphEdge::Down;\n        }\n    }\n    for (const auto &edgeList : layoutState.edge) {\n        auto &startBlock = layoutState.grid_blocks[edgeList.first];\n        startBlock.outputCount = edgeList.second.size();\n        for (auto &edge : edgeList.second) {\n            auto &targetBlock = layoutState.grid_blocks[edge.dest];\n            targetBlock.inputCount++;\n        }\n    }\n\n    layoutState.columns = 1;\n    layoutState.rows = 1;\n    for (auto &node : layoutState.grid_blocks) {\n        // count is at least index + 1\n        layoutState.rows = std::max(layoutState.rows, size_t(node.second.row) + 1);\n        // block is 2 column wide\n        layoutState.columns = std::max(layoutState.columns, size_t(node.second.col) + 2);\n    }\n\n    layoutState.rowHeight.assign(layoutState.rows, 0);\n    layoutState.columnWidth.assign(layoutState.columns, 0);\n    for (auto &node : layoutState.grid_blocks) {\n        const auto &inputBlock = blocks[node.first];\n        layoutState.rowHeight[node.second.row] =\n                std::max(inputBlock.height, layoutState.rowHeight[node.second.row]);\n        layoutState.columnWidth[node.second.col] =\n                std::max(inputBlock.width / 2, layoutState.columnWidth[node.second.col]);\n        layoutState.columnWidth[node.second.col + 1] =\n                std::max(inputBlock.width / 2, layoutState.columnWidth[node.second.col + 1]);\n    }\n\n    routeEdges(layoutState);\n\n    convertToPixelCoordinates(layoutState, width, height);\n    if (useLayoutOptimization) {\n        optimizeLayout(layoutState);\n        cropToContent(blocks, width, height);\n    }\n}\n\nvoid GraphGridLayout::findMergePoints(GraphGridLayout::LayoutState &state) const\n{\n    for (auto &blockIt : state.grid_blocks) {\n        auto &block = blockIt.second;\n        GridBlock *mergeBlock = nullptr;\n        int grandChildCount = 0;\n        for (auto edge : block.tree_edge) {\n            auto &targetBlock = state.grid_blocks[edge];\n            if (targetBlock.tree_edge.size()) {\n                mergeBlock = &state.grid_blocks[targetBlock.tree_edge[0]];\n            }\n            grandChildCount += targetBlock.tree_edge.size();\n        }\n        if (!mergeBlock || grandChildCount != 1) {\n            continue;\n        }\n        int blocksGoingToMerge = 0;\n        int blockWithTreeEdge = 0;\n        for (auto edge : block.tree_edge) {\n            auto &targetBlock = state.grid_blocks[edge];\n            bool goesToMerge = false;\n            for (auto secondEdgeTarget : targetBlock.dag_edge) {\n                if (secondEdgeTarget == mergeBlock->id) {\n                    goesToMerge = true;\n                    break;\n                }\n            }\n            if (goesToMerge) {\n                if (targetBlock.tree_edge.size() == 1) {\n                    blockWithTreeEdge = blocksGoingToMerge;\n                }\n                blocksGoingToMerge++;\n            } else {\n                break;\n            }\n        }\n        if (blocksGoingToMerge) {\n            block.mergeBlock = mergeBlock->id;\n            state.grid_blocks[block.tree_edge[blockWithTreeEdge]].col =\n                    blockWithTreeEdge * 2 - (blocksGoingToMerge - 1);\n        }\n    }\n}\n\nvoid GraphGridLayout::computeAllBlockPlacement(const std::vector<ut64> &blockOrder,\n                                               LayoutState &layoutState) const\n{\n    assignRows(layoutState, blockOrder);\n    selectTree(layoutState);\n    findMergePoints(layoutState);\n\n    // Shapes of subtrees are maintained using linked lists. Each value within list is column\n    // relative to previous row. This allows moving things around by changing only first value in\n    // list.\n    LinkedListPool<int> sides(blockOrder.size() * 2); // *2 = two sides for each node\n\n    // Process nodes in the order from bottom to top. Ensures that all subtrees are processed before\n    // parent node.\n    for (auto blockId : blockOrder) {\n        auto &block = layoutState.grid_blocks[blockId];\n        if (block.tree_edge.size() == 0) {\n            block.row_count = 1;\n            block.col = 0;\n            block.lastRowRight = 2;\n            block.lastRowLeft = 0;\n            block.leftPosition = 0;\n            block.rightPosition = 2;\n\n            block.leftSideShape = sides.makeList(0);\n            block.rightSideShape = sides.makeList(2);\n        } else {\n            auto &firstChild = layoutState.grid_blocks[block.tree_edge[0]];\n            auto leftSide =\n                    firstChild\n                            .leftSideShape; // left side of block children subtrees processed so far\n            auto rightSide = firstChild.rightSideShape;\n            block.row_count = firstChild.row_count;\n            block.lastRowRight = firstChild.lastRowRight;\n            block.lastRowLeft = firstChild.lastRowLeft;\n            block.leftPosition = firstChild.leftPosition;\n            block.rightPosition = firstChild.rightPosition;\n            // Place children subtrees side by side\n            for (size_t i = 1; i < block.tree_edge.size(); i++) {\n                auto &child = layoutState.grid_blocks[block.tree_edge[i]];\n                int minPos = INT_MIN;\n                int leftPos = 0;\n                int rightPos = 0;\n                auto leftIt = sides.head(rightSide);\n                auto rightIt = sides.head(child.leftSideShape);\n                int maxLeftWidth = 0;\n                int minRightPos = child.col;\n\n                while (leftIt\n                       && rightIt) { // process part of subtrees that touch when put side by side\n                    leftPos += *leftIt;\n                    rightPos += *rightIt;\n                    minPos = std::max(minPos, leftPos - rightPos);\n                    maxLeftWidth = std::max(maxLeftWidth, leftPos);\n                    minRightPos = std::min(minRightPos, rightPos);\n                    ++leftIt;\n                    ++rightIt;\n                }\n                int rightTreeOffset = 0;\n                if (tightSubtreePlacement) {\n                    rightTreeOffset = minPos; // mode a) place subtrees as close as possible\n                } else {\n                    // mode b) use bounding box for shortest subtree and full shape of other side\n                    if (leftIt) {\n                        rightTreeOffset = maxLeftWidth - child.leftPosition;\n                    } else {\n                        rightTreeOffset = block.rightPosition - minRightPos;\n                    }\n                }\n                // Calculate the new shape after putting the two subtrees side by side\n                child.col += rightTreeOffset;\n                if (leftIt) {\n                    *leftIt -= (rightTreeOffset + child.lastRowRight - leftPos);\n                    rightSide =\n                            sides.append(child.rightSideShape, sides.splitTail(rightSide, leftIt));\n                } else if (rightIt) {\n                    *rightIt += (rightPos + rightTreeOffset - block.lastRowLeft);\n                    leftSide =\n                            sides.append(leftSide, sides.splitTail(child.leftSideShape, rightIt));\n\n                    rightSide = child.rightSideShape;\n                    block.lastRowRight = child.lastRowRight + rightTreeOffset;\n                    block.lastRowLeft = child.lastRowLeft + rightTreeOffset;\n                } else {\n                    rightSide = child.rightSideShape;\n                }\n                *sides.head(rightSide) += rightTreeOffset;\n                block.row_count = std::max(block.row_count, child.row_count);\n                block.leftPosition =\n                        std::min(block.leftPosition, child.leftPosition + rightTreeOffset);\n                block.rightPosition =\n                        std::max(block.rightPosition, rightTreeOffset + child.rightPosition);\n            }\n\n            int col = 0;\n            // Calculate parent position\n            if (parentBetweenDirectChild) {\n                // mode a) keep one child to the left, other to the right\n                for (auto target : block.tree_edge) {\n                    col += layoutState.grid_blocks[target].col;\n                }\n                col /= block.tree_edge.size();\n            } else {\n                // mode b) somewhere between left most direct child and right most, preferably in\n                // the middle of horizontal dimensions. Results layout looks more like single\n                // vertical line.\n                col = (block.rightPosition + block.leftPosition) / 2 - 1;\n                col = std::max(col, layoutState.grid_blocks[block.tree_edge.front()].col - 1);\n                col = std::min(col, layoutState.grid_blocks[block.tree_edge.back()].col + 1);\n            }\n            block.col += col; // += instead of = to keep offset calculated in previous steps\n            block.row_count += 1;\n            block.leftPosition = std::min(block.leftPosition, block.col);\n            block.rightPosition = std::max(block.rightPosition, block.col + 2);\n\n            *sides.head(leftSide) -= block.col;\n            block.leftSideShape = sides.append(sides.makeList(block.col), leftSide);\n\n            *sides.head(rightSide) -= block.col + 2;\n            block.rightSideShape = sides.append(sides.makeList(block.col + 2), rightSide);\n\n            // Keep children positions relative to parent so that moving parent moves whole subtree\n            for (auto target : block.tree_edge) {\n                auto &targetBlock = layoutState.grid_blocks[target];\n                targetBlock.col -= block.col;\n            }\n        }\n    }\n\n    // Calculate root positions. Typical function should have one root node that matches with\n    // entrypoint. There can be more of them in case of switch statement analysis failure,\n    // unreahable basic blocks or using the algorithm for non control flow graphs.\n    int nextEmptyColumn = 0;\n    for (auto &blockIt : layoutState.grid_blocks) {\n        auto &block = blockIt.second;\n        if (block.row == 0) { // place all the roots first\n            auto offset = -block.leftPosition;\n            block.col += nextEmptyColumn + offset;\n            nextEmptyColumn = block.rightPosition + offset + nextEmptyColumn;\n        }\n    }\n    // Visit all nodes top to bottom, converting relative positions to absolute.\n    for (auto it = blockOrder.rbegin(), end = blockOrder.rend(); it != end; it++) {\n        auto &block = layoutState.grid_blocks[*it];\n        assert(block.col >= 0);\n        for (auto childId : block.tree_edge) {\n            auto &childBlock = layoutState.grid_blocks[childId];\n            childBlock.col += block.col;\n        }\n    }\n}\n\nvoid GraphGridLayout::routeEdges(GraphGridLayout::LayoutState &state) const\n{\n    calculateEdgeMainColumn(state);\n    roughRouting(state);\n    elaborateEdgePlacement(state);\n}\n\nvoid GraphGridLayout::calculateEdgeMainColumn(GraphGridLayout::LayoutState &state) const\n{\n    // Find an empty column as close as possible to start or end block's column.\n    // Use sweep line approach processing events sorted by row top to bottom. Use an appropriate\n    // tree structure to contain blocks above sweep line and query for nearest column which isn't\n    // blocked by a block.\n\n    struct Event\n    {\n        ut64 blockId;\n        size_t edgeId;\n        int row;\n        enum Type { Edge = 0, Block = 1 } type;\n    };\n    // create events\n    std::vector<Event> events;\n    events.reserve(state.grid_blocks.size() * 2);\n    for (const auto &it : state.grid_blocks) {\n        events.push_back({ it.first, 0, it.second.row, Event::Block });\n        const auto &inputBlock = (*state.blocks)[it.first];\n        int startRow = it.second.row + 1;\n\n        auto gridEdges = state.edge[it.first];\n        gridEdges.resize(inputBlock.edges.size());\n        for (size_t i = 0; i < inputBlock.edges.size(); i++) {\n            auto targetId = inputBlock.edges[i].target;\n            gridEdges[i].dest = targetId;\n            const auto &targetGridBlock = state.grid_blocks[targetId];\n            int endRow = targetGridBlock.row;\n            events.push_back({ it.first, i, std::max(startRow, endRow), Event::Edge });\n        }\n    }\n    std::sort(events.begin(), events.end(), [](const Event &a, const Event &b) {\n        if (a.row != b.row) {\n            return a.row < b.row;\n        }\n        return static_cast<int>(a.type) < static_cast<int>(b.type);\n    });\n\n    // process events and choose main column for each edge\n    PointSetMinTree blockedColumns(state.columns + 1, -1);\n    for (const auto &event : events) {\n        if (event.type == Event::Block) {\n            auto block = state.grid_blocks[event.blockId];\n            blockedColumns.set(block.col + 1, event.row);\n        } else {\n            auto block = state.grid_blocks[event.blockId];\n            int column = block.col + 1;\n            auto &edge = state.edge[event.blockId][event.edgeId];\n            const auto &targetBlock = state.grid_blocks[edge.dest];\n            auto topRow = std::min(block.row + 1, targetBlock.row);\n            auto targetColumn = targetBlock.col + 1;\n\n            // Prefer using the same column as starting node, it allows reducing amount of segments.\n            if (blockedColumns.valueAtPoint(column) < topRow) {\n                edge.mainColumn = column;\n            } else if (blockedColumns.valueAtPoint(targetColumn)\n                       < topRow) { // next try target block column\n                edge.mainColumn = targetColumn;\n            } else {\n                auto nearestLeft = blockedColumns.rightMostLessThan(column, topRow);\n                auto nearestRight = blockedColumns.leftMostLessThan(column, topRow);\n                // There should always be empty column at the sides of drawing\n                assert(nearestLeft != -1 && nearestRight != -1);\n\n                // Choose closest column. Take into account distance to source and target block\n                // columns.\n                auto distanceLeft = column - nearestLeft + abs(targetColumn - nearestLeft);\n                auto distanceRight = nearestRight - column + abs(targetColumn - nearestRight);\n\n                // For upward edges try to make a loop instead of 8 shape,\n                // it is slightly longer but produces less crossing.\n                if (targetBlock.row < block.row) {\n                    if (targetColumn < column && blockedColumns.valueAtPoint(column + 1) < topRow\n                        && column - targetColumn <= distanceLeft + 2) {\n                        edge.mainColumn = column + 1;\n                        continue;\n                    } else if (targetColumn > column\n                               && blockedColumns.valueAtPoint(column - 1) < topRow\n                               && targetColumn - column <= distanceRight + 2) {\n                        edge.mainColumn = column - 1;\n                        continue;\n                    }\n                }\n\n                if (distanceLeft != distanceRight) {\n                    edge.mainColumn = distanceLeft < distanceRight ? nearestLeft : nearestRight;\n                } else {\n                    // In case of tie choose based on edge index. Should result in true branches\n                    // being mostly on one side, false branches on other side.\n                    edge.mainColumn = event.edgeId < state.edge[event.blockId].size() / 2\n                            ? nearestLeft\n                            : nearestRight;\n                }\n            }\n        }\n    }\n}\n\nvoid GraphGridLayout::roughRouting(GraphGridLayout::LayoutState &state) const\n{\n    auto getSpacingOverride = [this](int blockWidth, int edgeCount) {\n        if (edgeCount == 0) {\n            return 0;\n        }\n        int maxSpacing = blockWidth / edgeCount;\n        if (maxSpacing < layoutConfig.edgeHorizontalSpacing) {\n            return std::max(maxSpacing, 1);\n        }\n        return 0;\n    };\n\n    for (auto &blockIt : state.grid_blocks) {\n        auto &blockEdges = state.edge[blockIt.first];\n        for (size_t i = 0; i < blockEdges.size(); i++) {\n            auto &edge = blockEdges[i];\n            const auto &start = blockIt.second;\n            const auto &target = state.grid_blocks[edge.dest];\n\n            edge.addPoint(start.row + 1, start.col + 1);\n            if (edge.mainColumn != start.col + 1) {\n                edge.addPoint(start.row + 1, start.col + 1,\n                              edge.mainColumn < start.col + 1 ? -1 : 1);\n                edge.addPoint(start.row + 1, edge.mainColumn, target.row <= start.row ? -2 : 0);\n            }\n            int mainColumnKind = 0;\n            if (edge.mainColumn < start.col + 1 && edge.mainColumn < target.col + 1) {\n                mainColumnKind = +2;\n            } else if (edge.mainColumn > start.col + 1 && edge.mainColumn > target.col + 1) {\n                mainColumnKind = -2;\n            } else if (edge.mainColumn == start.col + 1 && edge.mainColumn != target.col + 1) {\n                mainColumnKind = edge.mainColumn < target.col + 1 ? 1 : -1;\n            } else if (edge.mainColumn == target.col + 1 && edge.mainColumn != start.col + 1) {\n                mainColumnKind = edge.mainColumn < start.col + 1 ? 1 : -1;\n            }\n            edge.addPoint(target.row, edge.mainColumn, mainColumnKind);\n            if (target.col + 1 != edge.mainColumn) {\n                edge.addPoint(target.row, target.col + 1, target.row <= start.row ? 2 : 0);\n                edge.addPoint(target.row, target.col + 1,\n                              target.col + 1 < edge.mainColumn ? 1 : -1);\n            }\n\n            // reduce edge spacing when there is large amount of edges connected to single block\n            auto startSpacingOverride =\n                    getSpacingOverride((*state.blocks)[start.id].width, start.outputCount);\n            auto targetSpacingOverride =\n                    getSpacingOverride((*state.blocks)[target.id].width, target.inputCount);\n            edge.points.front().spacingOverride = startSpacingOverride;\n            edge.points.back().spacingOverride = targetSpacingOverride;\n            if (edge.points.size() <= 2) {\n                if (startSpacingOverride && startSpacingOverride < targetSpacingOverride) {\n                    edge.points.back().spacingOverride = startSpacingOverride;\n                }\n            } else {\n                edge.points[1].spacingOverride = startSpacingOverride;\n            }\n\n            int length = 0;\n            for (size_t i = 1; i < edge.points.size(); i++) {\n                length += abs(edge.points[i].row - edge.points[i - 1].row)\n                        + abs(edge.points[i].col - edge.points[i - 1].col);\n            }\n            edge.secondaryPriority = 2 * length + (target.row >= start.row ? 1 : 0);\n        }\n    }\n}\n\nnamespace {\n/**\n * @brief Single segment of an edge. An edge can be drawn using multiple horizontal and vertical\n * segments. x y meaning matches vertical segments. For horizontal segments axis are swapped.\n */\nstruct EdgeSegment\n{\n    int y0;\n    int y1;\n    int x;\n    int edgeIndex;\n    int secondaryPriority;\n    int16_t kind;\n    int16_t spacingOverride; //< segment spacing override, 0 if default spacing should be used\n};\nstruct NodeSide\n{\n    int x;\n    int y0;\n    int y1;\n    int size; //< block size in the x axis direction\n};\n}\n\n/**\n * @brief Calculate segment offsets relative to their column\n *\n * Argument naming uses terms for vertical segments, but the function can be used for horizontal\n * segments as well.\n *\n * @param segments Segments that need to be processed.\n * @param edgeOffsets Output argument for returning segment offsets relative to their columns.\n * @param edgeColumnWidth InOut argument describing how much column with edges take. Initial value\n * used as minimal value. May be increased to depending on amount of segments in each column and how\n * tightly they are packed.\n * @param nodeRightSide Right side of nodes. Used to reduce space reserved for edges by placing them\n * between nodes.\n * @param nodeLeftSide Same as right side.\n * @param columnWidth\n * @param H All the segmement and node coordinates y0 and y1 are expected to be in range [0;H)\n * @param segmentSpacing The expected spacing between two segments in the same column. Actual\n * spacing may be smaller for nodes with many edges.\n */\nvoid calculateSegmentOffsets(std::vector<EdgeSegment> &segments, std::vector<int> &edgeOffsets,\n                             std::vector<int> &edgeColumnWidth,\n                             std::vector<NodeSide> &nodeRightSide,\n                             std::vector<NodeSide> &nodeLeftSide,\n                             const std::vector<int> &columnWidth, size_t H, int segmentSpacing)\n{\n    for (auto &segment : segments) {\n        if (segment.y0 > segment.y1) {\n            std::swap(segment.y0, segment.y1);\n        }\n    }\n\n    std::sort(segments.begin(), segments.end(), [](const EdgeSegment &a, const EdgeSegment &b) {\n        if (a.x != b.x)\n            return a.x < b.x;\n        if (a.kind != b.kind)\n            return a.kind < b.kind;\n        auto aSize = a.y1 - a.y0;\n        auto bSize = b.y1 - b.y0;\n        if (aSize != bSize) {\n            if (a.kind != 1) {\n                return aSize < bSize;\n            } else {\n                return aSize > bSize;\n            }\n        }\n        if (a.kind != 1) {\n            return a.secondaryPriority < b.secondaryPriority;\n        } else {\n            return a.secondaryPriority > b.secondaryPriority;\n        }\n        return false;\n    });\n\n    auto compareNode = [](const NodeSide &a, const NodeSide &b) { return a.x < b.x; };\n    sort(nodeRightSide.begin(), nodeRightSide.end(), compareNode);\n    sort(nodeLeftSide.begin(), nodeLeftSide.end(), compareNode);\n\n    RangeAssignMaxTree maxSegment(H, INT_MIN);\n    auto nextSegmentIt = segments.begin();\n    auto rightSideIt = nodeRightSide.begin();\n    auto leftSideIt = nodeLeftSide.begin();\n    while (nextSegmentIt != segments.end()) {\n        int x = nextSegmentIt->x;\n\n        int leftColumWidth = 0;\n        if (x > 0) {\n            leftColumWidth = columnWidth[x - 1];\n        }\n        maxSegment.setRange(0, H, -leftColumWidth);\n        while (rightSideIt != nodeRightSide.end() && rightSideIt->x + 1 < x) {\n            rightSideIt++;\n        }\n        while (rightSideIt != nodeRightSide.end() && rightSideIt->x + 1 == x) {\n            maxSegment.setRange(rightSideIt->y0, rightSideIt->y1 + 1,\n                                rightSideIt->size - leftColumWidth);\n            rightSideIt++;\n        }\n\n        while (nextSegmentIt != segments.end() && nextSegmentIt->x == x\n               && nextSegmentIt->kind <= 1) {\n            int y = maxSegment.rangeMaximum(nextSegmentIt->y0, nextSegmentIt->y1 + 1);\n            if (nextSegmentIt->kind != -2) {\n                y = std::max(y, 0);\n            }\n            y += nextSegmentIt->spacingOverride ? nextSegmentIt->spacingOverride : segmentSpacing;\n            maxSegment.setRange(nextSegmentIt->y0, nextSegmentIt->y1 + 1, y);\n            edgeOffsets[nextSegmentIt->edgeIndex] = y;\n            nextSegmentIt++;\n        }\n\n        auto firstRightSideSegment = nextSegmentIt;\n        auto middleWidth = std::max(maxSegment.rangeMaximum(0, H), 0);\n\n        int rightColumnWidth = 0;\n        if (x < static_cast<int>(columnWidth.size())) {\n            rightColumnWidth = columnWidth[x];\n        }\n\n        maxSegment.setRange(0, H, -rightColumnWidth);\n        while (leftSideIt != nodeLeftSide.end() && leftSideIt->x < x) {\n            leftSideIt++;\n        }\n        while (leftSideIt != nodeLeftSide.end() && leftSideIt->x == x) {\n            maxSegment.setRange(leftSideIt->y0, leftSideIt->y1 + 1,\n                                leftSideIt->size - rightColumnWidth);\n            leftSideIt++;\n        }\n        while (nextSegmentIt != segments.end() && nextSegmentIt->x == x) {\n            int y = maxSegment.rangeMaximum(nextSegmentIt->y0, nextSegmentIt->y1 + 1);\n            y += nextSegmentIt->spacingOverride ? nextSegmentIt->spacingOverride : segmentSpacing;\n            maxSegment.setRange(nextSegmentIt->y0, nextSegmentIt->y1 + 1, y);\n            edgeOffsets[nextSegmentIt->edgeIndex] = y;\n            nextSegmentIt++;\n        }\n        auto rightSideMiddle = std::max(maxSegment.rangeMaximum(0, H), 0);\n        rightSideMiddle =\n                std::max(rightSideMiddle, edgeColumnWidth[x] - middleWidth - segmentSpacing);\n        for (auto it = firstRightSideSegment; it != nextSegmentIt; ++it) {\n            edgeOffsets[it->edgeIndex] =\n                    middleWidth + (rightSideMiddle - edgeOffsets[it->edgeIndex]) + segmentSpacing;\n        }\n        edgeColumnWidth[x] = middleWidth + segmentSpacing + rightSideMiddle;\n    }\n}\n\n/**\n * @brief Center the segments to the middle of edge columns when possible.\n * @param segmentOffsets offsets relative to the left side edge column.\n * @param edgeColumnWidth widths of edge columns\n * @param segments either all horizontal or all vertical edge segments\n */\nstatic void centerEdges(std::vector<int> &segmentOffsets, const std::vector<int> &edgeColumnWidth,\n                        const std::vector<EdgeSegment> &segments)\n{\n    /* Split segments in each edge column into non intersecting chunks. Center each chunk\n     * separately.\n     *\n     * Process segment endpoints sorted by x and y. Maintain count of currently started segments.\n     * When number of active segments reaches 0 there is empty space between chunks.\n     */\n    struct Event\n    {\n        int x;\n        int y;\n        int index;\n        bool start;\n    };\n    std::vector<Event> events;\n    events.reserve(segments.size() * 2);\n    for (const auto &segment : segments) {\n        auto offset = segmentOffsets[segment.edgeIndex];\n        // Exclude segments which are outside edge column and between the blocks. It's hard to\n        // ensure that moving them doesn't cause overlap with blocks.\n        if (offset >= 0 && offset <= edgeColumnWidth[segment.x]) {\n            events.push_back({ segment.x, segment.y0, segment.edgeIndex, true });\n            events.push_back({ segment.x, segment.y1, segment.edgeIndex, false });\n        }\n    }\n    std::sort(events.begin(), events.end(), [](const Event &a, const Event &b) {\n        if (a.x != b.x)\n            return a.x < b.x;\n        if (a.y != b.y)\n            return a.y < b.y;\n        // Process segment start events before end to ensure that activeSegmentCount doesn't go\n        // negative and only reaches 0 at the end of chunk.\n        return int(a.start) > int(b.start);\n    });\n\n    auto it = events.begin();\n    while (it != events.end()) {\n        int left, right;\n        left = right = segmentOffsets[it->index];\n        auto chunkStart = it++;\n        int activeSegmentCount = 1;\n\n        while (activeSegmentCount > 0) {\n            activeSegmentCount += it->start ? 1 : -1;\n            int offset = segmentOffsets[it->index];\n            left = std::min(left, offset);\n            right = std::max(right, offset);\n            it++;\n        }\n        int spacing = (edgeColumnWidth[chunkStart->x] - (right - left)) / 2 - left;\n        for (auto segment = chunkStart; segment != it; segment++) {\n            if (segment->start) {\n                segmentOffsets[segment->index] += spacing;\n            }\n        }\n    }\n}\n\n/**\n * @brief Convert segment coordinates from arbitrary range to continuous range starting at 0.\n * @param segments\n * @param leftSides\n * @param rightSides\n * @return Size of compressed coordinate range.\n */\nstatic int compressCoordinates(std::vector<EdgeSegment> &segments, std::vector<NodeSide> &leftSides,\n                               std::vector<NodeSide> &rightSides)\n{\n    std::vector<int> positions;\n    positions.reserve((segments.size() + leftSides.size()) * 2);\n    for (const auto &segment : segments) {\n        positions.push_back(segment.y0);\n        positions.push_back(segment.y1);\n    }\n    for (const auto &segment : leftSides) {\n        positions.push_back(segment.y0);\n        positions.push_back(segment.y1);\n    }\n    // y0 and y1 in rightSides should match leftSides\n\n    std::sort(positions.begin(), positions.end());\n    auto lastUnique = std::unique(positions.begin(), positions.end());\n    positions.erase(lastUnique, positions.end());\n\n    auto positionToIndex = [&](int position) {\n        size_t index =\n                std::lower_bound(positions.begin(), positions.end(), position) - positions.begin();\n        assert(index < positions.size());\n        return index;\n    };\n    for (auto &segment : segments) {\n        segment.y0 = positionToIndex(segment.y0);\n        segment.y1 = positionToIndex(segment.y1);\n    }\n    assert(leftSides.size() == rightSides.size());\n    for (size_t i = 0; i < leftSides.size(); i++) {\n        leftSides[i].y0 = rightSides[i].y0 = positionToIndex(leftSides[i].y0);\n        leftSides[i].y1 = rightSides[i].y1 = positionToIndex(leftSides[i].y1);\n    }\n    return positions.size();\n}\n\nvoid GraphGridLayout::elaborateEdgePlacement(GraphGridLayout::LayoutState &state) const\n{\n    int edgeIndex = 0;\n\n    auto segmentFromPoint = [&edgeIndex](const Point &point, const GridEdge &edge, int y0, int y1,\n                                         int x) {\n        EdgeSegment segment;\n        segment.y0 = y0;\n        segment.y1 = y1;\n        segment.x = x;\n        segment.edgeIndex = edgeIndex++;\n        segment.kind = point.kind;\n        segment.spacingOverride = point.spacingOverride;\n        segment.secondaryPriority = edge.secondaryPriority;\n        return segment;\n    };\n\n    std::vector<EdgeSegment> segments;\n    std::vector<NodeSide> rightSides;\n    std::vector<NodeSide> leftSides;\n    std::vector<int> edgeOffsets;\n\n    // Vertical segments\n    for (auto &edgeListIt : state.edge) {\n        for (const auto &edge : edgeListIt.second) {\n            for (size_t j = 1; j < edge.points.size(); j += 2) {\n                segments.push_back(\n                        segmentFromPoint(edge.points[j], edge,\n                                         edge.points[j - 1].row * 2, // edges in even rows\n                                         edge.points[j].row * 2, edge.points[j].col));\n            }\n        }\n    }\n    for (auto &blockIt : state.grid_blocks) {\n        auto &node = blockIt.second;\n        auto width = (*state.blocks)[blockIt.first].width;\n        auto leftWidth = width / 2;\n        // not the same as leftWidth, you would think that one pixel offset isn't visible, but it is\n        auto rightWidth = width - leftWidth;\n        int row = node.row * 2 + 1; // blocks in odd rows\n        leftSides.push_back({ node.col, row, row, leftWidth });\n        rightSides.push_back({ node.col + 1, row, row, rightWidth });\n    }\n    state.edgeColumnWidth.assign(state.columns + 1, layoutConfig.blockHorizontalSpacing);\n    state.edgeColumnWidth[0] = state.edgeColumnWidth.back() = layoutConfig.edgeHorizontalSpacing;\n    edgeOffsets.resize(edgeIndex);\n    calculateSegmentOffsets(segments, edgeOffsets, state.edgeColumnWidth, rightSides, leftSides,\n                            state.columnWidth, 2 * state.rows + 1,\n                            layoutConfig.edgeHorizontalSpacing);\n    centerEdges(edgeOffsets, state.edgeColumnWidth, segments);\n    edgeIndex = 0;\n\n    auto copySegmentsToEdges = [&](bool col) {\n        int edgeIndex = 0;\n        for (auto &edgeListIt : state.edge) {\n            for (auto &edge : edgeListIt.second) {\n                for (size_t j = col ? 1 : 2; j < edge.points.size(); j += 2) {\n                    int offset = edgeOffsets[edgeIndex++];\n                    if (col) {\n                        GraphBlock *block = nullptr;\n                        if (j == 1) {\n                            block = &(*state.blocks)[edgeListIt.first];\n                        } else if (j + 1 == edge.points.size()) {\n                            block = &(*state.blocks)[edge.dest];\n                        }\n                        if (block) {\n                            int blockWidth = block->width;\n                            int edgeColumWidth = state.edgeColumnWidth[edge.points[j].col];\n                            offset = std::max(-blockWidth / 2 + edgeColumWidth / 2, offset);\n                            offset = std::min(edgeColumWidth / 2\n                                                      + std::min(blockWidth, edgeColumWidth) / 2,\n                                              offset);\n                        }\n                    }\n                    edge.points[j].offset = offset;\n                }\n            }\n        }\n    };\n    auto oldColumnWidths = state.columnWidth;\n    adjustColumnWidths(state);\n    for (auto &segment : segments) {\n        auto &offset = edgeOffsets[segment.edgeIndex];\n        if (segment.kind == -2) {\n            offset -= (state.edgeColumnWidth[segment.x - 1] / 2 + state.columnWidth[segment.x - 1])\n                    - oldColumnWidths[segment.x - 1];\n        } else if (segment.kind == 2) {\n            offset += (state.edgeColumnWidth[segment.x + 1] / 2 + state.columnWidth[segment.x])\n                    - oldColumnWidths[segment.x];\n        }\n    }\n    calculateColumnOffsets(state.columnWidth, state.edgeColumnWidth, state.columnOffset,\n                           state.edgeColumnOffset);\n    copySegmentsToEdges(true);\n\n    // Horizontal segments\n    // Use exact x coordinates obtained from vertical segment placement.\n    segments.clear();\n    leftSides.clear();\n    rightSides.clear();\n\n    edgeIndex = 0;\n    for (auto &edgeListIt : state.edge) {\n        for (const auto &edge : edgeListIt.second) {\n            for (size_t j = 2; j < edge.points.size(); j += 2) {\n                int y0 = state.edgeColumnOffset[edge.points[j - 1].col] + edge.points[j - 1].offset;\n                int y1 = state.edgeColumnOffset[edge.points[j + 1].col] + edge.points[j + 1].offset;\n                segments.push_back(\n                        segmentFromPoint(edge.points[j], edge, y0, y1, edge.points[j].row));\n            }\n        }\n    }\n    edgeOffsets.resize(edgeIndex);\n    for (auto &blockIt : state.grid_blocks) {\n        auto &node = blockIt.second;\n        auto blockWidth = (*state.blocks)[node.id].width;\n        int leftSide = state.edgeColumnOffset[node.col + 1]\n                + state.edgeColumnWidth[node.col + 1] / 2 - blockWidth / 2;\n        int rightSide = leftSide + blockWidth;\n\n        int h = (*state.blocks)[blockIt.first].height;\n        int freeSpace = state.rowHeight[node.row] - h;\n        int topProfile = state.rowHeight[node.row];\n        int bottomProfile = h;\n        if (verticalBlockAlignmentMiddle) {\n            topProfile -= freeSpace / 2;\n            bottomProfile += freeSpace / 2;\n        }\n        leftSides.push_back({ node.row, leftSide, rightSide, topProfile });\n        rightSides.push_back({ node.row, leftSide, rightSide, bottomProfile });\n    }\n    state.edgeRowHeight.assign(state.rows + 1, layoutConfig.blockVerticalSpacing);\n    state.edgeRowHeight[0] = state.edgeRowHeight.back() = layoutConfig.edgeVerticalSpacing;\n    edgeOffsets.resize(edgeIndex);\n    auto compressedCoordinates = compressCoordinates(segments, leftSides, rightSides);\n    calculateSegmentOffsets(segments, edgeOffsets, state.edgeRowHeight, rightSides, leftSides,\n                            state.rowHeight, compressedCoordinates,\n                            layoutConfig.edgeVerticalSpacing);\n    copySegmentsToEdges(false);\n}\n\nvoid GraphGridLayout::adjustColumnWidths(GraphGridLayout::LayoutState &state) const\n{\n    state.rowHeight.assign(state.rows, 0);\n    state.columnWidth.assign(state.columns, 0);\n    for (auto &node : state.grid_blocks) {\n        const auto &inputBlock = (*state.blocks)[node.first];\n        state.rowHeight[node.second.row] =\n                std::max(inputBlock.height, state.rowHeight[node.second.row]);\n        int edgeWidth = state.edgeColumnWidth[node.second.col + 1];\n        int columnWidth = (inputBlock.width - edgeWidth) / 2;\n        state.columnWidth[node.second.col] =\n                std::max(columnWidth, state.columnWidth[node.second.col]);\n        state.columnWidth[node.second.col + 1] =\n                std::max(columnWidth, state.columnWidth[node.second.col + 1]);\n    }\n}\n\nint GraphGridLayout::calculateColumnOffsets(const std::vector<int> &columnWidth,\n                                            std::vector<int> &edgeColumnWidth,\n                                            std::vector<int> &columnOffset,\n                                            std::vector<int> &edgeColumnOffset)\n{\n    assert(edgeColumnWidth.size() == columnWidth.size() + 1);\n    int position = 0;\n    edgeColumnOffset.resize(edgeColumnWidth.size());\n    columnOffset.resize(columnWidth.size());\n    for (size_t i = 0; i < columnWidth.size(); i++) {\n        edgeColumnOffset[i] = position;\n        position += edgeColumnWidth[i];\n        columnOffset[i] = position;\n        position += columnWidth[i];\n    }\n    edgeColumnOffset.back() = position;\n    position += edgeColumnWidth.back();\n    return position;\n}\n\nvoid GraphGridLayout::convertToPixelCoordinates(GraphGridLayout::LayoutState &state, int &width,\n                                                int &height) const\n{\n    // calculate row and column offsets\n    width = calculateColumnOffsets(state.columnWidth, state.edgeColumnWidth, state.columnOffset,\n                                   state.edgeColumnOffset);\n    height = calculateColumnOffsets(state.rowHeight, state.edgeRowHeight, state.rowOffset,\n                                    state.edgeRowOffset);\n\n    // block pixel positions\n    for (auto &block : (*state.blocks)) {\n        const auto &gridBlock = state.grid_blocks[block.first];\n\n        block.second.x = state.edgeColumnOffset[gridBlock.col + 1]\n                + state.edgeColumnWidth[gridBlock.col + 1] / 2 - block.second.width / 2;\n        block.second.y = state.rowOffset[gridBlock.row];\n        if (verticalBlockAlignmentMiddle) {\n            block.second.y += (state.rowHeight[gridBlock.row] - block.second.height) / 2;\n        }\n    }\n    // edge pixel positions\n    for (auto &it : (*state.blocks)) {\n        auto &block = it.second;\n        for (size_t i = 0; i < block.edges.size(); i++) {\n            auto &resultEdge = block.edges[i];\n            resultEdge.polyline.clear();\n            resultEdge.polyline.push_back(QPointF(0, block.y + block.height));\n\n            const auto &edge = state.edge[it.first][i];\n            for (size_t j = 1; j < edge.points.size(); j++) {\n                if (j & 1) { // vertical segment\n                    int column = edge.points[j].col;\n                    int x = state.edgeColumnOffset[column] + edge.points[j].offset;\n                    resultEdge.polyline.back().setX(x);\n                    resultEdge.polyline.push_back(QPointF(x, 0));\n                } else { // horizontal segment\n                    int row = edge.points[j].row;\n                    int y = state.edgeRowOffset[row] + edge.points[j].offset;\n                    resultEdge.polyline.back().setY(y);\n                    resultEdge.polyline.push_back(QPointF(0, y));\n                }\n            }\n        }\n    }\n    connectEdgeEnds(*state.blocks);\n}\n\nvoid GraphGridLayout::cropToContent(GraphLayout::Graph &graph, int &width, int &height) const\n{\n    if (graph.empty()) {\n        width = std::max(1, layoutConfig.edgeHorizontalSpacing);\n        height = std::max(1, layoutConfig.edgeVerticalSpacing);\n        return;\n    }\n    const auto &anyBlock = graph.begin()->second;\n    int minPos[2] = { anyBlock.x, anyBlock.y };\n    int maxPos[2] = { anyBlock.x, anyBlock.y };\n\n    auto updateLimits = [&](int x, int y) {\n        minPos[0] = std::min(minPos[0], x);\n        minPos[1] = std::min(minPos[1], y);\n        maxPos[0] = std::max(maxPos[0], x);\n        maxPos[1] = std::max(maxPos[1], y);\n    };\n\n    for (const auto &blockIt : graph) {\n        auto &block = blockIt.second;\n        updateLimits(block.x, block.y);\n        updateLimits(block.x + block.width, block.y + block.height);\n        for (auto &edge : block.edges) {\n            for (auto &point : edge.polyline) {\n                updateLimits(point.x(), point.y());\n            }\n        }\n    }\n    minPos[0] -= layoutConfig.edgeHorizontalSpacing;\n    minPos[1] -= layoutConfig.edgeVerticalSpacing;\n    maxPos[0] += layoutConfig.edgeHorizontalSpacing;\n    maxPos[1] += layoutConfig.edgeVerticalSpacing;\n    for (auto &blockIt : graph) {\n        auto &block = blockIt.second;\n        block.x -= minPos[0];\n        block.y -= minPos[1];\n        for (auto &edge : block.edges) {\n            for (auto &point : edge.polyline) {\n                point -= QPointF(minPos[0], minPos[1]);\n            }\n        }\n    }\n    width = maxPos[0] - minPos[0];\n    height = maxPos[1] - minPos[1];\n}\n\nvoid GraphGridLayout::connectEdgeEnds(GraphLayout::Graph &graph) const\n{\n    for (auto &it : graph) {\n        auto &block = it.second;\n        for (size_t i = 0; i < block.edges.size(); i++) {\n            auto &resultEdge = block.edges[i];\n            const auto &target = graph[resultEdge.target];\n            resultEdge.polyline[0].ry() = block.y + block.height;\n            resultEdge.polyline.back().ry() = target.y;\n        }\n    }\n}\n\n/// Either equality or inequality x_i <= x_j + c\nusing Constraint = std::pair<std::pair<int, int>, int>;\n\n/**\n * @brief Single pass of linear program optimizer.\n * Changes variables until a constraint is hit, afterwards the two variables are changed together.\n * @param n number of variables\n * @param objectiveFunction coefficients for function \\f$\\sum c_i x_i\\f$ which needs to be minimized\n * @param inequalities inequality constraints \\f$x_{e_i} - x_{f_i} \\leq b_i\\f$\n * @param equalities equality constraints \\f$x_{e_i} - x_{f_i} = b_i\\f$\n * @param solution input/output argument, returns results, needs to be initialized with a feasible\n * solution\n * @param stickWhenNotMoving variable grouping strategy\n */\nstatic void optimizeLinearProgramPass(size_t n, std::vector<int> objectiveFunction,\n                                      std::vector<Constraint> inequalities,\n                                      std::vector<Constraint> equalities,\n                                      std::vector<int> &solution, bool stickWhenNotMoving)\n{\n    std::vector<int> group(n);\n    std::iota(group.begin(), group.end(), 0); // initially each variable is in it's own group\n    assert(n == objectiveFunction.size());\n    assert(n == solution.size());\n    std::vector<size_t> edgeCount(n);\n\n    LinkedListPool<size_t> edgePool(inequalities.size() * 2);\n\n    std::vector<decltype(edgePool)::List> edges(n);\n\n    auto getGroup = [&](int v) {\n        while (group[v] != v) {\n            group[v] = group[group[v]];\n            v = group[v];\n        }\n        return v;\n    };\n    auto joinGroup = [&](int a, int b) { group[getGroup(b)] = getGroup(a); };\n\n    for (auto &constraint : inequalities) {\n        int a = constraint.first.first;\n        int b = constraint.first.second;\n        size_t index = &constraint - &inequalities.front();\n        edges[a] = edgePool.append(edges[a], edgePool.makeList(index));\n        edges[b] = edgePool.append(edges[b], edgePool.makeList(index));\n        edgeCount[a]++;\n        edgeCount[b]++;\n    }\n    std::vector<uint8_t> processed(n);\n    // Smallest variable value in the group relative to main one, this is used to maintain implicit\n    // x_i >= 0 constraint\n    std::vector<int> groupRelativeMin(n, 0);\n\n    auto joinSegmentGroups = [&](int a, int b) {\n        a = getGroup(a);\n        b = getGroup(b);\n        joinGroup(a, b);\n        edgeCount[a] += edgeCount[b];\n        objectiveFunction[a] += objectiveFunction[b];\n        int internalEdgeCount = 0;\n        auto writeIt = edgePool.head(edges[b]);\n        // update inequalities and remove some of the constraints between variables that are now\n        // grouped\n        for (auto it = edgePool.head(edges[b]); it; ++it) {\n            auto &constraint = inequalities[*it];\n            int other = constraint.first.first + constraint.first.second - b;\n            if (getGroup(other)\n                == a) { // skip inequalities where both variables are now in the same group\n                internalEdgeCount++;\n                continue;\n            }\n            *writeIt++ = *it;\n            // Modify the inequalities for the group being attached relative to the main variable in\n            // the group to which it is being attached.\n            int diff = solution[a] - solution[b];\n            if (b == constraint.first.first) {\n                constraint.first.first = a;\n                constraint.second += diff;\n            } else {\n                constraint.first.second = a;\n                constraint.second -= diff;\n            }\n        }\n        edges[a] = edgePool.append(edges[a], edgePool.splitHead(edges[b], writeIt));\n        edgeCount[a] -= internalEdgeCount;\n        groupRelativeMin[a] =\n                std::min(groupRelativeMin[a], groupRelativeMin[b] + solution[b] - solution[a]);\n    };\n\n    for (auto &equality : equalities) {\n        // process equalities, assumes that initial solution is feasible solution and matches\n        // equality constraints\n        int a = getGroup(equality.first.first);\n        int b = getGroup(equality.first.second);\n        if (a == b) {\n            equality = { { 0, 0 }, 0 };\n            continue;\n        }\n        // always join smallest group to bigger one\n        if (edgeCount[a] > edgeCount[b]) {\n            std::swap(a, b);\n            // Update the equality equation so that later variable values can be calculated by\n            // simply iterating through them without need to check which direction the group joining\n            // was done.\n            std::swap(equality.first.first, equality.first.second);\n            equality.second = -equality.second;\n        }\n        joinSegmentGroups(b, a);\n        equality = { { a, b }, solution[a] - solution[b] };\n        processed[a] = 1;\n    }\n\n    // Priority queue for processing groups starting with currently smallest one. Doing it this way\n    // should result in number of constraints within group doubling each time two groups are joined.\n    // That way each constraint is processed no more than log(n) times.\n    std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>,\n                        std::greater<std::pair<int, int>>>\n            queue;\n    for (size_t i = 0; i < n; i++) {\n        if (!processed[i]) {\n            queue.push({ edgeCount[i], i });\n        }\n    }\n    while (!queue.empty()) {\n        int g = queue.top().second;\n        int size = queue.top().first;\n        queue.pop();\n        if ((size_t)size != edgeCount[g] || processed[g]) {\n            continue;\n        }\n        int direction = objectiveFunction[g];\n        if (direction == 0) {\n            continue;\n        }\n        // Find the first constraint which will be hit by changing the variable in the desired\n        // direction defined by objective function.\n        int limitingGroup = -1;\n        int smallestMove = 0;\n        if (direction < 0) {\n            smallestMove = INT_MAX;\n            for (auto it = edgePool.head(edges[g]); it; ++it) {\n                auto &inequality = inequalities[*it];\n                if (g == inequality.first.second) {\n                    continue;\n                }\n                int other = inequality.first.second;\n                if (getGroup(other) == g) {\n                    continue;\n                }\n                int move = solution[other] + inequality.second - solution[g];\n                if (move < smallestMove) {\n                    smallestMove = move;\n                    limitingGroup = other;\n                }\n            }\n        } else {\n            smallestMove = -solution[g] - groupRelativeMin[g]; // keep all variables >= 0\n            for (auto it = edgePool.head(edges[g]); it; ++it) {\n                auto &inequality = inequalities[*it];\n                if (g == inequality.first.first) {\n                    continue;\n                }\n                int other = inequality.first.first;\n                if (getGroup(other) == g) {\n                    continue;\n                }\n                int move = solution[other] - inequality.second - solution[g];\n                if (move > smallestMove) {\n                    smallestMove = move;\n                    limitingGroup = other;\n                }\n            }\n        }\n        assert(smallestMove != INT_MAX);\n        if (smallestMove == INT_MAX) {\n            // Unbound variable, this means that linear program wasn't set up correctly.\n            // Better don't change it instead of stretching the graph to infinity.\n            smallestMove = 0;\n        }\n        solution[g] += smallestMove;\n        if (smallestMove == 0 && stickWhenNotMoving == false) {\n            continue;\n        }\n        processed[g] = 1;\n        if (limitingGroup != -1) {\n            joinSegmentGroups(limitingGroup, g);\n            if (!processed[limitingGroup]) {\n                queue.push({ edgeCount[limitingGroup], limitingGroup });\n            }\n            equalities.push_back({ { g, limitingGroup }, solution[g] - solution[limitingGroup] });\n        } // else do nothing if limited by variable >= 0\n    }\n    for (auto it = equalities.rbegin(), end = equalities.rend(); it != end; ++it) {\n        solution[it->first.first] = solution[it->first.second] + it->second;\n    }\n}\n\n/**\n * @brief Linear programming solver\n * Does not guarantee optimal solution.\n * @param n number of variables\n * @param objectiveFunction coefficients for function \\f$\\sum c_i x_i\\f$ which needs to be minimized\n * @param inequalities inequality constraints \\f$x_{e_i} - x_{f_i} \\leq b_i\\f$\n * @param equalities equality constraints \\f$x_{e_i} - x_{f_i} = b_i\\f$\n * @param solution input/output argument, returns results, needs to be initialized with a feasible\n * solution\n */\nstatic void optimizeLinearProgram(size_t n, const std::vector<int> &objectiveFunction,\n                                  std::vector<Constraint> inequalities,\n                                  const std::vector<Constraint> &equalities,\n                                  std::vector<int> &solution)\n{\n    // Remove redundant inequalities\n    std::sort(inequalities.begin(), inequalities.end());\n    auto uniqueEnd = std::unique(\n            inequalities.begin(), inequalities.end(),\n            [](const Constraint &a, const Constraint &b) { return a.first == b.first; });\n    inequalities.erase(uniqueEnd, inequalities.end());\n\n    static const int ITERATIONS = 1;\n    for (int i = 0; i < ITERATIONS; i++) {\n        optimizeLinearProgramPass(n, objectiveFunction, inequalities, equalities, solution, true);\n        // optimizeLinearProgramPass(n, objectiveFunction, inequalities, equalities, solution,\n        // false);\n    }\n}\n\nnamespace {\nstruct Segment\n{\n    int x;\n    int variableId;\n    int y0, y1;\n};\n}\n\nstatic Constraint createInequality(size_t a, int posA, size_t b, int posB, int minSpacing,\n                                   const std::vector<int> &positions)\n{\n    minSpacing = std::min(minSpacing, posB - posA);\n    return { { a, b }, posB - positions[b] - (posA - positions[a]) - minSpacing };\n}\n\n/**\n * @brief Create inequality constraints from segments which preserves their relative order on single\n * axis.\n *\n * @param segments list of edge segments and block sides\n * @param positions initial element positions before optimization\n * @param blockCount number of variables representing blocks, it is assumed that segments with\n * variableId < \\a blockCount represent one side of block.\n * @param variableGroup used to check if segments are part of the same edge and spacing can be\n * reduced\n * @param blockSpacing minimal spacing between blocks\n * @param segmentSpacing minimal spacing between two edge segments, spacing may be less if values in\n * \\a positions are closer than this\n * @param inequalities output variable for resulting inequalities, values initially stored in it are\n * not removed\n */\nstatic void createInequalitiesFromSegments(std::vector<Segment> segments,\n                                           const std::vector<int> &positions,\n                                           const std::vector<size_t> &variableGroup, int blockCount,\n                                           int blockSpacing, int segmentSpacing,\n                                           std::vector<Constraint> &inequalities)\n{\n    // map used as binary search tree y_position -> segment{variableId, x_position}\n    // It is used to maintain which segment was last seen in the range y_position..\n    std::map<int, std::pair<int, int>> lastSegments;\n    lastSegments[-1] = { -1, -1 };\n\n    std::sort(segments.begin(), segments.end(),\n              [](const Segment &a, const Segment &b) { return a.x < b.x; });\n    for (auto &segment : segments) {\n        auto startPos = lastSegments.lower_bound(segment.y0);\n        --startPos; // should never be lastSegment.begin() because map is initialized with segment\n                    // at pos -1\n        auto lastSegment = startPos->second;\n        auto it = startPos;\n        while (it != lastSegments.end() && it->first <= segment.y1) {\n            int prevSegmentVariable = it->second.first;\n            int prevSegmentPos = it->second.second;\n            if (prevSegmentVariable != -1) {\n                int minSpacing = segmentSpacing;\n                if (prevSegmentVariable < blockCount && segment.variableId < blockCount) {\n                    // no need to add inequality between two sides of block\n                    if (prevSegmentVariable == segment.variableId) {\n                        ++it;\n                        continue;\n                    }\n                    minSpacing = blockSpacing;\n                } else if (variableGroup[prevSegmentVariable]\n                           == variableGroup[segment.variableId]) {\n                    minSpacing = 0;\n                }\n                inequalities.push_back(createInequality(prevSegmentVariable, prevSegmentPos,\n                                                        segment.variableId, segment.x, minSpacing,\n                                                        positions));\n            }\n            lastSegment = it->second;\n            ++it;\n        }\n        if (startPos->first < segment.y0) {\n            startPos++;\n        }\n        lastSegments.erase(startPos, it); // erase segments covered by current one\n        lastSegments[segment.y0] = {\n            segment.variableId, segment.x\n        }; // current segment\n           // either current segment splitting previous one into two parts or remaining part of\n           // partially covered segment\n        lastSegments[segment.y1] = lastSegment;\n    }\n}\n\nvoid GraphGridLayout::optimizeLayout(GraphGridLayout::LayoutState &state) const\n{\n    std::unordered_map<uint64_t, int> blockMapping;\n    size_t blockIndex = 0;\n    for (auto &blockIt : *state.blocks) {\n        blockMapping[blockIt.first] = blockIndex++;\n    }\n    std::vector<size_t> variableGroups(blockMapping.size());\n    std::iota(variableGroups.begin(), variableGroups.end(), 0);\n\n    std::vector<int> objectiveFunction;\n    std::vector<Constraint> inequalities;\n    std::vector<Constraint> equalities;\n    std::vector<int> solution;\n\n    auto addObjective = [&](size_t a, int posA, size_t b, int posB) {\n        objectiveFunction.resize(std::max(objectiveFunction.size(), std::max(a, b) + 1));\n        if (posA < posB) {\n            objectiveFunction[b] += 1;\n            objectiveFunction[a] -= 1;\n        } else {\n            objectiveFunction[a] += 1;\n            objectiveFunction[b] -= 1;\n        }\n    };\n    auto addInequality = [&](size_t a, int posA, size_t b, int posB, int minSpacing) {\n        inequalities.push_back(createInequality(a, posA, b, posB, minSpacing, solution));\n    };\n    auto addBlockSegmentEquality = [&](ut64 blockId, int edgeVariable, int edgeVariablePos) {\n        int blockPos = (*state.blocks)[blockId].x;\n        int blockVariable = blockMapping[blockId];\n        equalities.push_back({ { blockVariable, edgeVariable }, blockPos - edgeVariablePos });\n    };\n    auto setFeasibleSolution = [&](size_t variable, int value) {\n        solution.resize(std::max(solution.size(), variable + 1));\n        solution[variable] = value;\n    };\n\n    auto copyVariablesToPositions = [&](const std::vector<int> &solution, bool horizontal = false) {\n#ifndef NDEBUG\n        for (auto v : solution) {\n            assert(v >= 0);\n        }\n#endif\n        size_t variableIndex = blockMapping.size();\n        for (auto &blockIt : *state.blocks) {\n            auto &block = blockIt.second;\n            for (auto &edge : blockIt.second.edges) {\n                for (int i = 1 + int(horizontal); i < edge.polyline.size(); i += 2) {\n                    int x = solution[variableIndex++];\n                    if (horizontal) {\n                        edge.polyline[i].ry() = x;\n                        edge.polyline[i - 1].ry() = x;\n                    } else {\n                        edge.polyline[i].rx() = x;\n                        edge.polyline[i - 1].rx() = x;\n                    }\n                }\n            }\n            int blockVariable = blockMapping[blockIt.first];\n            (horizontal ? block.y : block.x) = solution[blockVariable];\n        }\n    };\n\n    std::vector<Segment> segments;\n    segments.reserve(state.blocks->size() * 2 + state.blocks->size() * 2);\n    size_t variableIndex = state.blocks->size();\n    size_t edgeIndex = 0;\n    // horizontal segments\n\n    objectiveFunction.assign(blockMapping.size(), 1);\n    for (auto &blockIt : *state.blocks) {\n        auto &block = blockIt.second;\n        int blockVariable = blockMapping[blockIt.first];\n        for (auto &edge : block.edges) {\n            auto &targetBlock = (*state.blocks)[edge.target];\n            if (block.y < targetBlock.y) {\n                int spacing = block.height + layoutConfig.blockVerticalSpacing;\n                inequalities.push_back({ { blockVariable, blockMapping[edge.target] }, -spacing });\n            }\n            if (edge.polyline.size() < 3) {\n                continue;\n            }\n            for (int i = 2; i < edge.polyline.size(); i += 2) {\n                int y0 = edge.polyline[i - 1].x();\n                int y1 = edge.polyline[i].x();\n                if (y0 > y1) {\n                    std::swap(y0, y1);\n                }\n                int x = edge.polyline[i].y();\n                segments.push_back({ x, int(variableIndex), y0, y1 });\n                variableGroups.push_back(blockMapping.size() + edgeIndex);\n                setFeasibleSolution(variableIndex, x);\n                if (i > 2) {\n                    int prevX = edge.polyline[i - 2].y();\n                    addObjective(variableIndex, x, variableIndex - 1, prevX);\n                }\n                variableIndex++;\n            }\n            edgeIndex++;\n        }\n        segments.push_back({ block.y, blockVariable, block.x, block.x + block.width });\n        segments.push_back(\n                { block.y + block.height, blockVariable, block.x, block.x + block.width });\n        setFeasibleSolution(blockVariable, block.y);\n    }\n\n    createInequalitiesFromSegments(std::move(segments), solution, variableGroups,\n                                   blockMapping.size(), layoutConfig.blockVerticalSpacing,\n                                   layoutConfig.edgeVerticalSpacing, inequalities);\n\n    objectiveFunction.resize(solution.size());\n    optimizeLinearProgram(solution.size(), objectiveFunction, inequalities, equalities, solution);\n    copyVariablesToPositions(solution, true);\n    connectEdgeEnds(*state.blocks);\n\n    // vertical segments\n    variableGroups.resize(blockMapping.size());\n    solution.clear();\n    equalities.clear();\n    inequalities.clear();\n    objectiveFunction.clear();\n    segments.clear();\n    variableIndex = blockMapping.size();\n    edgeIndex = 0;\n    for (auto &blockIt : *state.blocks) {\n        auto &block = blockIt.second;\n        for (auto &edge : block.edges) {\n            if (edge.polyline.size() < 2) {\n                continue;\n            }\n            size_t firstEdgeVariable = variableIndex;\n            for (int i = 1; i < edge.polyline.size(); i += 2) {\n                int y0 = edge.polyline[i - 1].y();\n                int y1 = edge.polyline[i].y();\n                if (y0 > y1) {\n                    std::swap(y0, y1);\n                }\n                int x = edge.polyline[i].x();\n                segments.push_back({ x, int(variableIndex), y0, y1 });\n                variableGroups.push_back(blockMapping.size() + edgeIndex);\n                setFeasibleSolution(variableIndex, x);\n                if (i > 2) {\n                    int prevX = edge.polyline[i - 2].x();\n                    addObjective(variableIndex, x, variableIndex - 1, prevX);\n                }\n                variableIndex++;\n            }\n            size_t lastEdgeVariableIndex = variableIndex - 1;\n            addBlockSegmentEquality(blockIt.first, firstEdgeVariable, edge.polyline[1].x());\n            addBlockSegmentEquality(edge.target, lastEdgeVariableIndex, segments.back().x);\n            edgeIndex++;\n        }\n        int blockVariable = blockMapping[blockIt.first];\n        segments.push_back({ block.x, blockVariable, block.y, block.y + block.height });\n        segments.push_back(\n                { block.x + block.width, blockVariable, block.y, block.y + block.height });\n        setFeasibleSolution(blockVariable, block.x);\n    }\n\n    createInequalitiesFromSegments(std::move(segments), solution, variableGroups,\n                                   blockMapping.size(), layoutConfig.blockHorizontalSpacing,\n                                   layoutConfig.edgeHorizontalSpacing, inequalities);\n\n    objectiveFunction.resize(solution.size());\n    // horizontal centering constraints\n    for (auto &blockIt : *state.blocks) {\n        auto &block = blockIt.second;\n        int blockVariable = blockMapping[blockIt.first];\n        if (block.edges.size() == 2) {\n            auto &blockLeft = (*state.blocks)[block.edges[0].target];\n            auto &blockRight = (*state.blocks)[block.edges[1].target];\n            auto middle = block.x + block.width / 2;\n            if (blockLeft.x + blockLeft.width < middle && blockRight.x > middle) {\n                addInequality(blockMapping[block.edges[0].target], blockLeft.x + blockLeft.width,\n                              blockVariable, middle, layoutConfig.blockHorizontalSpacing / 2);\n                addInequality(blockVariable, middle, blockMapping[block.edges[1].target],\n                              blockRight.x, layoutConfig.blockHorizontalSpacing / 2);\n                auto &gridBlock = state.grid_blocks[blockIt.first];\n                if (gridBlock.mergeBlock) {\n                    auto &mergeBlock = (*state.blocks)[gridBlock.mergeBlock];\n                    if (mergeBlock.x + mergeBlock.width / 2 == middle) {\n                        equalities.push_back(\n                                { { blockVariable, blockMapping[gridBlock.mergeBlock] },\n                                  block.x - mergeBlock.x });\n                    }\n                }\n            }\n        }\n    }\n\n    optimizeLinearProgram(solution.size(), objectiveFunction, inequalities, equalities, solution);\n    copyVariablesToPositions(solution);\n}\n"
  },
  {
    "path": "src/widgets/GraphGridLayout.h",
    "content": "#ifndef GRAPHGRIDLAYOUT_H\n#define GRAPHGRIDLAYOUT_H\n\n#include \"core/Cutter.h\"\n#include \"GraphLayout.h\"\n#include \"common/LinkedListPool.h\"\n\n/**\n * @brief Graph layout algorithm on layered graph layout approach. For simplicity all the nodes are\n * placed in a grid.\n */\nclass GraphGridLayout : public GraphLayout\n{\npublic:\n    enum class LayoutType {\n        Medium,\n        Wide,\n        Narrow,\n    };\n\n    GraphGridLayout(LayoutType layoutType = LayoutType::Medium);\n    virtual void CalculateLayout(Graph &blocks, ut64 entry, int &width, int &height) const override;\n    void setTightSubtreePlacement(bool enabled) { tightSubtreePlacement = enabled; }\n    void setParentBetweenDirectChild(bool enabled) { parentBetweenDirectChild = enabled; }\n    void setverticalBlockAlignmentMiddle(bool enabled) { verticalBlockAlignmentMiddle = enabled; }\n    void setLayoutOptimization(bool enabled) { useLayoutOptimization = enabled; }\n\nprivate:\n    /// false - use bounding box for smallest subtree when placing them side by side\n    bool tightSubtreePlacement = false;\n    /// true if code should try to place parent between direct children as much as possible\n    bool parentBetweenDirectChild = false;\n    /// false if blocks in rows should be aligned at top, true for middle alignment\n    bool verticalBlockAlignmentMiddle = false;\n    bool useLayoutOptimization = true;\n\n    struct GridBlock\n    {\n        ut64 id;\n        std::vector<ut64> tree_edge; //!< subset of outgoing edges that form a tree\n        std::vector<ut64> dag_edge; //!< subset of outgoing edges that form a dag\n        std::size_t has_parent = false;\n        int inputCount = 0;\n        int outputCount = 0;\n\n        /// Number of rows in subtree\n        int row_count = 0;\n        /// Column in which the block is\n        int col = 0;\n        /// Row in which the block is\n        int row = 0;\n\n        ut64 mergeBlock = 0;\n\n        int lastRowLeft; //!< left side of subtree last row\n        int lastRowRight; //!< right side of subtree last row\n        int leftPosition; //!< left side of subtree\n        int rightPosition; //!< right side of subtree\n        LinkedListPool<int>::List leftSideShape;\n        LinkedListPool<int>::List rightSideShape;\n    };\n\n    struct Point\n    {\n        int row;\n        int col;\n        int offset;\n        int16_t kind;\n        int16_t spacingOverride;\n    };\n\n    struct GridEdge\n    {\n        ut64 dest;\n        int mainColumn = -1;\n        std::vector<Point> points;\n        int secondaryPriority;\n\n        void addPoint(int row, int col, int16_t kind = 0)\n        {\n            this->points.push_back({ row, col, 0, kind, 0 });\n        }\n    };\n\n    struct LayoutState\n    {\n        std::unordered_map<ut64, GridBlock> grid_blocks;\n        std::unordered_map<ut64, GraphBlock> *blocks = nullptr;\n        std::unordered_map<ut64, std::vector<GridEdge>> edge;\n        size_t rows = -1;\n        size_t columns = -1;\n        std::vector<int> columnWidth;\n        std::vector<int> rowHeight;\n        std::vector<int> edgeColumnWidth;\n        std::vector<int> edgeRowHeight;\n\n        std::vector<int> columnOffset;\n        std::vector<int> rowOffset;\n        std::vector<int> edgeColumnOffset;\n        std::vector<int> edgeRowOffset;\n    };\n\n    using GridBlockMap = std::unordered_map<ut64, GridBlock>;\n\n    /**\n     * @brief Find nodes where control flow merges after splitting.\n     * Sets node column offset so that after computing placement merge point is centered bellow\n     * nodes above.\n     */\n    void findMergePoints(LayoutState &state) const;\n    /**\n     * @brief Compute node rows and columns within grid.\n     * @param blockOrder Nodes in the reverse topological order.\n     */\n    void computeAllBlockPlacement(const std::vector<ut64> &blockOrder,\n                                  LayoutState &layoutState) const;\n    /**\n     * @brief Perform the topological sorting of graph nodes.\n     * If the graph contains loops, a subset of edges is selected. Subset of edges forming DAG are\n     * stored in GridBlock::dag_edge.\n     * @param state Graph layout state including the input graph.\n     * @param entry Entrypoint node. When removing loops prefer placing this node at top.\n     * @return Reverse topological ordering.\n     */\n    static std::vector<ut64> topoSort(LayoutState &state, ut64 entry);\n\n    /**\n     * @brief Assign row positions to nodes.\n     * @param state\n     * @param blockOrder reverse topological ordering of nodes\n     */\n    static void assignRows(LayoutState &state, const std::vector<ut64> &blockOrder);\n    /**\n     * @brief Select subset of DAG edges that form tree.\n     * @param state\n     */\n    static void selectTree(LayoutState &state);\n\n    /**\n     * @brief routeEdges Route edges, expects node positions to be calculated previously.\n     */\n    void routeEdges(LayoutState &state) const;\n    /**\n     * @brief Choose which column to use for transition from start node row to target node row.\n     */\n    void calculateEdgeMainColumn(LayoutState &state) const;\n    /**\n     * @brief Do rough edge routing within grid using up to 5 segments.\n     */\n    void roughRouting(LayoutState &state) const;\n    /**\n     * @brief Calculate segment placement relative to their columns.\n     */\n    void elaborateEdgePlacement(LayoutState &state) const;\n    /**\n     * @brief Recalculate column widths, trying to compensate for the space taken by edge columns.\n     */\n    void adjustColumnWidths(LayoutState &state) const;\n    /**\n     * @brief Calculate position of each column(or row) based on widths.\n     * It is assumed that columnWidth.size() + 1 = edgeColumnWidth.size() and they are interleaved.\n     * @param columnWidth\n     * @param edgeColumnWidth\n     * @param columnOffset\n     * @param edgeColumnOffset\n     * @return total width of all the columns\n     */\n    static int calculateColumnOffsets(const std::vector<int> &columnWidth,\n                                      std::vector<int> &edgeColumnWidth,\n                                      std::vector<int> &columnOffset,\n                                      std::vector<int> &edgeColumnOffset);\n    /**\n     * @brief Final graph layout step. Convert grids cell relative positions to absolute pixel\n     * positions.\n     * @param state\n     * @param width image width output argument\n     * @param height image height output argument\n     */\n    void convertToPixelCoordinates(LayoutState &state, int &width, int &height) const;\n    /**\n     * @brief Move the graph content to top left corner and update dimensions.\n     * @param graph\n     * @param width width after cropping\n     * @param height height after cropping\n     */\n    void cropToContent(Graph &graph, int &width, int &height) const;\n    /**\n     * @brief Connect edge ends to blocks by changing y.\n     * @param graph\n     */\n    void connectEdgeEnds(Graph &graph) const;\n    /**\n     * @brief Reduce spacing between nodes and edges by pushing everything together ignoring the\n     * grid.\n     * @param state\n     */\n    void optimizeLayout(LayoutState &state) const;\n};\n\n#endif // GRAPHGRIDLAYOUT_H\n"
  },
  {
    "path": "src/widgets/GraphHorizontalAdapter.cpp",
    "content": "#include \"GraphHorizontalAdapter.h\"\n\nGraphHorizontalAdapter::GraphHorizontalAdapter(std::unique_ptr<GraphLayout> layout)\n    : GraphLayout({}), layout(std::move(layout))\n{\n    swapLayoutConfigDirection();\n}\n\nvoid GraphHorizontalAdapter::CalculateLayout(GraphLayout::Graph &blocks, unsigned long long entry,\n                                             int &width, int &height) const\n{\n    for (auto &block : blocks) {\n        std::swap(block.second.width, block.second.height);\n    }\n    layout->CalculateLayout(blocks, entry, height,\n                            width); // intentionally swapping height and width\n    for (auto &block : blocks) {\n        std::swap(block.second.width, block.second.height);\n        std::swap(block.second.x, block.second.y);\n        for (auto &edge : block.second.edges) {\n            for (auto &point : edge.polyline) {\n                std::swap(point.rx(), point.ry());\n            }\n            switch (edge.arrow) {\n            case GraphEdge::Down:\n                edge.arrow = GraphEdge::Right;\n                break;\n            case GraphEdge::Left:\n                edge.arrow = GraphEdge::Up;\n                break;\n            case GraphEdge::Up:\n                edge.arrow = GraphEdge::Left;\n                break;\n            case GraphEdge::Right:\n                edge.arrow = GraphEdge::Down;\n                break;\n            case GraphEdge::None:\n                edge.arrow = GraphEdge::None;\n                break;\n            }\n        }\n    }\n}\n\nvoid GraphHorizontalAdapter::setLayoutConfig(const GraphLayout::LayoutConfig &config)\n{\n    GraphLayout::setLayoutConfig(config);\n    swapLayoutConfigDirection();\n    layout->setLayoutConfig(config);\n}\n\nvoid GraphHorizontalAdapter::swapLayoutConfigDirection()\n{\n    std::swap(layoutConfig.edgeVerticalSpacing, layoutConfig.edgeHorizontalSpacing);\n    std::swap(layoutConfig.blockVerticalSpacing, layoutConfig.blockHorizontalSpacing);\n}\n"
  },
  {
    "path": "src/widgets/GraphHorizontalAdapter.h",
    "content": "#ifndef GRAPH_HORIZONTAL_ADAPTER_H\n#define GRAPH_HORIZONTAL_ADAPTER_H\n\n#include \"core/Cutter.h\"\n#include \"GraphLayout.h\"\n\n#include <memory>\n\n/**\n * @brief Adapter for converting vertical graph layout into horizontal one.\n */\nclass GraphHorizontalAdapter : public GraphLayout\n{\npublic:\n    GraphHorizontalAdapter(std::unique_ptr<GraphLayout> layout);\n    virtual void CalculateLayout(GraphLayout::Graph &blocks, ut64 entry, int &width,\n                                 int &height) const override;\n    void setLayoutConfig(const LayoutConfig &config) override;\n\nprivate:\n    std::unique_ptr<GraphLayout> layout;\n    void swapLayoutConfigDirection();\n};\n\n#endif // GRAPH_HORIZONTAL_ADAPTER_H\n"
  },
  {
    "path": "src/widgets/GraphLayout.h",
    "content": "#ifndef GRAPHLAYOUT_H\n#define GRAPHLAYOUT_H\n\n#include \"core/Cutter.h\"\n\n#include <unordered_map>\n\nclass GraphLayout\n{\npublic:\n    struct GraphEdge\n    {\n        ut64 target;\n        QPolygonF polyline;\n        enum ArrowDirection { Down, Left, Up, Right, None };\n        ArrowDirection arrow = ArrowDirection::Down;\n\n        explicit GraphEdge(ut64 target) : target(target) {}\n    };\n\n    struct GraphBlock\n    {\n        int x = 0;\n        int y = 0;\n        int width = 0;\n        int height = 0;\n        // This is a unique identifier, e.g. offset in the case of rizin blocks\n        ut64 entry;\n        // Edges\n        std::vector<GraphEdge> edges;\n    };\n    using Graph = std::unordered_map<ut64, GraphBlock>;\n\n    struct LayoutConfig\n    {\n        int blockVerticalSpacing = 40;\n        int blockHorizontalSpacing = 20;\n        int edgeVerticalSpacing = 10;\n        int edgeHorizontalSpacing = 10;\n    };\n\n    GraphLayout(const LayoutConfig &layout_config) : layoutConfig(layout_config) {}\n    virtual ~GraphLayout() {}\n    virtual void CalculateLayout(Graph &blocks, ut64 entry, int &width, int &height) const = 0;\n    virtual void setLayoutConfig(const LayoutConfig &config) { this->layoutConfig = config; };\n\nprotected:\n    LayoutConfig layoutConfig;\n};\n\n#endif // GRAPHLAYOUT_H\n"
  },
  {
    "path": "src/widgets/GraphView.cpp",
    "content": "#include \"GraphView.h\"\n\n#include \"GraphGridLayout.h\"\n#ifdef CUTTER_ENABLE_GRAPHVIZ\n#    include \"GraphvizLayout.h\"\n#endif\n#include \"GraphHorizontalAdapter.h\"\n#include \"Helpers.h\"\n\n#include <vector>\n#include <QPainter>\n#include <QMouseEvent>\n#include <QKeyEvent>\n#include <QPropertyAnimation>\n#include <QSvgGenerator>\n\n#ifndef CUTTER_NO_OPENGL_GRAPH\n#    include <QOpenGLContext>\n#    include <QOpenGLWidget>\n#    include <QOpenGLPaintDevice>\n#    include <QOpenGLExtraFunctions>\n#endif\n\nGraphView::GraphView(QWidget *parent)\n    : QAbstractScrollArea(parent),\n      useGL(false)\n#ifndef CUTTER_NO_OPENGL_GRAPH\n      ,\n      cacheTexture(0),\n      cacheFBO(0)\n#endif\n{\n#ifndef CUTTER_NO_OPENGL_GRAPH\n    if (useGL) {\n        glWidget = new QOpenGLWidget(this);\n        setViewport(glWidget);\n    } else {\n        glWidget = nullptr;\n    }\n#endif\n    setGraphLayout(makeGraphLayout(Layout::GridMedium));\n}\n\nGraphView::~GraphView() {}\n\n// Callbacks\n\nvoid GraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos)\n{\n    Q_UNUSED(block);\n    Q_UNUSED(event);\n    Q_UNUSED(pos);\n}\n\nvoid GraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos)\n{\n    Q_UNUSED(block);\n    Q_UNUSED(event);\n    Q_UNUSED(pos);\n}\n\nvoid GraphView::blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event, QPoint pos)\n{\n    Q_UNUSED(block);\n    Q_UNUSED(event);\n    Q_UNUSED(pos);\n}\n\nbool GraphView::helpEvent(QHelpEvent *event)\n{\n    auto p = viewToLogicalCoordinates(event->pos());\n    if (auto block = getBlockContaining(p)) {\n        blockHelpEvent(*block, event, p - QPoint(block->x, block->y));\n        return true;\n    }\n    return false;\n}\n\nvoid GraphView::blockTransitionedTo(GraphView::GraphBlock *to)\n{\n    Q_UNUSED(to);\n}\n\nGraphView::EdgeConfiguration GraphView::edgeConfiguration(GraphView::GraphBlock &from,\n                                                          GraphView::GraphBlock *to,\n                                                          bool interactive)\n{\n    Q_UNUSED(from)\n    Q_UNUSED(to)\n    Q_UNUSED(interactive)\n    qWarning() << \"Edge configuration not overridden!\";\n    EdgeConfiguration ec;\n    return ec;\n}\n\nvoid GraphView::blockContextMenuRequested(GraphView::GraphBlock &, QContextMenuEvent *, QPoint) {}\n\nbool GraphView::event(QEvent *event)\n{\n    if (event->type() == QEvent::ToolTip) {\n        if (helpEvent(static_cast<QHelpEvent *>(event))) {\n            return true;\n        }\n    } else if (event->type() == QEvent::Gesture) {\n        if (gestureEvent(static_cast<QGestureEvent *>(event))) {\n            return true;\n        }\n    }\n\n    return QAbstractScrollArea::event(event);\n}\n\nbool GraphView::gestureEvent(QGestureEvent *event)\n{\n    Q_UNUSED(event)\n    return false;\n}\n\nvoid GraphView::contextMenuEvent(QContextMenuEvent *event)\n{\n    event->ignore();\n    if (event->reason() == QContextMenuEvent::Mouse) {\n        QPoint p = viewToLogicalCoordinates(event->pos());\n        if (auto block = getBlockContaining(p)) {\n            blockContextMenuRequested(*block, event, p);\n        }\n    }\n}\n\nvoid GraphView::computeGraphPlacement()\n{\n    graphLayoutSystem->CalculateLayout(blocks, entry, width, height);\n    setCacheDirty();\n    clampViewOffset();\n    viewport()->update();\n}\n\nvoid GraphView::cleanupEdges(GraphLayout::Graph &graph)\n{\n    for (auto &blockIt : graph) {\n        auto &block = blockIt.second;\n        auto outIt = block.edges.begin();\n        std::unordered_set<ut64> seenEdges;\n        for (auto it = block.edges.begin(), end = block.edges.end(); it != end; ++it) {\n            // remove edges going  to different functions\n            // and remove duplicate edges, common in switch statements\n            if (graph.find(it->target) != graph.end()\n                && seenEdges.find(it->target) == seenEdges.end()) {\n                *outIt++ = *it;\n                seenEdges.insert(it->target);\n            }\n        }\n        block.edges.erase(outIt, block.edges.end());\n    }\n}\n\nvoid GraphView::beginMouseDrag(QMouseEvent *event)\n{\n    scrollBase = event->pos();\n    scroll_mode = true;\n    setCursor(Qt::ClosedHandCursor);\n}\n\nvoid GraphView::setViewOffset(QPoint offset)\n{\n    setViewOffsetInternal(offset);\n}\n\nvoid GraphView::setViewScale(qreal scale)\n{\n    this->current_scale = scale;\n    emit viewScaleChanged(scale);\n}\n\nQSize GraphView::getCacheSize()\n{\n    return\n#ifndef CUTTER_NO_OPENGL_GRAPH\n            useGL ? cacheSize :\n#endif\n                  pixmap.size();\n}\n\nqreal GraphView::getCacheDevicePixelRatioF()\n{\n    return\n#ifndef CUTTER_NO_OPENGL_GRAPH\n            useGL ? 1.0 :\n#endif\n                  qhelpers::devicePixelRatio(&pixmap);\n}\n\nQSize GraphView::getRequiredCacheSize()\n{\n    return viewport()->size() * qhelpers::devicePixelRatio(this);\n}\n\nqreal GraphView::getRequiredCacheDevicePixelRatioF()\n{\n    return\n#ifndef CUTTER_NO_OPENGL_GRAPH\n            useGL ? 1.0f :\n#endif\n                  qhelpers::devicePixelRatio(this);\n}\n\nvoid GraphView::paintEvent(QPaintEvent *)\n{\n#ifndef CUTTER_NO_OPENGL_GRAPH\n    if (useGL) {\n        glWidget->makeCurrent();\n    }\n#endif\n\n    if (!qFuzzyCompare(getCacheDevicePixelRatioF(), getRequiredCacheDevicePixelRatioF())\n        || getCacheSize() != getRequiredCacheSize()) {\n        setCacheDirty();\n    }\n\n    if (cacheDirty) {\n        paintGraphCache();\n        cacheDirty = false;\n    }\n\n    if (useGL) {\n#ifndef CUTTER_NO_OPENGL_GRAPH\n        auto gl = glWidget->context()->extraFunctions();\n        gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, cacheFBO);\n        gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glWidget->defaultFramebufferObject());\n        auto dpr = qhelpers::devicePixelRatio(this);\n        gl->glBlitFramebuffer(0, 0, cacheSize.width(), cacheSize.height(), 0, 0,\n                              viewport()->width() * dpr, viewport()->height() * dpr,\n                              GL_COLOR_BUFFER_BIT, GL_NEAREST);\n        glWidget->doneCurrent();\n#endif\n    } else {\n        QPainter p(viewport());\n        p.drawPixmap(QPoint(0, 0), pixmap);\n    }\n}\n\nvoid GraphView::clampViewOffset()\n{\n    const qreal edgeFraction = 0.25;\n    qreal edgeX = edgeFraction * (viewport()->width() / current_scale);\n    qreal edgeY = edgeFraction * (viewport()->height() / current_scale);\n    offset.rx() = std::max(std::min(qreal(offset.x()), width - edgeX),\n                           -viewport()->width() / current_scale + edgeX);\n    offset.ry() = std::max(std::min(qreal(offset.y()), height - edgeY),\n                           -viewport()->height() / current_scale + edgeY);\n}\n\nvoid GraphView::setViewOffsetInternal(QPoint pos, bool emitSignal)\n{\n    offset = pos;\n    clampViewOffset();\n    if (emitSignal)\n        emit viewOffsetChanged(offset);\n}\n\nvoid GraphView::addViewOffset(QPoint move, bool emitSignal)\n{\n    setViewOffsetInternal(offset + move, emitSignal);\n}\n\nvoid GraphView::paintGraphCache()\n{\n#ifndef CUTTER_NO_OPENGL_GRAPH\n    std::unique_ptr<QOpenGLPaintDevice> paintDevice;\n#endif\n    QPainter p;\n    if (useGL) {\n#ifndef CUTTER_NO_OPENGL_GRAPH\n        auto gl = QOpenGLContext::currentContext()->functions();\n\n        bool resizeTex = false;\n        QSize sizeNeed = getRequiredCacheSize();\n        if (!cacheTexture) {\n            gl->glGenTextures(1, &cacheTexture);\n            gl->glBindTexture(GL_TEXTURE_2D, cacheTexture);\n            gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n            gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n            gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n            gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n            gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n            resizeTex = true;\n        } else if (cacheSize != sizeNeed) {\n            gl->glBindTexture(GL_TEXTURE_2D, cacheTexture);\n            resizeTex = true;\n        }\n        if (resizeTex) {\n            cacheSize = sizeNeed;\n            gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cacheSize.width(), cacheSize.height(), 0,\n                             GL_RGBA, GL_UNSIGNED_BYTE, nullptr);\n            gl->glGenFramebuffers(1, &cacheFBO);\n            gl->glBindFramebuffer(GL_FRAMEBUFFER, cacheFBO);\n            gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,\n                                       cacheTexture, 0);\n        } else {\n            gl->glBindFramebuffer(GL_FRAMEBUFFER, cacheFBO);\n        }\n        gl->glViewport(0, 0, viewport()->width(), viewport()->height());\n        gl->glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(),\n                         1.0f);\n        gl->glClear(GL_COLOR_BUFFER_BIT);\n\n        paintDevice.reset(new QOpenGLPaintDevice(cacheSize));\n        p.begin(paintDevice.get());\n#endif\n    } else {\n        auto dpr = qhelpers::devicePixelRatio(this);\n        pixmap = QPixmap(getRequiredCacheSize() * dpr);\n        pixmap.setDevicePixelRatio(dpr);\n        pixmap.fill(backgroundColor);\n        p.begin(&pixmap);\n        p.setRenderHint(QPainter::Antialiasing);\n        p.setViewport(this->viewport()->rect());\n    }\n    paint(p, offset, this->viewport()->rect(), current_scale);\n\n    p.end();\n}\n\nvoid GraphView::paint(QPainter &p, QPoint offset, QRect viewport, qreal scale, bool interactive)\n{\n    QPointF offsetF(offset.x(), offset.y());\n    p.setBrush(Qt::black);\n\n    int render_width = viewport.width();\n    int render_height = viewport.height();\n\n    // window - rectangle in logical coordinates\n    QRect window =\n            QRect(offset, QSize(qRound(render_width / scale), qRound(render_height / scale)));\n    p.setWindow(window);\n    QRectF windowF(window.x(), window.y(), window.width(), window.height());\n\n    for (auto &blockIt : blocks) {\n        GraphBlock &block = blockIt.second;\n\n        QRectF blockRect(block.x, block.y, block.width, block.height);\n\n        // Check if block is visible by checking if block intersects with view area\n        if (blockRect.intersects(windowF)) {\n            drawBlock(p, block, interactive);\n        }\n\n        p.setBrush(Qt::gray);\n\n        // Always draw edges\n        // TODO: Only draw edges if they are actually visible ...\n        // Draw edges\n        for (GraphEdge &edge : block.edges) {\n            if (edge.polyline.empty()) {\n                continue;\n            }\n            QPolygonF polyline = edge.polyline;\n            EdgeConfiguration ec = edgeConfiguration(block, &blocks[edge.target], interactive);\n            QPen pen(ec.color);\n            pen.setStyle(ec.lineStyle);\n            pen.setWidthF(pen.width() * ec.width_scale);\n            if (scale_thickness_multiplier && ec.width_scale > 1.01 && pen.widthF() * scale < 2) {\n                pen.setWidthF(ec.width_scale / scale);\n            }\n            if (pen.widthF() * scale < 2) {\n                pen.setWidth(0);\n            }\n            p.setPen(pen);\n            p.setBrush(ec.color);\n            p.drawPolyline(polyline);\n            pen.setStyle(Qt::SolidLine);\n            p.setPen(pen);\n\n            auto drawArrow = [&](QPointF tip, QPointF dir) {\n                pen.setWidth(0);\n                p.setPen(pen);\n                QPolygonF arrow;\n                arrow << tip;\n                QPointF dy(-dir.y(), dir.x());\n                QPointF base = tip - dir * 6;\n                arrow << base + 3 * dy;\n                arrow << base - 3 * dy;\n                p.drawConvexPolygon(arrow);\n            };\n\n            if (!polyline.empty()) {\n                if (ec.start_arrow) {\n                    auto firstPt = edge.polyline.first();\n                    drawArrow(firstPt, QPointF(0, 1));\n                }\n                if (ec.end_arrow) {\n                    auto lastPt = edge.polyline.last();\n                    QPointF dir(0, -1);\n                    switch (edge.arrow) {\n                    case GraphLayout::GraphEdge::Down:\n                        dir = QPointF(0, 1);\n                        break;\n                    case GraphLayout::GraphEdge::Up:\n                        dir = QPointF(0, -1);\n                        break;\n                    case GraphLayout::GraphEdge::Left:\n                        dir = QPointF(-1, 0);\n                        break;\n                    case GraphLayout::GraphEdge::Right:\n                        dir = QPointF(1, 0);\n                        break;\n                    default:\n                        break;\n                    }\n                    drawArrow(lastPt, dir);\n                }\n            }\n        }\n    }\n}\n\nvoid GraphView::saveAsBitmap(QString path, const char *format, double scaler, bool transparent)\n{\n    QImage image(width * scaler, height * scaler, QImage::Format_ARGB32);\n    if (transparent) {\n        image.fill(qRgba(0, 0, 0, 0));\n    } else {\n        image.fill(backgroundColor);\n    }\n    QPainter p;\n    p.begin(&image);\n    paint(p, QPoint(0, 0), image.rect(), scaler, false);\n    p.end();\n    if (!image.save(path, format)) {\n        qWarning() << \"Could not save image\";\n    }\n}\n\nvoid GraphView::saveAsSvg(QString path)\n{\n    QSvgGenerator generator;\n    generator.setFileName(path);\n    generator.setSize(QSize(width, height));\n    generator.setViewBox(QRect(0, 0, width, height));\n    generator.setTitle(tr(\"Cutter graph export\"));\n    QPainter p;\n    p.begin(&generator);\n    paint(p, QPoint(0, 0), QRect(0, 0, width, height), 1.0, false);\n    p.end();\n}\n\nvoid GraphView::center()\n{\n    centerX(false);\n    centerY(false);\n    emit viewOffsetChanged(offset);\n}\n\nvoid GraphView::centerX(bool emitSignal)\n{\n    offset.rx() = -((viewport()->width() - width * current_scale) / 2);\n    offset.rx() /= current_scale;\n    clampViewOffset();\n    if (emitSignal) {\n        emit viewOffsetChanged(offset);\n    }\n}\n\nvoid GraphView::centerY(bool emitSignal)\n{\n    offset.ry() = -((viewport()->height() - height * current_scale) / 2);\n    offset.ry() /= current_scale;\n    clampViewOffset();\n    if (emitSignal) {\n        emit viewOffsetChanged(offset);\n    }\n}\n\nvoid GraphView::showBlock(GraphBlock &block, bool anywhere)\n{\n    showRectangle(QRect(block.x, block.y, block.width, block.height), anywhere);\n    blockTransitionedTo(&block);\n}\n\nvoid GraphView::showRectangle(const QRect &block, bool anywhere)\n{\n    QSizeF renderSize = QSizeF(viewport()->size()) / current_scale;\n    if (width * current_scale <= viewport()->width()) {\n        centerX(false);\n    } else {\n        if (!anywhere || block.x() < offset.x()\n            || block.right() > offset.x() + renderSize.width()) {\n            offset.rx() = block.x() - ((renderSize.width() - block.width()) / 2);\n        }\n    }\n    if (height * current_scale <= viewport()->height()) {\n        centerY(false);\n    } else {\n        if (!anywhere || block.y() < offset.y()\n            || block.bottom() > offset.y() + renderSize.height()) {\n            offset.ry() = block.y();\n            // Leave some space at top if possible\n            const qreal topPadding = 10 / current_scale;\n            if (block.height() + topPadding < renderSize.height()) {\n                offset.ry() -= topPadding;\n            }\n        }\n    }\n    clampViewOffset();\n    emit viewOffsetChanged(offset);\n    viewport()->update();\n}\n\nGraphView::GraphBlock *GraphView::getBlockContaining(QPoint p)\n{\n    // Check if a block was clicked\n    for (auto &blockIt : blocks) {\n        GraphBlock &block = blockIt.second;\n\n        QRect rec(block.x, block.y, block.width, block.height);\n        if (rec.contains(p)) {\n            return &block;\n        }\n    }\n    return nullptr;\n}\n\nQPoint GraphView::viewToLogicalCoordinates(QPoint p)\n{\n    return p / current_scale + offset;\n}\n\nQPoint GraphView::logicalToViewCoordinates(QPoint p)\n{\n    return (p - offset) * current_scale;\n}\n\nvoid GraphView::setGraphLayout(std::unique_ptr<GraphLayout> layout)\n{\n    graphLayoutSystem = std::move(layout);\n    if (!graphLayoutSystem) {\n        graphLayoutSystem = makeGraphLayout(Layout::GridMedium);\n    }\n}\n\nvoid GraphView::setLayoutConfig(const GraphLayout::LayoutConfig &config)\n{\n    graphLayoutSystem->setLayoutConfig(config);\n}\n\nstd::unique_ptr<GraphLayout> GraphView::makeGraphLayout(GraphView::Layout layout, bool horizontal)\n{\n    std::unique_ptr<GraphLayout> result;\n    bool needAdapter = true;\n\n#ifdef CUTTER_ENABLE_GRAPHVIZ\n    auto makeGraphvizLayout = [&](GraphvizLayout::LayoutType type) {\n        result.reset(new GraphvizLayout(\n                type, horizontal ? GraphvizLayout::Direction::LR : GraphvizLayout::Direction::TB));\n        needAdapter = false;\n    };\n#endif\n\n    switch (layout) {\n    case Layout::GridNarrow:\n        result.reset(new GraphGridLayout(GraphGridLayout::LayoutType::Narrow));\n        break;\n    case Layout::GridMedium:\n        result.reset(new GraphGridLayout(GraphGridLayout::LayoutType::Medium));\n        break;\n    case Layout::GridWide:\n        result.reset(new GraphGridLayout(GraphGridLayout::LayoutType::Wide));\n        break;\n    case Layout::GridAAA:\n    case Layout::GridAAB:\n    case Layout::GridABA:\n    case Layout::GridABB:\n    case Layout::GridBAA:\n    case Layout::GridBAB:\n    case Layout::GridBBA:\n    case Layout::GridBBB: {\n        int options = static_cast<int>(layout) - static_cast<int>(Layout::GridAAA);\n        std::unique_ptr<GraphGridLayout> gridLayout(new GraphGridLayout());\n        gridLayout->setTightSubtreePlacement((options & 1) == 0);\n        gridLayout->setParentBetweenDirectChild((options & 2));\n        gridLayout->setLayoutOptimization((options & 4));\n        result = std::move(gridLayout);\n        break;\n    }\n#ifdef CUTTER_ENABLE_GRAPHVIZ\n    case Layout::GraphvizOrtho:\n        makeGraphvizLayout(GraphvizLayout::LayoutType::DotOrtho);\n        break;\n    case Layout::GraphvizPolyline:\n        makeGraphvizLayout(GraphvizLayout::LayoutType::DotPolyline);\n        break;\n    case Layout::GraphvizSfdp:\n        makeGraphvizLayout(GraphvizLayout::LayoutType::Sfdp);\n        break;\n    case Layout::GraphvizNeato:\n        makeGraphvizLayout(GraphvizLayout::LayoutType::Neato);\n        break;\n    case Layout::GraphvizTwoPi:\n        makeGraphvizLayout(GraphvizLayout::LayoutType::TwoPi);\n        break;\n    case Layout::GraphvizCirco:\n        makeGraphvizLayout(GraphvizLayout::LayoutType::Circo);\n        break;\n#endif\n    }\n    if (needAdapter && horizontal) {\n        result.reset(new GraphHorizontalAdapter(std::move(result)));\n    }\n    return result;\n}\n\nvoid GraphView::addBlock(GraphView::GraphBlock block)\n{\n    blocks[block.entry] = block;\n}\n\nvoid GraphView::setEntry(ut64 e)\n{\n    entry = e;\n}\n\nbool GraphView::checkPointClicked(QPointF &point, int x, int y, bool above_y)\n{\n    int half_target_size = 5;\n    if ((point.x() - half_target_size < x)\n        && (point.y() - (above_y ? (2 * half_target_size) : 0) < y)\n        && (x < point.x() + half_target_size)\n        && (y < point.y() + (above_y ? 0 : (3 * half_target_size)))) {\n        return true;\n    }\n    return false;\n}\n\n// Mouse events\nvoid GraphView::mousePressEvent(QMouseEvent *event)\n{\n    if (event->button() == Qt::MiddleButton) {\n        beginMouseDrag(event);\n        return;\n    }\n\n    QPoint pos = viewToLogicalCoordinates(event->pos());\n\n    // Check if a block was clicked\n    if (auto block = getBlockContaining(pos)) {\n        blockClicked(*block, event, pos - QPoint(block->x, block->y));\n        // Don't do anything else here! blockClicked might seek and\n        // all our data is invalid then.\n        return;\n    }\n\n    // Check if a line beginning/end  was clicked\n    if (event->button() == Qt::LeftButton) {\n        for (auto &blockIt : blocks) {\n            GraphBlock &block = blockIt.second;\n            for (GraphEdge &edge : block.edges) {\n                if (edge.polyline.length() < 2) {\n                    continue;\n                }\n                QPointF start = edge.polyline.first();\n                QPointF end = edge.polyline.last();\n                if (checkPointClicked(start, pos.x(), pos.y())) {\n                    showBlock(blocks[edge.target]);\n                    // TODO: Callback to child\n                    return;\n                }\n                if (checkPointClicked(end, pos.x(), pos.y(), true)) {\n                    showBlock(block);\n                    // TODO: Callback to child\n                    return;\n                }\n            }\n        }\n    }\n\n    // No block was clicked\n    if (event->button() == Qt::LeftButton) {\n        // Left click outside any block, enter scrolling mode\n        beginMouseDrag(event);\n        return;\n    }\n\n    QAbstractScrollArea::mousePressEvent(event);\n}\n\nvoid GraphView::mouseMoveEvent(QMouseEvent *event)\n{\n    if (scroll_mode) {\n        addViewOffset((scrollBase - event->pos()) / current_scale);\n        scrollBase = event->pos();\n        viewport()->update();\n    }\n}\n\nvoid GraphView::mouseDoubleClickEvent(QMouseEvent *event)\n{\n    auto p = viewToLogicalCoordinates(event->pos());\n    if (auto block = getBlockContaining(p)) {\n        blockDoubleClicked(*block, event, p - QPoint(block->x, block->y));\n    }\n}\n\nvoid GraphView::keyPressEvent(QKeyEvent *event)\n{\n    // for scrolling with arrow keys\n    const int delta = static_cast<int>(30.0 / current_scale);\n    // for scrolling with pgup/pgdown keys\n    const int delta2 = static_cast<int>(100.0 / current_scale);\n    int dx = 0, dy = 0;\n    switch (event->key()) {\n    case Qt::Key_Up:\n        dy = -delta;\n        break;\n    case Qt::Key_Down:\n        dy = delta;\n        break;\n    case Qt::Key_Left:\n        dx = -delta;\n        break;\n    case Qt::Key_Right:\n        dx = delta;\n        break;\n    case Qt::Key_PageUp:\n        dy = -delta2;\n        break;\n    case Qt::Key_PageDown:\n        dy = delta2;\n        break;\n    default:\n        QAbstractScrollArea::keyPressEvent(event);\n        return;\n    }\n    addViewOffset(QPoint(dx, dy));\n    viewport()->update();\n    event->accept();\n}\n\nvoid GraphView::mouseReleaseEvent(QMouseEvent *event)\n{\n    if (scroll_mode && (event->buttons() & (Qt::LeftButton | Qt::MiddleButton)) == 0) {\n        scroll_mode = false;\n        setCursor(Qt::ArrowCursor);\n    }\n}\n\nvoid GraphView::wheelEvent(QWheelEvent *event)\n{\n    if (scroll_mode) {\n        // With some mice it's easy to hit sideway scroll button while holding middle mouse.\n        // That would result in unwanted scrolling while panning.\n        return;\n    }\n    QPoint delta = -event->angleDelta();\n    delta /= current_scale;\n    addViewOffset(delta);\n    viewport()->update();\n    event->accept();\n}\n"
  },
  {
    "path": "src/widgets/GraphView.h",
    "content": "#ifndef GRAPHVIEW_H\n#define GRAPHVIEW_H\n\n#include <QObject>\n#include <QPainter>\n#include <QWidget>\n#include <QAbstractScrollArea>\n#include <QScrollBar>\n#include <QElapsedTimer>\n#include <QHelpEvent>\n#include <QGestureEvent>\n\n#include <unordered_map>\n#include <unordered_set>\n#include <queue>\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"widgets/GraphLayout.h\"\n\n#if defined(QT_NO_OPENGL) || QT_VERSION < QT_VERSION_CHECK(5, 6, 0)\n// QOpenGLExtraFunctions were introduced in 5.6\n#    define CUTTER_NO_OPENGL_GRAPH\n#endif\n\n#ifndef CUTTER_NO_OPENGL_GRAPH\nclass QOpenGLWidget;\n#endif\n\nclass GraphView : public QAbstractScrollArea\n{\n    Q_OBJECT\n\nsignals:\n    void viewOffsetChanged(QPoint offset);\n    void viewScaleChanged(qreal scale);\n\npublic:\n    using GraphBlock = GraphLayout::GraphBlock;\n    using GraphEdge = GraphLayout::GraphEdge;\n\n    enum class Layout {\n        GridNarrow,\n        GridMedium,\n        GridWide,\n        GridAAA,\n        GridAAB,\n        GridABA,\n        GridABB,\n        GridBAA,\n        GridBAB,\n        GridBBA,\n        GridBBB\n#ifdef CUTTER_ENABLE_GRAPHVIZ\n        ,\n        GraphvizOrtho,\n        GraphvizPolyline,\n        GraphvizSfdp,\n        GraphvizNeato,\n        GraphvizTwoPi,\n        GraphvizCirco\n#endif\n    };\n    static std::unique_ptr<GraphLayout> makeGraphLayout(Layout layout, bool horizontal = false);\n\n    struct EdgeConfiguration\n    {\n        QColor color = QColor(128, 128, 128);\n        bool start_arrow = false;\n        bool end_arrow = true;\n        qreal width_scale = 1.0;\n        Qt::PenStyle lineStyle = Qt::PenStyle::SolidLine;\n    };\n\n    explicit GraphView(QWidget *parent);\n    ~GraphView() override;\n\n    void showBlock(GraphBlock &block, bool anywhere = false);\n    /**\n     * @brief Move view so that area is visible.\n     * @param rect Rectangle to show\n     * @param anywhere - set to true for minimizing movement\n     */\n    void showRectangle(const QRect &rect, bool anywhere = false);\n    /**\n     * @brief Get block containing specified point logical coordinates.\n     * @param p positionin graph logical coordinates\n     * @return Block or nullptr if position is outside all blocks.\n     */\n    GraphView::GraphBlock *getBlockContaining(QPoint p);\n    QPoint viewToLogicalCoordinates(QPoint p);\n    QPoint logicalToViewCoordinates(QPoint p);\n\n    void setGraphLayout(std::unique_ptr<GraphLayout> layout);\n    GraphLayout &getGraphLayout() const { return *graphLayoutSystem; }\n    void setLayoutConfig(const GraphLayout::LayoutConfig &config);\n\n    void paint(QPainter &p, QPoint offset, QRect area, qreal scale = 1.0, bool interactive = true);\n\n    void saveAsBitmap(QString path, const char *format = nullptr, double scaler = 1.0,\n                      bool transparent = false);\n    void saveAsSvg(QString path);\n\n    void computeGraphPlacement();\n\n    /**\n     * @brief Remove duplicate edges and edges without target in graph.\n     * @param graph\n     */\n    static void cleanupEdges(GraphLayout::Graph &graph);\n\nprotected:\n    std::unordered_map<ut64, GraphBlock> blocks;\n    /// image background color\n    QColor backgroundColor = QColor(Qt::white);\n\n    // Padding inside the block\n    int block_padding = 16;\n\n    void setCacheDirty() { cacheDirty = true; }\n\n    void addBlock(GraphView::GraphBlock block);\n    void setEntry(ut64 e);\n\n    // Callbacks that should be overridden\n    /**\n     * @brief drawBlock\n     * @param p painter object, not necesarily current widget\n     * @param block\n     * @param interactive - can be used for disabling elemnts during export\n     */\n    virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive = true) = 0;\n    virtual void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos);\n    virtual void blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos);\n    virtual void blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event, QPoint pos);\n    virtual bool helpEvent(QHelpEvent *event);\n    virtual void blockTransitionedTo(GraphView::GraphBlock *to);\n    virtual void wheelEvent(QWheelEvent *event) override;\n    virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from,\n                                                GraphView::GraphBlock *to, bool interactive = true);\n    virtual bool gestureEvent(QGestureEvent *event);\n    /**\n     * @brief Called when user requested context menu for a block. Should open a block specific\n     * contextmenu. Typically triggered by right click.\n     * @param block - the block that was clicked on\n     * @param event - context menu event that triggered the callback, can be used to display context\n     * menu at correct position\n     * @param pos - mouse click position in logical coordinates of the drawing, set only if event\n     * reason is mouse\n     */\n    virtual void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,\n                                           QPoint pos);\n\n    bool event(QEvent *event) override;\n    void contextMenuEvent(QContextMenuEvent *event) override;\n\n    // Mouse events\n    void mousePressEvent(QMouseEvent *event) override;\n    void mouseMoveEvent(QMouseEvent *event) override;\n    void mouseReleaseEvent(QMouseEvent *event) override;\n    void mouseDoubleClickEvent(QMouseEvent *event) override;\n\n    void keyPressEvent(QKeyEvent *event) override;\n\n    void paintEvent(QPaintEvent *event) override;\n\n    int width = 0;\n    int height = 0;\n    bool scale_thickness_multiplier = false;\n\n    void clampViewOffset();\n    void setViewOffsetInternal(QPoint pos, bool emitSignal = true);\n    void addViewOffset(QPoint move, bool emitSignal = true);\n\nprivate:\n    void centerX(bool emitSignal);\n    void centerY(bool emitSignal);\n\n    void paintGraphCache();\n\n    bool checkPointClicked(QPointF &point, int x, int y, bool above_y = false);\n\n    // Zoom data\n    qreal current_scale = 1.0;\n\n    QPoint offset = QPoint(0, 0);\n\n    ut64 entry = 0;\n\n    std::unique_ptr<GraphLayout> graphLayoutSystem;\n\n    QPoint scrollBase;\n    bool scroll_mode = false;\n\n    bool useGL;\n\n    /**\n     * @brief pixmap that caches the graph nodes\n     */\n    QPixmap pixmap;\n\n#ifndef CUTTER_NO_OPENGL_GRAPH\n    uint32_t cacheTexture;\n    uint32_t cacheFBO;\n    QSize cacheSize;\n    QOpenGLWidget *glWidget;\n#endif\n\n    /**\n     * @brief flag to control if the cache is invalid and should be re-created in the next draw\n     */\n    bool cacheDirty = true;\n    QSize getCacheSize();\n    qreal getCacheDevicePixelRatioF();\n    QSize getRequiredCacheSize();\n    qreal getRequiredCacheDevicePixelRatioF();\n\n    void beginMouseDrag(QMouseEvent *event);\n\npublic:\n    QPoint getViewOffset() const { return offset; }\n    void setViewOffset(QPoint offset);\n    qreal getViewScale() const { return current_scale; }\n    void setViewScale(qreal scale);\n\n    void center();\n    void centerX() { centerX(true); }\n    void centerY() { centerY(true); }\n};\n\n#endif // GRAPHVIEW_H\n"
  },
  {
    "path": "src/widgets/GraphWidget.cpp",
    "content": "#include \"core/MainWindow.h\"\n#include \"GraphWidget.h\"\n#include \"DisassemblerGraphView.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include <QVBoxLayout>\n\nGraphWidget::GraphWidget(MainWindow *main) : MemoryDockWidget(MemoryWidgetType::Graph, main)\n{\n    setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());\n\n    setAllowedAreas(Qt::AllDockWidgetAreas);\n\n    auto *layoutWidget = new QWidget(this);\n    setWidget(layoutWidget);\n    auto *layout = new QVBoxLayout(layoutWidget);\n    layout->setContentsMargins(0, 0, 0, 0);\n    layoutWidget->setLayout(layout);\n\n    header = new QLineEdit(this);\n    header->setReadOnly(true);\n    layout->addWidget(header);\n\n    graphView = new DisassemblerGraphView(layoutWidget, seekable, main, { &syncAction });\n    layout->addWidget(graphView);\n\n    // Title needs to get set after graphView is defined\n    updateWindowTitle();\n\n    // getting the name of the class is implementation defined, and cannot be\n    // used reliably across different compilers.\n    // QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts[typeid(this).name()], main);\n    QShortcut *toggle_shortcut = Shortcuts()->makeQShortcut(\"Graph.toggle\", main);\n    connect(toggle_shortcut, &QShortcut::activated, this, [=]() { toggleDockWidget(true); });\n\n    connect(graphView, &DisassemblerGraphView::nameChanged, this,\n            &MemoryDockWidget::updateWindowTitle);\n\n    connect(this, &QDockWidget::visibilityChanged, this, [=](bool visibility) {\n        main->toggleOverview(visibility, this);\n        if (visibility) {\n            graphView->onSeekChanged(Core()->getOffset());\n        }\n    });\n\n    QAction *switchAction = Shortcuts()->makeAction(\"Graph.switchToDisassembly\", this);\n    switchAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n    addAction(switchAction);\n    connect(switchAction, &QAction::triggered, this,\n            [this] { mainWindow->showMemoryWidget(MemoryWidgetType::Disassembly); });\n\n    connect(graphView, &DisassemblerGraphView::graphMoved, this,\n            [=]() { main->toggleOverview(true, this); });\n    connect(seekable, &CutterSeekable::seekableSeekChanged, this, &GraphWidget::prepareHeader);\n    connect(Core(), &CutterCore::functionRenamed, this, &GraphWidget::prepareHeader);\n    graphView->installEventFilter(this);\n}\n\nQWidget *GraphWidget::widgetToFocusOnRaise()\n{\n    return graphView;\n}\n\nvoid GraphWidget::closeEvent(QCloseEvent *event)\n{\n    CutterDockWidget::closeEvent(event);\n    emit graphClosed();\n}\n\nQString GraphWidget::getWindowTitle() const\n{\n    return graphView->windowTitle;\n}\n\nDisassemblerGraphView *GraphWidget::getGraphView() const\n{\n    return graphView;\n}\n\nQString GraphWidget::getWidgetType()\n{\n    return \"Graph\";\n}\n\nvoid GraphWidget::prepareHeader()\n{\n    RzAnalysisFunction *f = Core()->functionIn(seekable->getOffset());\n    char *str = f ? rz_analysis_function_get_signature(f) : nullptr;\n    if (!str) {\n        header->hide();\n        return;\n    }\n    header->show();\n    header->setText(str);\n    free(str);\n}\n"
  },
  {
    "path": "src/widgets/GraphWidget.h",
    "content": "#ifndef GRAPHWIDGET_H\n#define GRAPHWIDGET_H\n\n#include \"MemoryDockWidget.h\"\n#include <QLineEdit>\n\nclass MainWindow;\nclass DisassemblerGraphView;\n\nclass GraphWidget : public MemoryDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit GraphWidget(MainWindow *main);\n    ~GraphWidget() override {}\n\n    DisassemblerGraphView *getGraphView() const;\n\n    static QString getWidgetType();\n\nsignals:\n    void graphClosed();\n\nprotected:\n    QWidget *widgetToFocusOnRaise() override;\n\nprivate:\n    void closeEvent(QCloseEvent *event) override;\n\n    QString getWindowTitle() const override;\n    void prepareHeader();\n\n    DisassemblerGraphView *graphView;\n    QLineEdit *header = nullptr;\n};\n\n#endif // GRAPHWIDGET_H\n"
  },
  {
    "path": "src/widgets/GraphvizLayout.cpp",
    "content": "#include \"GraphvizLayout.h\"\n\n#include <unordered_set>\n#include <unordered_map>\n#include <queue>\n#include <stack>\n#include <cassert>\n#include <sstream>\n#include <iomanip>\n#include <set>\n\n#include <gvc.h>\n\nGraphvizLayout::GraphvizLayout(LayoutType lineType, Direction direction)\n    : GraphLayout({}), direction(direction), layoutType(lineType)\n{\n}\n\nstatic GraphLayout::GraphEdge::ArrowDirection getArrowDirection(QPointF direction,\n                                                                bool preferVertical)\n{\n    if (abs(direction.x()) > abs(direction.y()) * (preferVertical ? 3.0 : 1.0)) {\n        if (direction.x() > 0) {\n            return GraphLayout::GraphEdge::Right;\n        } else {\n            return GraphLayout::GraphEdge::Left;\n        }\n    } else {\n        if (direction.y() > 0) {\n            return GraphLayout::GraphEdge::Down;\n        } else {\n            return GraphLayout::GraphEdge::Up;\n        }\n    }\n}\n\nstatic std::set<std::pair<ut64, ut64>> SelectLoopEdges(const GraphLayout::Graph &graph, ut64 entry)\n{\n    std::set<std::pair<ut64, ut64>> result;\n    // Run DFS to select backwards/loop edges\n    // 0 - not visited\n    // 1 - in stack\n    // 2 - visited\n    std::unordered_map<ut64, uint8_t> visited;\n    visited.reserve(graph.size());\n    std::stack<std::pair<ut64, size_t>> stack;\n    auto dfsFragment = [&visited, &graph, &stack, &result](ut64 first) {\n        visited[first] = 1;\n        stack.push({ first, 0 });\n        while (!stack.empty()) {\n            auto v = stack.top().first;\n            auto edge_index = stack.top().second;\n            auto blockIt = graph.find(v);\n            if (blockIt == graph.end()) {\n                continue;\n            }\n            const auto &block = blockIt->second;\n            if (edge_index < block.edges.size()) {\n                ++stack.top().second;\n                auto target = block.edges[edge_index].target;\n                auto &targetState = visited[target];\n                if (targetState == 0) {\n                    targetState = 1;\n                    stack.push({ target, 0 });\n                } else if (targetState == 1) {\n                    result.insert({ v, target });\n                }\n            } else {\n                stack.pop();\n                visited[v] = 2;\n            }\n        }\n    };\n\n    dfsFragment(entry);\n    for (auto &blockIt : graph) {\n        if (!visited[blockIt.first]) {\n            dfsFragment(blockIt.first);\n        }\n    }\n\n    return result;\n}\n\nvoid GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &blocks, ut64 entry,\n                                     int &width, int &height) const\n{\n    // https://gitlab.com/graphviz/graphviz/issues/1441\n#define STR(v) const_cast<char *>(v)\n\n    width = height = 10;\n    GVC_t *gvc = gvContext();\n    Agraph_t *g = agopen(STR(\"G\"), Agdirected, nullptr);\n\n    std::unordered_map<ut64, Agnode_t *> nodes;\n    for (const auto &block : blocks) {\n        nodes[block.first] = agnode(g, nullptr, true);\n    }\n\n    std::vector<std::string> strc;\n    strc.reserve(2 * blocks.size());\n    std::map<std::pair<ut64, ut64>, Agedge_t *> edges;\n\n    agsafeset(g, STR(\"splines\"),\n              layoutType == LayoutType::DotOrtho ? STR(\"ortho\") : STR(\"polyline\"), STR(\"\"));\n    switch (direction) {\n    case Direction::LR:\n        agsafeset(g, STR(\"rankdir\"), STR(\"LR\"), STR(\"\"));\n        break;\n    case Direction::TB:\n        agsafeset(g, STR(\"rankdir\"), STR(\"BT\"), STR(\"\"));\n        break;\n    }\n    agsafeset(g, STR(\"newrank\"), STR(\"true\"), STR(\"\"));\n    // graphviz has builtin 72 dpi setting for input that differs from output\n    // it's easier to use 72 everywhere\n    const double dpi = 72.0;\n    agsafeset(g, STR(\"dpi\"), STR(\"72\"), STR(\"\"));\n\n    auto widhAttr = agattr(g, AGNODE, STR(\"width\"), STR(\"1\"));\n    auto heightAatr = agattr(g, AGNODE, STR(\"height\"), STR(\"1\"));\n    agattr(g, AGNODE, STR(\"shape\"), STR(\"box\"));\n    agattr(g, AGNODE, STR(\"fixedsize\"), STR(\"true\"));\n    auto constraintAttr = agattr(g, AGEDGE, STR(\"constraint\"), STR(\"1\"));\n\n    std::ostringstream stream;\n    stream.imbue(std::locale::classic());\n    auto setFloatingPointAttr = [&stream](void *obj, Agsym_t *sym, double value) {\n        stream.str({});\n        stream << std::fixed << std::setw(4) << value;\n        auto str = stream.str();\n        agxset(obj, sym, STR(str.c_str()));\n    };\n\n    std::set<std::pair<ut64, ut64>> loopEdges = SelectLoopEdges(blocks, entry);\n\n    for (const auto &blockIt : blocks) {\n        auto u = nodes[blockIt.first];\n        auto &block = blockIt.second;\n\n        for (auto &edge : block.edges) {\n            auto v = nodes.find(edge.target);\n            if (v == nodes.end()) {\n                continue;\n            }\n            auto e = agedge(g, u, v->second, nullptr, true);\n            edges[{ blockIt.first, edge.target }] = e;\n            if (loopEdges.find({ blockIt.first, edge.target }) != loopEdges.end()) {\n                agxset(e, constraintAttr, STR(\"0\"));\n            }\n        }\n        setFloatingPointAttr(u, widhAttr, block.width / dpi);\n        setFloatingPointAttr(u, heightAatr, block.height / dpi);\n    }\n\n    const char *layoutEngine = \"dot\";\n    switch (layoutType) {\n    case LayoutType::DotOrtho:\n    case LayoutType::DotPolyline:\n        layoutEngine = \"dot\";\n        break;\n    case LayoutType::Sfdp:\n        layoutEngine = \"sfdp\";\n        break;\n    case LayoutType::Neato:\n        layoutEngine = \"neato\";\n        break;\n    case LayoutType::TwoPi:\n        layoutEngine = \"twopi\";\n        break;\n    case LayoutType::Circo:\n        layoutEngine = \"circo\";\n        break;\n    }\n\n    gvLayout(gvc, g, layoutEngine);\n\n    for (auto &blockIt : blocks) {\n        auto &block = blockIt.second;\n        auto u = nodes[blockIt.first];\n\n        auto pos = ND_coord(u);\n\n        auto w = ND_width(u) * dpi;\n        auto h = ND_height(u) * dpi;\n        block.x = pos.x - w / 2.0;\n        block.y = pos.y - h / 2.0;\n        width = std::max(width, block.x + block.width);\n        height = std::max(height, block.y + block.height);\n\n        for (auto &edge : block.edges) {\n            auto it = edges.find({ blockIt.first, edge.target });\n            if (it != edges.end()) {\n                auto e = it->second;\n                if (auto spl = ED_spl(e)) {\n                    for (size_t i = 0; i < 1 && i < spl->size; i++) {\n                        auto bz = spl->list[i];\n                        edge.polyline.clear();\n                        edge.polyline.reserve(bz.size + 1);\n                        for (size_t j = 0; j < bz.size; j++) {\n                            edge.polyline.push_back(QPointF(bz.list[j].x, bz.list[j].y));\n                        }\n                        QPointF last(0, 0);\n                        if (!edge.polyline.empty()) {\n                            last = edge.polyline.back();\n                        }\n                        if (bz.eflag) {\n                            QPointF tip = QPointF(bz.ep.x, bz.ep.y);\n                            edge.polyline.push_back(tip);\n                        }\n\n                        if (edge.polyline.size() >= 2) {\n                            // make sure self loops go from bottom to top\n                            if (edge.target == block.entry\n                                && edge.polyline.first().y() < edge.polyline.last().y()) {\n                                std::reverse(edge.polyline.begin(), edge.polyline.end());\n                            }\n                            auto it = std::prev(edge.polyline.end());\n                            QPointF direction = *it;\n                            direction -= *(--it);\n                            edge.arrow = getArrowDirection(direction,\n                                                           layoutType == LayoutType::DotPolyline);\n\n                        } else {\n                            edge.arrow = GraphEdge::Down;\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    gvFreeLayout(gvc, g);\n    agclose(g);\n    gvFreeContext(gvc);\n#undef STR\n}\n"
  },
  {
    "path": "src/widgets/GraphvizLayout.h",
    "content": "#ifndef GRAPHVIZLAYOUT_H\n#define GRAPHVIZLAYOUT_H\n\n#include \"core/Cutter.h\"\n#include \"GraphLayout.h\"\n\nclass GraphvizLayout : public GraphLayout\n{\npublic:\n    enum class LayoutType {\n        DotOrtho,\n        DotPolyline,\n        Sfdp,\n        Neato,\n        TwoPi,\n        Circo,\n    };\n    enum class Direction { TB, LR };\n    GraphvizLayout(LayoutType layoutType, Direction direction = Direction::TB);\n    virtual void CalculateLayout(std::unordered_map<ut64, GraphBlock> &blocks, ut64 entry,\n                                 int &width, int &height) const override;\n\nprivate:\n    Direction direction;\n    LayoutType layoutType;\n};\n\n#endif // GRAPHVIZLAYOUT_H\n"
  },
  {
    "path": "src/widgets/HeadersWidget.cpp",
    "content": "#include \"HeadersWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n\nHeadersModel::HeadersModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint HeadersModel::rowCount(const QModelIndex &) const\n{\n    return headers.count();\n}\n\nint HeadersModel::columnCount(const QModelIndex &) const\n{\n    return HeadersModel::ColumnCount;\n}\n\nQVariant HeadersModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= headers.count())\n        return QVariant();\n\n    const HeaderDescription &header = headers.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case OffsetColumn:\n            return RzAddressString(header.vaddr);\n        case NameColumn:\n            return header.name;\n        case ValueColumn:\n            return header.value;\n        case CommentColumn:\n            return Core()->getCommentAt(header.vaddr);\n        default:\n            return QVariant();\n        }\n    case HeaderDescriptionRole:\n        return QVariant::fromValue(header);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant HeadersModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case OffsetColumn:\n            return tr(\"Offset\");\n        case NameColumn:\n            return tr(\"Name\");\n        case ValueColumn:\n            return tr(\"Value\");\n        case CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA HeadersModel::address(const QModelIndex &index) const\n{\n    const HeaderDescription &header = headers.at(index.row());\n    return header.vaddr;\n}\n\nQString HeadersModel::name(const QModelIndex &index) const\n{\n    const HeaderDescription &header = headers.at(index.row());\n    return header.name;\n}\n\nHeadersProxyModel::HeadersProxyModel(HeadersModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n}\n\nbool HeadersProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    HeaderDescription item =\n            index.data(HeadersModel::HeaderDescriptionRole).value<HeaderDescription>();\n    return qhelpers::filterStringContains(item.name, this);\n}\n\nbool HeadersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    HeaderDescription leftHeader =\n            left.data(HeadersModel::HeaderDescriptionRole).value<HeaderDescription>();\n    HeaderDescription rightHeader =\n            right.data(HeadersModel::HeaderDescriptionRole).value<HeaderDescription>();\n\n    switch (left.column()) {\n    case HeadersModel::OffsetColumn:\n        return leftHeader.vaddr < rightHeader.vaddr;\n    case HeadersModel::NameColumn:\n        return leftHeader.name < rightHeader.name;\n    case HeadersModel::ValueColumn:\n        return leftHeader.value < rightHeader.value;\n    case HeadersModel::CommentColumn:\n        return Core()->getCommentAt(leftHeader.vaddr) < Core()->getCommentAt(rightHeader.vaddr);\n    default:\n        break;\n    }\n\n    return leftHeader.vaddr < rightHeader.vaddr;\n}\n\nHeadersWidget::HeadersWidget(MainWindow *main) : ListDockWidget(main)\n{\n    setWindowTitle(tr(\"Headers\"));\n    setObjectName(\"HeadersWidget\");\n\n    headersModel = new HeadersModel(this);\n    headersProxyModel = new HeadersProxyModel(headersModel, this);\n    setModels(headersProxyModel);\n    ui->treeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder);\n\n    ui->quickFilterView->closeFilter();\n    showCount(false);\n\n    connect(Core(), &CutterCore::codeRebased, this, &HeadersWidget::refreshHeaders);\n    connect(Core(), &CutterCore::refreshAll, this, &HeadersWidget::refreshHeaders);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(headersModel, HeadersModel::CommentColumn); });\n}\n\nHeadersWidget::~HeadersWidget() {}\n\nvoid HeadersWidget::refreshHeaders()\n{\n    headersModel->beginResetModel();\n    headersModel->headers = Core()->getAllHeaders();\n    headersModel->endResetModel();\n\n    ui->treeView->resizeColumnToContents(0);\n    ui->treeView->resizeColumnToContents(1);\n}\n"
  },
  {
    "path": "src/widgets/HeadersWidget.h",
    "content": "#ifndef HEADERSWIDGET_H\n#define HEADERSWIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"ListDockWidget.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidget;\n\nnamespace Ui {\nclass HeadersWidget;\n}\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass HeadersWidget;\n\nclass HeadersModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend HeadersWidget;\n\nprivate:\n    QList<HeaderDescription> headers;\n\npublic:\n    enum Column { OffsetColumn = 0, NameColumn, ValueColumn, CommentColumn, ColumnCount };\n    enum Role { HeaderDescriptionRole = Qt::UserRole };\n\n    HeadersModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n};\n\nclass HeadersProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    HeadersProxyModel(HeadersModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass HeadersWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit HeadersWidget(MainWindow *main);\n    ~HeadersWidget();\n\nprivate slots:\n    void refreshHeaders();\n\nprivate:\n    HeadersModel *headersModel;\n    HeadersProxyModel *headersProxyModel;\n};\n\n#endif // HEADERSWIDGET_H\n"
  },
  {
    "path": "src/widgets/HeapBinsGraphView.cpp",
    "content": "#include <Configuration.h>\n#include <dialogs/GlibcHeapInfoDialog.h>\n#include \"HeapBinsGraphView.h\"\n\nHeapBinsGraphView::HeapBinsGraphView(QWidget *parent, RzHeapBin *bin, MainWindow *main)\n    : SimpleTextGraphView(parent, main), heapBin(bin)\n{\n    chunkInfoAction = new QAction(tr(\"Detailed Chunk Info\"), this);\n    addressableItemContextMenu.addAction(chunkInfoAction);\n    addAction(chunkInfoAction);\n\n    connect(chunkInfoAction, &QAction::triggered, this, &HeapBinsGraphView::viewChunkInfo);\n\n    bits = Core()->getArchBits();\n\n    enableAddresses(true);\n}\n\nvoid HeapBinsGraphView::viewChunkInfo()\n{\n    GlibcHeapInfoDialog heapInfoDialog(selectedBlock, QString(), this);\n    heapInfoDialog.exec();\n}\n\nvoid HeapBinsGraphView::loadCurrentGraph()\n{\n    blockContent.clear();\n    blocks.clear();\n\n    RzListIter *iter;\n    RzHeapChunkListItem *item;\n    QVector<GraphHeapChunk> chunks;\n\n    // if the bin is a fastbin or not\n    bool singleLinkedBin = QString(heapBin->type) == QString(\"Fast\")\n            || QString(heapBin->type) == QString(\"Tcache\");\n\n    // store info about the chunks in a vector for easy access\n    CutterRzListForeach (heapBin->chunks, iter, RzHeapChunkListItem, item) {\n        GraphHeapChunk graphHeapChunk;\n        graphHeapChunk.addr = item->addr;\n        RzHeapChunkSimple *chunkInfo = Core()->getHeapChunk(item->addr);\n        if (!chunkInfo) {\n            break;\n        }\n        QString content = \"Chunk @ \" + RzAddressString(chunkInfo->addr) + \"\\nSize: \"\n                + RzHexString(chunkInfo->size) + \"\\nFd: \" + RzAddressString(chunkInfo->fd);\n\n        // fastbins lack bk pointer\n        if (!singleLinkedBin) {\n            content += \"\\nBk: \" + RzAddressString(chunkInfo->bk);\n        }\n        graphHeapChunk.fd = chunkInfo->fd;\n        graphHeapChunk.bk = chunkInfo->bk;\n        graphHeapChunk.content = content;\n        chunks.append(graphHeapChunk);\n        free(chunkInfo);\n    }\n\n    // fast and tcache bins have single linked list and other bins have double linked list\n    if (singleLinkedBin) {\n        display_single_linked_list(chunks);\n    } else {\n        display_double_linked_list(chunks);\n    }\n\n    cleanupEdges(blocks);\n    computeGraphPlacement();\n}\n\nvoid HeapBinsGraphView::display_single_linked_list(QVector<GraphHeapChunk> chunks)\n{\n    bool tcache = QString(heapBin->type) == QString(\"Tcache\");\n    int ptrSize = bits;\n    // add the graph block for the bin\n    GraphLayout::GraphBlock gbBin;\n    gbBin.entry = 1;\n    gbBin.edges.emplace_back(heapBin->fd);\n    QString content = tr(heapBin->type) + tr(\"bin \") + QString::number(heapBin->bin_num);\n    if (tcache) {\n        content += \"\\nEntry: \" + RzAddressString(heapBin->fd);\n    } else {\n        content += \"\\nFd: \" + RzAddressString(heapBin->fd);\n    }\n    addBlock(gbBin, content);\n\n    // add the graph blocks for the chunks\n    for (int i = 0; i < chunks.size(); i++) {\n        GraphLayout::GraphBlock gbChunk;\n        gbChunk.entry = chunks[i].addr;\n\n        if (tcache && chunks[i].fd) {\n            // base_address = address - 2 * PTR_SIZE\n            gbChunk.edges.emplace_back(chunks[i].fd - 2 * ptrSize);\n        } else {\n            gbChunk.edges.emplace_back(chunks[i].fd);\n        }\n\n        if (i == chunks.size() - 1 && heapBin->message) {\n            chunks[i].content += \"\\n\" + QString(heapBin->message);\n        }\n\n        addBlock(gbChunk, chunks[i].content, chunks[i].addr);\n    }\n\n    // add the END block if no message\n    if (!heapBin->message) {\n        GraphLayout::GraphBlock gbEnd;\n        gbEnd.entry = 0;\n        addBlock(gbEnd, \"END\", 0);\n    }\n}\n\nvoid HeapBinsGraphView::display_double_linked_list(QVector<GraphHeapChunk> chunks)\n{\n    // add the graph block for the bin\n    GraphLayout::GraphBlock gbBin;\n    gbBin.entry = heapBin->addr;\n    gbBin.edges.emplace_back(heapBin->fd);\n    gbBin.edges.emplace_back(heapBin->bk);\n    QString content = tr(heapBin->type) + tr(\"bin \") + QString::number(heapBin->bin_num) + tr(\" @ \")\n            + RzAddressString(heapBin->addr);\n    content += \"\\nFd: \" + RzAddressString(heapBin->fd);\n    content += \"\\nBk: \" + RzAddressString(heapBin->bk);\n\n    addBlock(gbBin, content, heapBin->addr);\n\n    // add the blocks for the chunks\n    for (int i = 0; i < chunks.size(); i++) {\n        GraphLayout::GraphBlock gbChunk;\n        gbChunk.entry = chunks[i].addr;\n        gbChunk.edges.emplace_back(chunks[i].fd);\n        gbChunk.edges.emplace_back(chunks[i].bk);\n\n        // if last chunk and there is message then show it in the chunk\n        if (i == chunks.size() - 1 && heapBin->message) {\n            chunks[i].content += \"\\n\" + QString(heapBin->message);\n        }\n\n        addBlock(gbChunk, chunks[i].content, chunks[i].addr);\n    }\n}\n\n// overriding this function from SimpleTextGraphView to support multiline text in graph block\n// most code is shared from that implementation\nvoid HeapBinsGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive)\n{\n    QRectF blockRect(block.x, block.y, block.width, block.height);\n\n    p.setPen(Qt::black);\n    p.setBrush(Qt::gray);\n    p.setFont(Config()->getFont());\n    p.drawRect(blockRect);\n\n    // Render node\n    auto &content = blockContent[block.entry];\n\n    p.setPen(QColor(0, 0, 0, 0));\n    p.setBrush(QColor(0, 0, 0, 100));\n    p.setPen(QPen(graphNodeColor, 1));\n\n    bool blockSelected = interactive && (block.entry == selectedBlock);\n    if (blockSelected) {\n        p.setBrush(disassemblySelectedBackgroundColor);\n    } else {\n        p.setBrush(disassemblyBackgroundColor);\n    }\n    // Draw basic block background\n    p.drawRect(blockRect);\n\n    // Stop rendering text when it's too small\n    auto transform = p.combinedTransform();\n    QRect screenChar = transform.mapRect(QRect(0, 0, ACharWidth, charHeight));\n\n    if (screenChar.width() < Config()->getGraphMinFontSize()) {\n        return;\n    }\n\n    p.setPen(palette().color(QPalette::WindowText));\n\n    // Render node text\n    // the only change from SimpleTextGraphView implementation\n    p.drawText(blockRect, Qt::AlignCenter, content.text);\n}\n\n// overriding this function to support multiline text in graph blocks\nvoid HeapBinsGraphView::addBlock(GraphLayout::GraphBlock block, const QString &text, RVA address)\n{\n    auto &content = blockContent[block.entry];\n    content.text = text;\n    content.address = address;\n\n    int height = 1;\n    double width = 0;\n\n    // split text into different lines\n    auto lines = text.split(\"\\n\", CUTTER_QT_SKIP_EMPTY_PARTS);\n\n    // width of the block is the maximum width of a line\n    for (QString &line : lines) {\n        width = std::max(mFontMetrics->width(line), width);\n    }\n    block.width = static_cast<int>(width + padding);\n    block.height = (height * charHeight) * lines.length() + padding;\n    GraphView::addBlock(std::move(block));\n}\n\n// overriding to support detailed heap info action in context menu\nvoid HeapBinsGraphView::blockContextMenuRequested(GraphView::GraphBlock &block,\n                                                  QContextMenuEvent *event, QPoint /*pos*/)\n{\n    if (haveAddresses) {\n        const auto &content = blockContent[block.entry];\n        selectedBlock = content.address;\n        addressableItemContextMenu.setTarget(content.address, content.text);\n        QPoint pos = event->globalPos();\n\n        if (event->reason() != QContextMenuEvent::Mouse) {\n            QPoint blockPosition(block.x + block.width / 2, block.y + block.height / 2);\n            blockPosition = logicalToViewCoordinates(blockPosition);\n            if (viewport()->rect().contains(blockPosition)) {\n                pos = mapToGlobal(blockPosition);\n            }\n        }\n        addressableItemContextMenu.exec(pos);\n        event->accept();\n    }\n}\n"
  },
  {
    "path": "src/widgets/HeapBinsGraphView.h",
    "content": "#ifndef CUTTER_HEAPBINSGRAPHVIEW_H\n#define CUTTER_HEAPBINSGRAPHVIEW_H\n#include \"SimpleTextGraphView.h\"\n\nclass HeapBinsGraphView : public SimpleTextGraphView\n{\n    Q_OBJECT\n    struct GraphHeapChunk\n    {\n        QString content;\n        ut64 addr;\n        ut64 fd;\n        ut64 bk;\n    };\n\npublic:\n    explicit HeapBinsGraphView(QWidget *parent, RzHeapBin *bin, MainWindow *main);\n\nprotected:\n    void loadCurrentGraph() override;\n    void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override;\n    void addBlock(GraphLayout::GraphBlock block, const QString &text,\n                  RVA address = RVA_INVALID) override;\n    void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,\n                                   QPoint) override;\n\nprivate slots:\n    void viewChunkInfo();\n\nprivate:\n    RzHeapBin *heapBin;\n    void display_single_linked_list(QVector<GraphHeapChunk>);\n    void display_double_linked_list(QVector<GraphHeapChunk>);\n    QAction *chunkInfoAction;\n    RVA selectedBlock;\n    int bits;\n};\n\n#endif // CUTTER_HEAPBINSGRAPHVIEW_H\n"
  },
  {
    "path": "src/widgets/HeapDockWidget.cpp",
    "content": "#include \"HeapDockWidget.h\"\n#include \"ui_HeapDockWidget.h\"\n#include \"widgets/GlibcHeapWidget.h\"\n\nHeapDockWidget::HeapDockWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::HeapDockWidget), main(main)\n{\n    ui->setupUi(this);\n\n    ui->allocatorSelector->addItem(\"Glibc Heap\");\n    ui->verticalLayout->setContentsMargins(0, 0, 0, 0);\n\n    connect<void (QComboBox::*)(int)>(ui->allocatorSelector, &QComboBox::currentIndexChanged, this,\n                                      &HeapDockWidget::onAllocatorSelected);\n\n    // select Glibc heap by default\n    onAllocatorSelected(0);\n}\n\nHeapDockWidget::~HeapDockWidget()\n{\n    delete ui;\n}\n\nvoid HeapDockWidget::onAllocatorSelected(int index)\n{\n    if (index >= AllocatorCount)\n        return;\n\n    // remove the current heap widget from layout\n    if (currentHeapWidget) {\n        ui->verticalLayout->removeWidget(currentHeapWidget);\n        delete currentHeapWidget;\n    }\n\n    // change widget depending upon selected allocator\n    if (index == Glibc) {\n        currentHeapWidget = new GlibcHeapWidget(main, this);\n    }\n    ui->verticalLayout->addWidget(currentHeapWidget);\n}\n"
  },
  {
    "path": "src/widgets/HeapDockWidget.h",
    "content": "#ifndef HEAPDOCKWIDGET_H\n#define HEAPDOCKWIDGET_H\n\n#include <QDockWidget>\n#include \"CutterDockWidget.h\"\n\nnamespace Ui {\nclass HeapDockWidget;\n}\n\nclass HeapDockWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit HeapDockWidget(MainWindow *main);\n    ~HeapDockWidget();\nprivate slots:\n    void onAllocatorSelected(int index);\n\nprivate:\n    enum Allocator { Glibc = 0, AllocatorCount };\n    Ui::HeapDockWidget *ui;\n    MainWindow *main;\n    QWidget *currentHeapWidget = nullptr;\n};\n\n#endif // HEAPDOCKWIDGET_H\n"
  },
  {
    "path": "src/widgets/HeapDockWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>HeapDockWidget</class>\n <widget class=\"QDockWidget\" name=\"HeapDockWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Heap</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <item>\n     <widget class=\"QComboBox\" name=\"allocatorSelector\"/>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/HexWidget.cpp",
    "content": "#include \"HexWidget.h\"\n#include \"Cutter.h\"\n#include \"Configuration.h\"\n#include \"dialogs/MarkDialog.h\"\n#include \"dialogs/WriteCommandsDialogs.h\"\n#include \"dialogs/CommentsDialog.h\"\n#include \"dialogs/FlagDialog.h\"\n#include \"widgets/AddressRangeScrollBar.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QPainter>\n#include <QPaintEvent>\n#include <QResizeEvent>\n#include <QMouseEvent>\n#include <QKeyEvent>\n#include <QWheelEvent>\n#include <QtEndian>\n#include <QScrollBar>\n#include <QMenu>\n#include <QClipboard>\n#include <QApplication>\n#include <QInputDialog>\n#include <QPushButton>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QRegularExpression>\n#include <QToolTip>\n#include <QActionGroup>\n\nstatic constexpr uint64_t MAX_COPY_SIZE = 128 * 1024 * 1024;\nstatic constexpr int MAX_LINE_WIDTH_PRESET = 32;\nstatic constexpr int MAX_LINE_WIDTH_BYTES = 128 * 1024;\nstatic constexpr int WARNING_TIME_MS = 500;\n\nHexWidget::HexWidget(QWidget *parent)\n    : QScrollArea(parent),\n      cursorEnabled(true),\n      cursorOnAscii(false),\n      updatingSelection(false),\n      itemByteLen(1),\n      itemGroupSize(1),\n      rowSizeBytes(16),\n      columnMode(ColumnMode::PowerOf2),\n      itemFormat(ItemFormatHex),\n      itemBigEndian(false),\n      addrCharLen(AddrWidth64),\n      showHeader(true),\n      showAscii(true),\n      showExHex(true),\n      showExAddr(true),\n      ioModesController(parent),\n      warningTimer(this)\n{\n    setMouseTracking(true);\n    setFocusPolicy(Qt::FocusPolicy::StrongFocus);\n    connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &HexWidget::updateViewport);\n\n    connect(Config(), &Configuration::colorsUpdated, this, &HexWidget::updateColors);\n    connect(Config(), &Configuration::fontsUpdated, this,\n            [this]() { setMonospaceFont(Config()->getFont()); });\n\n    vScrollBar = new AddressRangeScrollBar(this);\n    setVerticalScrollBar(vScrollBar);\n    vScrollBar->setPageStep(10);\n    vScrollBar->setSingleStep(1);\n    connect(vScrollBar, &AddressRangeScrollBar::scrolled, this,\n            [this](int lines) { scrollLines(lines, true); });\n    connect(vScrollBar, &QScrollBar::valueChanged, this,\n            [this](int) { setStartAddress(vScrollBar->address()); });\n    connect(vScrollBar, &AddressRangeScrollBar::hideScrollBar, this,\n            [this]() { setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); });\n    connect(vScrollBar, &AddressRangeScrollBar::showScrollBar, this,\n            [this]() { setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); });\n    vScrollBar->refreshRange();\n\n    auto sizeActionGroup = new QActionGroup(this);\n    for (int i = 1; i <= 8; i *= 2) {\n        QAction *action = new QAction(QString::number(i), this);\n        action->setCheckable(true);\n        action->setActionGroup(sizeActionGroup);\n        connect(action, &QAction::triggered, this, [=]() { setItemSize(i); });\n        actionsItemSize.append(action);\n    }\n    actionsItemSize.at(0)->setChecked(true);\n\n    /* Follow the order in ItemFormat enum */\n    QStringList names;\n    names << tr(\"Hexadecimal\");\n    names << tr(\"Octal\");\n    names << tr(\"Decimal\");\n    names << tr(\"Signed decimal\");\n    names << tr(\"Float\");\n\n    auto formatActionGroup = new QActionGroup(this);\n    for (int i = 0; i < names.length(); ++i) {\n        QAction *action = new QAction(names.at(i), this);\n        action->setCheckable(true);\n        action->setActionGroup(formatActionGroup);\n        connect(action, &QAction::triggered, this,\n                [=]() { setItemFormat(static_cast<ItemFormat>(i)); });\n        actionsItemFormat.append(action);\n    }\n    actionsItemFormat.at(0)->setChecked(true);\n    actionsItemFormat.at(ItemFormatFloat)->setEnabled(false);\n\n    rowSizeMenu = new QMenu(tr(\"Bytes per row\"), this);\n    auto columnsActionGroup = new QActionGroup(this);\n    for (int i = 1; i <= MAX_LINE_WIDTH_PRESET; i *= 2) {\n        QAction *action = new QAction(QString::number(i), rowSizeMenu);\n        action->setCheckable(true);\n        action->setActionGroup(columnsActionGroup);\n        connect(action, &QAction::triggered, this, [=]() { setFixedLineSize(i); });\n        rowSizeMenu->addAction(action);\n    }\n    rowSizeMenu->addSeparator();\n    actionRowSizePowerOf2 = new QAction(tr(\"Power of 2\"), this);\n    actionRowSizePowerOf2->setCheckable(true);\n    actionRowSizePowerOf2->setActionGroup(columnsActionGroup);\n    connect(actionRowSizePowerOf2, &QAction::triggered, this,\n            [=]() { setColumnMode(ColumnMode::PowerOf2); });\n    rowSizeMenu->addAction(actionRowSizePowerOf2);\n\n    actionItemBigEndian = new QAction(tr(\"Big Endian\"), this);\n    actionItemBigEndian->setCheckable(true);\n    actionItemBigEndian->setEnabled(false);\n    connect(actionItemBigEndian, &QAction::triggered, this, &HexWidget::setItemEndianness);\n\n    actionHexPairs = new QAction(tr(\"Bytes as pairs\"), this);\n    actionHexPairs->setCheckable(true);\n    connect(actionHexPairs, &QAction::triggered, this, &HexWidget::onHexPairsModeEnabled);\n\n    actionCopy = Shortcuts()->makeAction(\"Hex.copy\", this);\n    addAction(actionCopy);\n    actionCopy->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n    connect(actionCopy, &QAction::triggered, this, &HexWidget::copy);\n\n    actionCopyAddress = Shortcuts()->makeAction(\"General.copyAddress\", this);\n    actionCopyAddress->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n    connect(actionCopyAddress, &QAction::triggered, this, &HexWidget::copyAddress);\n    addAction(actionCopyAddress);\n\n    // Add comment option\n    actionComment = Shortcuts()->makeAction(\"General.addComment\", this);\n    actionComment->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n    connect(actionComment, &QAction::triggered, this, &HexWidget::onActionAddCommentTriggered);\n    addAction(actionComment);\n\n    // Add flag option\n    actionAddFlag = Shortcuts()->makeAction(\"Hex.addFlag\", this);\n    actionAddFlag->setText(tr(\"Add flag at %1\").arg(RzAddressString(getLocationAddress())));\n    actionAddFlag->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n    connect(actionAddFlag, &QAction::triggered, this, &HexWidget::onActionAddFlagTriggered);\n    connect(this, &HexWidget::positionChanged, this, [this](RVA pos) {\n        RzAnalysisFunction *fcn = Core()->functionAt(pos);\n        if (fcn) {\n            actionAddFlag->setVisible(false);\n        } else {\n            actionAddFlag->setVisible(true);\n        }\n    });\n    addAction(actionAddFlag);\n\n    // delete comment option\n    actionDeleteComment = new QAction(tr(\"Delete Comment\"), this);\n    actionDeleteComment->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);\n    connect(actionDeleteComment, &QAction::triggered, this,\n            &HexWidget::onActionDeleteCommentTriggered);\n    addAction(actionDeleteComment);\n\n    actionSelectRange = new QAction(tr(\"Select range\"), this);\n    connect(actionSelectRange, &QAction::triggered, this,\n            [this]() { rangeDialog.open(cursor.address); });\n    addAction(actionSelectRange);\n    connect(&rangeDialog, &QDialog::accepted, this, &HexWidget::onRangeDialogAccepted);\n\n    actionsWriteString.reserve(5);\n    QAction *actionWriteString = new QAction(tr(\"Write string\"), this);\n    connect(actionWriteString, &QAction::triggered, this, &HexWidget::w_writeString);\n    actionsWriteString.append(actionWriteString);\n\n    QAction *actionWriteLenString = new QAction(tr(\"Write length and string\"), this);\n    connect(actionWriteLenString, &QAction::triggered, this, &HexWidget::w_writePascalString);\n    actionsWriteString.append(actionWriteLenString);\n\n    QAction *actionWriteWideString = new QAction(tr(\"Write wide string\"), this);\n    connect(actionWriteWideString, &QAction::triggered, this, &HexWidget::w_writeWideString);\n    actionsWriteString.append(actionWriteWideString);\n\n    QAction *actionWriteCString = new QAction(tr(\"Write zero terminated string\"), this);\n    connect(actionWriteCString, &QAction::triggered, this, &HexWidget::w_writeCString);\n    actionsWriteString.append(actionWriteCString);\n\n    QAction *actionWrite64 = new QAction(tr(\"Write a decoded or encoded Base64 string\"), this);\n    connect(actionWrite64, &QAction::triggered, this, &HexWidget::w_write64);\n    actionsWriteString.append(actionWrite64);\n\n    actionsWriteOther.reserve(5);\n    QAction *actionWriteBytes = new QAction(tr(\"Write hex bytes\"), this);\n    connect(actionWriteBytes, &QAction::triggered, this, &HexWidget::w_writeBytes);\n    actionsWriteOther.append(actionWriteBytes);\n\n    QAction *actionWriteZeros = new QAction(tr(\"Write zeros\"), this);\n    connect(actionWriteZeros, &QAction::triggered, this, &HexWidget::w_writeZeros);\n    actionsWriteOther.append(actionWriteZeros);\n\n    QAction *actionWriteRandom = new QAction(tr(\"Write random bytes\"), this);\n    connect(actionWriteRandom, &QAction::triggered, this, &HexWidget::w_writeRandom);\n    actionsWriteOther.append(actionWriteRandom);\n\n    QAction *actionDuplicateFromOffset = new QAction(tr(\"Duplicate from offset\"), this);\n    connect(actionDuplicateFromOffset, &QAction::triggered, this, &HexWidget::w_duplFromOffset);\n    actionsWriteOther.append(actionDuplicateFromOffset);\n\n    QAction *actionIncDec = new QAction(tr(\"Increment/Decrement\"), this);\n    connect(actionIncDec, &QAction::triggered, this, &HexWidget::w_increaseDecrease);\n    actionsWriteOther.append(actionIncDec);\n\n    actionKeyboardEdit = new QAction(tr(\"Edit with keyboard\"), this);\n    actionKeyboardEdit->setCheckable(true);\n    connect(actionKeyboardEdit, &QAction::triggered, this, &HexWidget::onKeyboardEditTriggered);\n    connect(actionKeyboardEdit, &QAction::toggled, this, &HexWidget::onKeyboardEditChanged);\n\n    connect(this, &HexWidget::selectionChanged, this,\n            [this](Selection newSelection) { actionCopy->setEnabled(!newSelection.empty); });\n\n    actionAddMark = Shortcuts()->makeAction(\"Hex.addMark\", this);\n    connect(actionAddMark, &QAction::triggered, this, &HexWidget::onActionAddMarkTriggered);\n    addAction(actionAddMark);\n\n    updateMetrics();\n    updateItemLength();\n\n    startAddress = 0ULL;\n    cursor.address = 0ULL;\n    data.reset(new MemoryData());\n    oldData.reset(new MemoryData());\n\n    fetchData();\n    updateCursorMeta();\n\n    connect(&cursor.blinkTimer, &QTimer::timeout, this, &HexWidget::onCursorBlinked);\n    cursor.setBlinkPeriod(1000);\n    cursor.startBlinking();\n\n    updateColors();\n\n    warningTimer.setSingleShot(true);\n    connect(&warningTimer, &QTimer::timeout, this, &HexWidget::hideWarningRect);\n}\n\nvoid HexWidget::setMonospaceFont(const QFont &font)\n{\n    if (!(font.styleHint() & QFont::Monospace)) {\n        /* FIXME: Use default monospace font\n        setFont(XXX); */\n    }\n    QScrollArea::setFont(font);\n    monospaceFont = font.resolve(this->font());\n    updateMetrics();\n    fetchData();\n    updateCursorMeta();\n\n    updateViewport();\n}\n\nvoid HexWidget::setItemSize(int nbytes)\n{\n    static const QVector<int> values({ 1, 2, 4, 8 });\n\n    if (!values.contains(nbytes))\n        return;\n\n    finishEditingWord();\n\n    itemByteLen = nbytes;\n    if (itemByteLen > rowSizeBytes) {\n        rowSizeBytes = itemByteLen;\n    }\n\n    actionsItemFormat.at(ItemFormatFloat)->setEnabled(nbytes >= 4);\n    actionItemBigEndian->setEnabled(nbytes != 1);\n\n    refreshWordEditState();\n\n    updateItemLength();\n    if (!cursorOnAscii && cursor.address % itemByteLen) {\n        moveCursor(-int(cursor.address % itemByteLen));\n    }\n    fetchData();\n    updateCursorMeta();\n\n    updateViewport();\n}\n\nvoid HexWidget::setItemFormat(ItemFormat format)\n{\n    finishEditingWord();\n\n    itemFormat = format;\n\n    bool sizeEnabled = true;\n    if (format == ItemFormatFloat)\n        sizeEnabled = false;\n    actionsItemSize.at(0)->setEnabled(sizeEnabled);\n    actionsItemSize.at(1)->setEnabled(sizeEnabled);\n\n    refreshWordEditState();\n\n    updateItemLength();\n    fetchData();\n    updateCursorMeta();\n\n    updateViewport();\n}\n\nvoid HexWidget::setItemGroupSize(int size)\n{\n    itemGroupSize = size;\n\n    updateCounts();\n    fetchData();\n    updateCursorMeta();\n\n    updateViewport();\n}\n\n/**\n * @brief Checks if Item at the address changed compared to the last read data.\n * @param address Address of Item to be compared.\n * @return True if Item is different, False if Item is equal or last read didn't contain the\n * address.\n * @see HexWidget#readItem\n *\n * Checks if current Item at the address changed compared to the last read data.\n * It is assumed that the current read data buffer contains the address.\n */\nbool HexWidget::isItemDifferentAt(uint64_t address)\n{\n    char oldItem[sizeof(uint64_t)] = {};\n    char newItem[sizeof(uint64_t)] = {};\n    if (data->copy(newItem, address, static_cast<size_t>(itemByteLen))\n        && oldData->copy(oldItem, address, static_cast<size_t>(itemByteLen))) {\n        return memcmp(oldItem, newItem, sizeof(oldItem)) != 0;\n    }\n    return false;\n}\n\nvoid HexWidget::updateCounts()\n{\n    actionHexPairs->setEnabled(rowSizeBytes > 1 && itemByteLen == 1\n                               && itemFormat == ItemFormat::ItemFormatHex);\n    actionHexPairs->setChecked(Core()->getConfigb(\"hex.pairs\"));\n    if (actionHexPairs->isChecked() && actionHexPairs->isEnabled()) {\n        itemGroupSize = 2;\n    } else {\n        itemGroupSize = 1;\n    }\n\n    if (columnMode == ColumnMode::PowerOf2) {\n        int last_good_size = itemGroupByteLen();\n        for (int i = itemGroupByteLen(); i <= MAX_LINE_WIDTH_BYTES; i *= 2) {\n            rowSizeBytes = i;\n            itemColumns = rowSizeBytes / itemGroupByteLen();\n            updateAreasPosition();\n            if (horizontalScrollBar()->maximum() == 0) {\n                last_good_size = rowSizeBytes;\n            } else {\n                break;\n            }\n        }\n        rowSizeBytes = last_good_size;\n    }\n\n    itemColumns = rowSizeBytes / itemGroupByteLen();\n\n    // ensure correct action is selected when changing line size programmatically\n    if (columnMode == ColumnMode::Fixed) {\n        int w = 1;\n        const auto &actions = rowSizeMenu->actions();\n        for (auto action : actions) {\n            action->setChecked(false);\n        }\n        for (auto action : actions) {\n            if (w > MAX_LINE_WIDTH_PRESET) {\n                break;\n            }\n            if (rowSizeBytes == w) {\n                action->setChecked(true);\n            }\n            w *= 2;\n        }\n    } else if (columnMode == ColumnMode::PowerOf2) {\n        actionRowSizePowerOf2->setChecked(true);\n    }\n\n    updateAreasPosition();\n}\n\nvoid HexWidget::setFixedLineSize(int lineSize)\n{\n    if (lineSize < 1 || lineSize < itemGroupByteLen() || lineSize % itemGroupByteLen()) {\n        updateCounts();\n        return;\n    }\n    rowSizeBytes = lineSize;\n    columnMode = ColumnMode::Fixed;\n\n    updateCounts();\n    fetchData();\n    updateCursorMeta();\n\n    updateViewport();\n}\n\nvoid HexWidget::setColumnMode(ColumnMode mode)\n{\n    columnMode = mode;\n\n    updateCounts();\n    fetchData();\n    updateCursorMeta();\n\n    updateViewport();\n}\n\nvoid HexWidget::selectRange(RVA start, RVA end)\n{\n    BasicCursor endCursor(end);\n    endCursor += 1;\n    setCursorAddr(endCursor);\n    selection.set(start, end);\n    cursorEnabled = false;\n    emit selectionChanged(getSelection());\n}\n\nvoid HexWidget::clearSelection()\n{\n    setCursorAddr(BasicCursor(cursor.address), false);\n    emit selectionChanged(getSelection());\n}\n\nHexWidget::Selection HexWidget::getSelection()\n{\n    return Selection { selection.isEmpty(), selection.start(), selection.end() };\n}\n\nvoid HexWidget::seek(uint64_t address)\n{\n    if (!cursorOnAscii) {\n        // when other widget causes seek to the middle of word\n        // switch to ascii column which operates with byte positions\n        auto viewOffset = startAddress % itemByteLen;\n        auto addrOffset = address % itemByteLen;\n        if ((addrOffset + itemByteLen - viewOffset) % itemByteLen) {\n            setCursorOnAscii(true);\n        }\n    }\n    setCursorAddr(BasicCursor(address));\n}\n\nvoid HexWidget::refresh()\n{\n    fetchData();\n    updateViewport();\n}\n\nvoid HexWidget::setItemEndianness(bool bigEndian)\n{\n    finishEditingWord();\n    itemBigEndian = bigEndian;\n\n    updateCursorMeta(); // Update cached item character\n\n    updateViewport();\n}\n\nvoid HexWidget::updateColors()\n{\n    borderColor = Config()->getColor(\"gui.border\");\n    backgroundColor = Config()->getColor(\"gui.background\");\n    b0x00Color = Config()->getColor(\"b0x00\");\n    b0x7fColor = Config()->getColor(\"b0x7f\");\n    b0xffColor = Config()->getColor(\"b0xff\");\n    printableColor = Config()->getColor(\"ai.write\");\n    defColor = Config()->getColor(\"btext\");\n    addrColor = Config()->getColor(\"func_var_addr\");\n    diffColor = Config()->getColor(\"graph.diff.unmatch\");\n    warningColor = QColor(\"red\");\n\n    updateCursorMeta();\n    updateViewport();\n}\n\nvoid HexWidget::paintEvent(QPaintEvent *event)\n{\n    QPainter painter(viewport());\n    painter.setFont(monospaceFont);\n\n    int xOffset = horizontalScrollBar()->value();\n    if (xOffset > 0)\n        painter.translate(QPoint(-xOffset, 0));\n\n    if (event->rect() == cursor.screenPos.toAlignedRect()) {\n        /* Cursor blink */\n        drawCursor(painter);\n        return;\n    }\n\n    painter.fillRect(event->rect().translated(xOffset, 0), backgroundColor);\n\n    drawHeader(painter);\n\n    drawAddrArea(painter);\n    drawItemArea(painter);\n    drawAsciiArea(painter);\n\n    if (warningRectVisible) {\n        painter.setPen(warningColor);\n        painter.drawRect(warningRect);\n    }\n\n    if (!cursorEnabled)\n        return;\n\n    drawCursor(painter, true);\n}\n\nvoid HexWidget::updateWidth()\n{\n    int max = (showAscii ? asciiArea.right() : itemArea.right()) - viewport()->width();\n    if (max < 0)\n        max = 0;\n    else\n        max += charWidth;\n    horizontalScrollBar()->setMaximum(max);\n    horizontalScrollBar()->setSingleStep(charWidth);\n}\n\nbool HexWidget::isFixedWidth() const\n{\n    return itemFormat == ItemFormatHex || itemFormat == ItemFormatOct;\n}\n\nvoid HexWidget::resizeEvent(QResizeEvent *event)\n{\n    int oldByteCount = bytesPerScreen();\n    updateCounts();\n\n    if (event->oldSize().height() == event->size().height() && oldByteCount == bytesPerScreen())\n        return;\n\n    updateAreasHeight();\n    fetchData(); // rowCount was changed\n    updateCursorMeta();\n\n    updateViewport();\n}\n\nvoid HexWidget::mouseMoveEvent(QMouseEvent *event)\n{\n    QPoint pos = event->pos();\n    pos.rx() += horizontalScrollBar()->value();\n\n    auto mouseAddr = mousePosToAddr(pos).address;\n\n    QString infoText;\n    if (!updatingSelection && (itemArea.contains(pos) || asciiArea.contains(pos))) {\n        QString metaData = getFlagsAndComment(mouseAddr);\n        if (!metaData.isEmpty() && itemArea.contains(pos)) {\n            infoText = metaData.replace(\",\", \", \");\n        }\n\n        const auto marks = Core()->getMarksAt(mouseAddr);\n        for (const auto &mark : marks) {\n            if (mark.realname.isEmpty()) {\n                continue;\n            }\n            if (!infoText.isEmpty()) {\n                infoText += \"<br>\";\n            }\n            QColor c = mark.color;\n            infoText += QString(\"<span style='white-space:nowrap; color: rgba(%1, %2, %3, %4);'>● \"\n                                \"</span> %5\")\n                                .arg(c.red())\n                                .arg(c.green())\n                                .arg(c.blue())\n                                .arg(MARK_ALPHA_F)\n                                .arg(mark.realname.toHtmlEscaped());\n        }\n        if (!infoText.isEmpty()) {\n            // forces tooltip to follow cursor movement\n            QToolTip::showText(mapToGlobal(event->pos()), infoText + \" \", this);\n\n            QToolTip::showText(mapToGlobal(event->pos()), infoText, this);\n        } else {\n            QToolTip::hideText();\n        }\n    } else {\n        QToolTip::hideText();\n    }\n\n    if (!updatingSelection) {\n        if (itemArea.contains(pos) || asciiArea.contains(pos))\n            setCursor(Qt::IBeamCursor);\n        else\n            setCursor(Qt::ArrowCursor);\n        return;\n    }\n\n    auto &area = currentArea();\n    if (pos.x() < area.left())\n        pos.setX(area.left());\n    else if (pos.x() > area.right())\n        pos.setX(area.right());\n    auto addr = currentAreaPosToAddr(pos, true);\n    setCursorAddr(addr, true);\n\n    /* Stop blinking */\n    cursorEnabled = false;\n\n    updateViewport();\n}\n\nvoid HexWidget::mousePressEvent(QMouseEvent *event)\n{\n    QPoint pos(event->pos());\n    pos.rx() += horizontalScrollBar()->value();\n\n    if (event->button() == Qt::LeftButton) {\n        bool selectingData = itemArea.contains(pos);\n        bool selecting = selectingData || asciiArea.contains(pos);\n        bool holdingShift = event->modifiers() == Qt::ShiftModifier;\n\n        // move cursor within actively edited item\n        if (selectingData && !holdingShift && editWordState >= EditWordState::WriteNotEdited) {\n\n            auto editWordArea = itemRectangle(cursor.address - startAddress);\n            if (editWordArea.contains(pos)) {\n                int wordOffset = 0;\n                auto cursorPosition = screenPosToAddr(pos, false, &wordOffset);\n                if (cursorPosition.address == cursor.address\n                    // allow selecting after last character only when cursor limited to current word\n                    && (wordOffset < editWord.length()\n                        || navigationMode == HexNavigationMode::WordChar)) {\n                    editWordPos = std::max(0, wordOffset);\n                    editWordPos = std::min<int>(editWordPos, editWord.length());\n\n                    if (isFixedWidth()) {\n                        updatingSelection = true;\n                        auto selectionCursor = cursorPosition;\n                        if (editWordPos > itemCharLen / 2) {\n                            selectionCursor += itemByteLen;\n                        }\n                        selection.init(selectionCursor);\n                    }\n\n                    updateViewport();\n                    return;\n                }\n            }\n        }\n\n        // cursor within any item if the mode allows\n        if (selectingData && !holdingShift && editWordState >= EditWordState::WriteNotStarted\n            && navigationMode == HexNavigationMode::AnyChar) {\n            updatingSelection = true;\n            setCursorOnAscii(false);\n            int wordOffset = 0;\n            auto cursorPosition = screenPosToAddr(pos, false, &wordOffset);\n            finishEditingWord();\n            if (isFixedWidth() && wordOffset >= itemCharLen - itemPrefixLen) {\n                wordOffset = 0;\n                cursorPosition += itemByteLen;\n            }\n            setCursorAddr(cursorPosition, holdingShift);\n            auto selectionPosition = currentAreaPosToAddr(pos, true);\n            selection.init(selectionPosition);\n            emit selectionChanged(getSelection());\n\n            if (wordOffset > 0) {\n                startEditWord();\n                editWordPos = std::min<int>(wordOffset, editWord.length() - 1);\n            }\n            updateViewport();\n            return;\n        }\n\n        if (selecting) {\n            finishEditingWord();\n\n            updatingSelection = true;\n            setCursorOnAscii(!selectingData);\n            auto cursorPosition = currentAreaPosToAddr(pos, true);\n            setCursorAddr(cursorPosition, holdingShift);\n            updateViewport();\n        }\n    }\n}\n\nvoid HexWidget::mouseDoubleClickEvent(QMouseEvent *event)\n{\n    QPoint pos(event->pos());\n    pos.rx() += horizontalScrollBar()->value();\n\n    if (event->button() == Qt::LeftButton && !isFixedWidth()\n        && editWordState == EditWordState::WriteNotStarted && itemArea.contains(pos)) {\n        int wordOffset = 0;\n        auto cursorPosition = screenPosToAddr(pos, false, &wordOffset);\n        setCursorAddr(cursorPosition, false);\n        startEditWord();\n        int padding = std::max<int>(0, itemCharLen - editWord.length());\n        editWordPos = std::max(0, wordOffset - padding);\n        editWordPos = std::min<int>(editWordPos, editWord.length());\n    }\n}\n\nvoid HexWidget::mouseReleaseEvent(QMouseEvent *event)\n{\n    if (event->button() == Qt::LeftButton) {\n        if (selection.isEmpty()) {\n            selection.init(BasicCursor(cursor.address));\n            cursorEnabled = true;\n            updateViewport();\n        }\n        updatingSelection = false;\n    }\n}\n\nvoid HexWidget::wheelEvent(QWheelEvent *event)\n{\n\n    // according to Qt doc 1 row per 5 degrees, angle measured in 1/8 of degree\n    int dy = event->angleDelta().y() / (8 * 5);\n    scrollLines(dy);\n    vScrollBar->showTransientScrollBar();\n}\n\nbool HexWidget::validCharForEdit(QChar digit)\n{\n    switch (itemFormat) {\n    case ItemFormatHex:\n        return (digit >= '0' && digit <= '9') || (digit >= 'a' && digit <= 'f')\n                || (digit >= 'A' && digit <= 'F');\n    case ItemFormatOct: {\n        if (editWordPos > 0) {\n            return (digit >= '0' && digit <= '7');\n        } else {\n            int bitsInMSD = (itemByteLen * 8) % 3;\n            int biggestDigit = (1 << bitsInMSD) - 1;\n            return digit >= '0' && digit <= char('0' + biggestDigit);\n        }\n    }\n    case ItemFormatDec:\n        return (digit >= '0' && digit <= '9');\n    case ItemFormatSignedDec:\n        return (digit >= '0' && digit <= '9') || digit == '-';\n    case ItemFormatFloat:\n        return (digit >= '0' && digit <= '9') || digit == '-' || digit == '+' || digit == '.'\n                || digit == ',' || digit == '+' || digit == 'e' || digit == 'E' || digit == 'i'\n                || digit == 'n' || digit == 'f' || digit == 'I' || digit == 'N' || digit == 'F'\n                || digit == 'a' || digit == 'A';\n    }\n\n    return false;\n}\n\nvoid HexWidget::movePrevEditCharAny()\n{\n    if (!selection.isEmpty()) {\n        clearSelection();\n    }\n    editWordPos -= 1;\n    if (editWordPos < 0) {\n        finishEditingWord();\n        if (moveCursor(-itemByteLen, false, OverflowMove::Ignore)) {\n            startEditWord();\n            editWordPos = editWord.length() - 1;\n        }\n    }\n    updateViewport();\n}\n\nvoid HexWidget::typeOverwriteModeChar(QChar c)\n{\n    if (editWordState < EditWordState::WriteNotEdited || !isFixedWidth()) {\n        return;\n    }\n    editWord[editWordPos] = c;\n    editWordPos++;\n    editWordState = EditWordState::WriteEdited;\n    if (editWordPos >= editWord.length()) {\n        finishEditingWord();\n        bool moved = moveCursor(itemByteLen, false, OverflowMove::Ignore);\n        startEditWord();\n        if (!moved) {\n            editWordPos = editWord.length() - 1;\n        }\n    }\n}\n\nHexWidget::HexNavigationMode HexWidget::defaultNavigationMode()\n{\n    switch (editWordState) {\n    case EditWordState::Read:\n        return HexNavigationMode::Words;\n    case EditWordState::WriteNotStarted:\n        return isFixedWidth() ? HexNavigationMode::AnyChar : HexNavigationMode::Words;\n    case EditWordState::WriteNotEdited:\n    case EditWordState::WriteEdited:\n        return isFixedWidth() ? HexNavigationMode::AnyChar : HexNavigationMode::WordChar;\n    }\n    return HexNavigationMode::Words;\n}\n\nvoid HexWidget::refreshWordEditState()\n{\n    navigationMode = defaultNavigationMode();\n}\n\nbool HexWidget::handleAsciiWrite(QKeyEvent *event)\n{\n    if (!cursorOnAscii || !canKeyboardEdit()) {\n        return false;\n    }\n    if (event->key() == Qt::Key_Backspace || event->matches(QKeySequence::Backspace)) {\n        if (!selection.isEmpty()) {\n            writeZeros(selection.start(), selection.size());\n        } else {\n            moveCursor(-1, false);\n            writeZeros(cursor.address, 1);\n        }\n        return true;\n    }\n    if (event->key() == Qt::Key_Delete || event->matches(QKeySequence::Delete)) {\n        if (!selection.isEmpty()) {\n            writeZeros(selection.start(), selection.size());\n        } else {\n            writeZeros(cursor.address, 1);\n            moveCursor(1, false);\n        }\n        return true;\n    }\n    QString text;\n    if (event->matches(QKeySequence::Paste)) {\n        text = QApplication::clipboard()->text();\n        if (text.length() <= 0) {\n            return false;\n        }\n    } else {\n        text = event->text();\n        if (text.length() <= 0) {\n            return false;\n        }\n        QChar c = text[0];\n        if (c <= '\\x1f' || c == '\\x7f') {\n            return false;\n        }\n    }\n\n    auto bytes = text.toUtf8(); // TODO:#3028 use selected text encoding\n    auto address = getLocationAddress();\n    clearSelection();\n    data->write(reinterpret_cast<const uint8_t *>(bytes.data()), address, bytes.length());\n    seek(address + bytes.length());\n    updateViewport();\n    return true;\n}\n\nbool HexWidget::handleNumberWrite(QKeyEvent *event)\n{\n    if (editWordState < EditWordState::WriteNotStarted) {\n        return false;\n    }\n    bool overwrite = isFixedWidth();\n    auto keyText = event->text();\n    bool editingWord = editWordState >= EditWordState::WriteNotEdited;\n    if (keyText.length() > 0 && validCharForEdit(keyText[0])) {\n        if (!selection.isEmpty()) {\n            setCursorAddr(BasicCursor(selection.start()));\n        }\n        if (!editingWord) {\n            startEditWord();\n        }\n        if (overwrite) {\n            typeOverwriteModeChar(keyText[0]);\n        } else if (!editingWord /* && !overwrite */) {\n            editWord = keyText;\n            editWordPos = editWord.length();\n            editWordState = EditWordState::WriteEdited;\n        } else if (itemFormat == ItemFormatFloat || editWord.length() < itemCharLen) {\n            editWord.insert(editWordPos, keyText);\n            editWordPos += keyText.length();\n            editWordState = EditWordState::WriteEdited;\n        }\n        maybeFlushCharEdit();\n        return true;\n    }\n    if (event->matches(QKeySequence::Paste) && (editingWord || overwrite)) {\n        QString text = QApplication::clipboard()->text();\n        if (text.length() > 0) {\n            if (overwrite) {\n                startEditWord();\n                for (QChar c : text) {\n                    if (validCharForEdit(c)) {\n                        typeOverwriteModeChar(c);\n                    }\n                }\n            } else {\n                editWord.insert(editWordPos, text);\n                editWordPos += text.length();\n                editWordState = EditWordState::WriteEdited;\n            }\n        }\n        maybeFlushCharEdit();\n        return true;\n    }\n    if (editingWord) {\n        if (event->matches(QKeySequence::Cancel)) {\n            cancelEditedWord();\n            return true;\n        } else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {\n            bool needToAdvance =\n                    !(editWordPos == 0 && overwrite && editWordState < EditWordState::WriteEdited);\n            if (finishEditingWord(false) && needToAdvance) {\n                moveCursor(itemByteLen);\n            }\n            return true;\n        }\n    }\n    if (event->matches(QKeySequence::Delete)) {\n        if (!selection.isEmpty()) {\n            writeZeros(selection.start(), selection.size());\n            clearSelection();\n        } else {\n            startEditWord();\n            if (overwrite) {\n                typeOverwriteModeChar('0');\n            } else if (editWordPos < editWord.length()) {\n                editWord.remove(editWordPos, 1);\n                editWordState = EditWordState::WriteEdited;\n            }\n        }\n        maybeFlushCharEdit();\n        return true;\n    }\n    if (event->matches(QKeySequence::DeleteEndOfWord) && selection.isEmpty()) {\n        startEditWord();\n        if (overwrite) {\n            for (int i = editWordPos; i < editWord.length(); i++) {\n                typeOverwriteModeChar('0');\n            }\n        } else if (editWordPos < editWord.length()) {\n            editWord.remove(editWordPos, editWord.length());\n            editWordState = EditWordState::WriteEdited;\n        }\n        maybeFlushCharEdit();\n        return true;\n    }\n    if (event->matches(QKeySequence::DeleteStartOfWord) && selection.isEmpty()) {\n        if (!editingWord || (overwrite && editWordPos == 0)) {\n            if (!moveCursor(-itemByteLen, false, OverflowMove::Ignore)) {\n                return false;\n            }\n            startEditWord();\n            editWordPos = editWord.length();\n        }\n        if (overwrite) {\n            while (editWordPos > 0) {\n                editWordPos--;\n                editWord[editWordPos] = '0';\n            }\n            editWordState = EditWordState::WriteEdited;\n            maybeFlushCharEdit();\n            return true;\n        } else {\n            if (editWordPos > 0) {\n                editWord.remove(0, editWordPos);\n                editWordState = EditWordState::WriteEdited;\n                editWordPos = 0;\n                maybeFlushCharEdit();\n                return true;\n            }\n        }\n        return false;\n    }\n\n    if (event->key() == Qt::Key_Backspace) {\n        if (!selection.isEmpty()) {\n            writeZeros(selection.start(), selection.size());\n            clearSelection();\n            setCursorAddr(BasicCursor(selection.start()), false);\n            return true;\n        } else {\n            if (!editingWord || (overwrite && editWordPos == 0)) {\n                if (!moveCursor(-itemByteLen, false, OverflowMove::Ignore)) {\n                    return false;\n                }\n                startEditWord();\n                editWordPos = editWord.length();\n            }\n            if (editWordPos > 0) {\n                editWordPos -= 1;\n                if (overwrite) {\n                    editWord[editWordPos] = '0';\n                } else {\n                    editWord.remove(editWordPos, 1);\n                }\n                editWordState = EditWordState::WriteEdited;\n                maybeFlushCharEdit();\n                return true;\n            }\n        }\n        return false;\n    }\n\n    return false;\n}\n\nbool HexWidget::event(QEvent *event)\n{\n    // prefer treating keys like 's' 'g' '.' as typing input instead of global shortcuts\n    if (event->type() == QEvent::ShortcutOverride) {\n        auto keyEvent = static_cast<QKeyEvent *>(event);\n        auto modifiers = keyEvent->modifiers();\n        if ((modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier\n             || modifiers == Qt::KeypadModifier)\n            && keyEvent->key() < Qt::Key_Escape && canKeyboardEdit()) {\n            keyEvent->accept();\n            return true;\n        }\n    }\n\n    return QScrollArea::event(event);\n}\n\nvoid HexWidget::keyPressEvent(QKeyEvent *event)\n{\n    bool select = false;\n    auto moveOrSelect = [event, &select](QKeySequence::StandardKey moveSeq,\n                                         QKeySequence::StandardKey selectSeq) -> bool {\n        if (event->matches(moveSeq)) {\n            select = false;\n            return true;\n        } else if (event->matches(selectSeq)) {\n            select = true;\n            return true;\n        }\n        return false;\n    };\n\n    if (canKeyboardEdit()) {\n        if (handleAsciiWrite(event)) {\n            updateViewport();\n            return;\n        }\n        if (editWordState >= EditWordState::WriteNotStarted && !cursorOnAscii) {\n            if (handleNumberWrite(event)) {\n                updateViewport();\n                return;\n            }\n        }\n    }\n\n    if (cursorOnAscii || navigationMode == HexNavigationMode::Words\n        || navigationMode == HexNavigationMode::AnyChar) {\n        if (moveOrSelect(QKeySequence::MoveToNextPage, QKeySequence::SelectNextPage)) {\n            moveCursor(bytesPerScreen(), select);\n        } else if (moveOrSelect(QKeySequence::MoveToPreviousPage,\n                                QKeySequence::SelectPreviousPage)) {\n            moveCursor(-bytesPerScreen(), select);\n        } else if (moveOrSelect(QKeySequence::MoveToStartOfLine, QKeySequence::SelectStartOfLine)) {\n            int linePos =\n                    int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));\n            moveCursor(-linePos, select);\n        } else if (moveOrSelect(QKeySequence::MoveToEndOfLine, QKeySequence::SelectEndOfLine)) {\n            int linePos =\n                    int((cursor.address % itemRowByteLen()) - (startAddress % itemRowByteLen()));\n            moveCursor(itemRowByteLen() - linePos, select);\n        }\n    }\n\n    if (navigationMode == HexNavigationMode::Words || cursorOnAscii) {\n        if (moveOrSelect(QKeySequence::MoveToNextLine, QKeySequence::SelectNextLine)) {\n            moveCursor(itemRowByteLen(), select, OverflowMove::Ignore);\n        } else if (moveOrSelect(QKeySequence::MoveToPreviousLine,\n                                QKeySequence::SelectPreviousLine)) {\n            moveCursor(-itemRowByteLen(), select, OverflowMove::Ignore);\n        } else if (moveOrSelect(QKeySequence::MoveToNextChar, QKeySequence::SelectNextChar)\n                   || moveOrSelect(QKeySequence::MoveToNextWord, QKeySequence::SelectNextWord)) {\n            moveCursor(cursorOnAscii ? 1 : itemByteLen, select);\n        } else if (moveOrSelect(QKeySequence::MoveToPreviousChar, QKeySequence::SelectPreviousChar)\n                   || moveOrSelect(QKeySequence::MoveToPreviousWord,\n                                   QKeySequence::SelectPreviousWord)) {\n            moveCursor(cursorOnAscii ? -1 : -itemByteLen, select);\n        }\n    } else if (navigationMode == HexNavigationMode::AnyChar && !cursorOnAscii) {\n        if (moveOrSelect(QKeySequence::MoveToNextLine, QKeySequence::SelectNextLine)) {\n            moveCursorKeepEditOffset(itemRowByteLen(), select, OverflowMove::Ignore);\n        } else if (moveOrSelect(QKeySequence::MoveToPreviousLine,\n                                QKeySequence::SelectPreviousLine)) {\n            moveCursorKeepEditOffset(-itemRowByteLen(), select, OverflowMove::Ignore);\n        } else if (moveOrSelect(QKeySequence::MoveToNextChar, QKeySequence::SelectNextChar)) {\n            if (select) {\n                moveCursor(itemByteLen, select);\n            } else {\n                if (!selection.isEmpty()) {\n                    clearSelection();\n                }\n                if (editWordState == EditWordState::WriteNotStarted) {\n                    startEditWord();\n                }\n                editWordPos += 1;\n                if (editWordPos >= editWord.length()) {\n                    bool moved = moveCursor(itemByteLen, false, OverflowMove::Ignore);\n                    startEditWord();\n                    if (!moved) {\n                        editWordPos = editWord.length() - 1;\n                    }\n                }\n            }\n            updateViewport();\n        } else if (event->matches(QKeySequence::MoveToPreviousChar)) {\n            movePrevEditCharAny();\n        } else if (event->matches(QKeySequence::SelectPreviousChar)) {\n            moveCursor(-itemByteLen, true);\n        } else if (moveOrSelect(QKeySequence::MoveToNextWord, QKeySequence::SelectNextWord)) {\n            moveCursor(itemByteLen, select);\n        } else if (event->matches(QKeySequence::MoveToPreviousWord)) {\n            if (editWordPos > 0) {\n                editWordPos = 0;\n                updateViewport();\n            } else {\n                moveCursor(-itemByteLen, false);\n            }\n        } else if (event->matches(QKeySequence::SelectPreviousWord)) {\n            moveCursor(-itemByteLen, true);\n        }\n    } else if (navigationMode == HexNavigationMode::WordChar) {\n        if (event->matches(QKeySequence::MoveToNextChar)) {\n            editWordPos = std::min<int>(editWord.length(), editWordPos + 1);\n            updateViewport();\n        } else if (event->matches(QKeySequence::MoveToPreviousChar)) {\n            editWordPos = std::max(0, editWordPos - 1);\n            updateViewport();\n        } else if (event->matches(QKeySequence::MoveToStartOfLine)) {\n            editWordPos = 0;\n            updateViewport();\n        } else if (event->matches(QKeySequence::MoveToEndOfLine)) {\n            editWordPos = editWord.length();\n            updateViewport();\n        } else if (event->matches(QKeySequence::MoveToPreviousWord)) {\n            if (editWordPos > 0) {\n                editWordPos = 0;\n            } else {\n                moveCursor(-itemByteLen, select);\n                startEditWord();\n            }\n            updateViewport();\n        } else if (event->matches(QKeySequence::MoveToNextWord)) {\n            if (editWordPos < editWord.length()) {\n                editWordPos = editWord.length();\n            } else {\n                moveCursor(itemByteLen, select);\n                startEditWord();\n                editWordPos = editWord.length();\n            }\n            updateViewport();\n        }\n    }\n}\n\nvoid HexWidget::contextMenuEvent(QContextMenuEvent *event)\n{\n    QPoint pt = event->pos();\n    bool mouseOutsideSelection = false;\n    if (event->reason() == QContextMenuEvent::Mouse) {\n        auto mouseAddr = mousePosToAddr(pt).address;\n        if (asciiArea.contains(pt)) {\n            cursorOnAscii = true;\n        } else if (itemArea.contains(pt)) {\n            cursorOnAscii = false;\n        }\n        if (selection.isEmpty()) {\n            seek(mouseAddr);\n        } else {\n            mouseOutsideSelection = !selection.contains(mouseAddr);\n        }\n    }\n\n    auto disableOutsideSelectionActions = [this](bool disable) {\n        actionCopyAddress->setDisabled(disable);\n    };\n\n    QString comment = Core()->getCommentAt(cursor.address);\n\n    if (comment.isEmpty()) {\n        actionDeleteComment->setVisible(false);\n        actionComment->setText(tr(\"Add Comment\"));\n    } else {\n        actionDeleteComment->setVisible(true);\n        actionComment->setText(tr(\"Edit Comment\"));\n    }\n\n    QString flag = Core()->flagAt(cursor.address, false);\n    actionAddFlag->setData(flag);\n\n    if (flag.isEmpty()) {\n        actionAddFlag->setText(tr(\"Add flag at %1\").arg(RzAddressString(cursor.address)));\n    } else {\n        actionAddFlag->setText(tr(\"Rename flag \\\"%1\\\"\").arg(flag));\n    }\n\n    if (!ioModesController.canWrite()) {\n        actionKeyboardEdit->setChecked(false);\n    }\n\n    auto *menu = new QMenu(this);\n    QMenu *sizeMenu = menu->addMenu(tr(\"Item size:\"));\n    sizeMenu->addActions(actionsItemSize);\n    QMenu *formatMenu = menu->addMenu(tr(\"Item format:\"));\n    formatMenu->addActions(actionsItemFormat);\n    menu->addMenu(rowSizeMenu);\n    menu->addAction(actionHexPairs);\n    menu->addAction(actionItemBigEndian);\n    QMenu *writeMenu = menu->addMenu(tr(\"Edit\"));\n    writeMenu->addActions(actionsWriteString);\n    writeMenu->addSeparator();\n    writeMenu->addActions(actionsWriteOther);\n    menu->addAction(actionKeyboardEdit);\n\n    menu->addSeparator();\n    menu->addAction(actionCopy);\n    disableOutsideSelectionActions(mouseOutsideSelection);\n    menu->addAction(actionCopyAddress);\n    menu->addActions(this->actions());\n\n    /* Marks */\n    menu->addSeparator();\n    menu->addAction(actionAddMark);\n    auto marks = Core()->getMarksAt(cursor.address);\n    if (!marks.empty()) {\n        if (marks.size() == 1) {\n            // Add direct actions if only one mark contains this address\n            const auto &mark = marks.front();\n            QAction *editAction = menu->addAction(tr(\"Edit Mark\"));\n            QAction *removeAction = menu->addAction(tr(\"Remove Mark\"));\n            QString markName = mark.name;\n            connect(removeAction, &QAction::triggered, this,\n                    [this, markName]() { onActionDeleteMarkTriggered(markName); });\n            connect(editAction, &QAction::triggered, this,\n                    [this, markName]() { onActionEditMarkTriggered(markName); });\n        } else {\n            // Add submenus if multiple marks contain this address\n            QMenu *editMarkMenu = menu->addMenu(tr(\"Edit Mark\"));\n            QMenu *removeMarkMenu = menu->addMenu(tr(\"Remove Mark\"));\n            for (const auto &mark : marks) {\n                QAction *subActionRename = removeMarkMenu->addAction(mark.realname);\n                QAction *subActionEdit = editMarkMenu->addAction(mark.realname);\n                QString markName = mark.name;\n                connect(subActionRename, &QAction::triggered, this,\n                        [this, markName]() { onActionDeleteMarkTriggered(markName); });\n                connect(subActionEdit, &QAction::triggered, this,\n                        [this, markName]() { onActionEditMarkTriggered(markName); });\n            }\n        }\n    }\n\n    menu->exec(mapToGlobal(pt));\n    disableOutsideSelectionActions(false);\n    menu->deleteLater();\n}\n\nvoid HexWidget::onCursorBlinked()\n{\n    if (!cursorEnabled)\n        return;\n    cursor.blink();\n    QRect cursorRect = cursor.screenPos.toAlignedRect();\n    viewport()->update(cursorRect.translated(-horizontalScrollBar()->value(), 0));\n}\n\nvoid HexWidget::onHexPairsModeEnabled(bool enable)\n{\n    // Sync configuration\n    Core()->setConfig(\"hex.pairs\", enable);\n    if (enable) {\n        setItemGroupSize(2);\n    } else {\n        setItemGroupSize(1);\n    }\n}\n\nvoid HexWidget::copy()\n{\n    if (selection.isEmpty() || selection.size() > MAX_COPY_SIZE)\n        return;\n\n    auto x = cursorOnAscii\n            ? Core()->getString(selection.start(), selection.size(), RZ_STRING_ENC_8BIT, true)\n            : Core()->ioRead(selection.start(), (int)selection.size()).toHex();\n    QApplication::clipboard()->setText(x);\n}\n\nvoid HexWidget::copyAddress()\n{\n    uint64_t addr = getLocationAddress();\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(RzAddressString(addr));\n}\n\n// slot for add comment action\nvoid HexWidget::onActionAddCommentTriggered()\n{\n    uint64_t addr = cursor.address;\n    CommentsDialog::addOrEditComment(addr, this);\n    refresh();\n}\n\n// slot for deleting comment action\nvoid HexWidget::onActionDeleteCommentTriggered()\n{\n    uint64_t addr = cursor.address;\n    Core()->delComment(addr);\n    refresh();\n}\n\nvoid HexWidget::onActionAddFlagTriggered()\n{\n    QString flagNameHint = actionAddFlag->data().toString();\n    if (FlagDialog(cursor.address, this, flagNameHint).exec()) {\n        refresh();\n    }\n}\n\nvoid HexWidget::onRangeDialogAccepted()\n{\n    if (rangeDialog.empty()) {\n        seek(rangeDialog.getStartAddress());\n        return;\n    }\n    selectRange(rangeDialog.getStartAddress(), rangeDialog.getEndAddress());\n}\n\nvoid HexWidget::onActionAddMarkTriggered()\n{\n    RVA from = cursor.address;\n    RVA to = cursor.address;\n    if (!selection.isEmpty()) {\n        from = selection.start();\n        to = selection.end();\n    }\n    if (MarkDialog(from, to, this).exec()) {\n        refresh();\n    }\n}\n\nvoid HexWidget::onActionDeleteMarkTriggered(const QString &name)\n{\n    Core()->delMark(name);\n    refresh();\n}\n\nvoid HexWidget::onActionEditMarkTriggered(const QString &name)\n{\n    RVA addr = cursor.address;\n    if (MarkDialog(addr, addr, this, name).exec()) {\n        refresh();\n    }\n}\n\nvoid HexWidget::writeZeros(uint64_t address, uint64_t length)\n{\n    const uint64_t MAX_BUFFER = 1024;\n    std::vector<uint8_t> zeroes(std::min(MAX_BUFFER, length), 0);\n    while (length > zeroes.size()) {\n        data->write(zeroes.data(), address, zeroes.size());\n        address += zeroes.size();\n        length -= zeroes.size();\n    }\n    if (length > 0) {\n        data->write(zeroes.data(), address, length);\n    }\n}\n\nvoid HexWidget::w_writeString()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    bool ok = false;\n    QString str = QInputDialog::getText(this, tr(\"Write string\"), tr(\"String:\"), QLineEdit::Normal,\n                                        \"\", &ok);\n    if (!ok || str.isEmpty()) {\n        return;\n    }\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_string_at(core, getLocationAddress(), str.toUtf8().constData());\n    }\n    refresh();\n}\n\nvoid HexWidget::w_increaseDecrease()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    IncrementDecrementDialog d;\n    int ret = d.exec();\n    if (ret == QDialog::Rejected) {\n        return;\n    }\n    int64_t value = (int64_t)d.getValue();\n    uint8_t sz = d.getNBytes();\n    if (d.getMode() == IncrementDecrementDialog::Decrease) {\n        value *= -1;\n    }\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_value_inc_at(core, getLocationAddress(), value, sz);\n    }\n    refresh();\n}\n\nvoid HexWidget::w_writeBytes()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    bool ok = false;\n\n    int size = INT_MAX;\n    if (!selection.isEmpty() && selection.size() <= INT_MAX) {\n        size = static_cast<int>(selection.size());\n    }\n\n    QByteArray bytes = QInputDialog::getText(this, tr(\"Write hex bytes\"), tr(\"Hex byte string:\"),\n                                             QLineEdit::Normal, \"\", &ok)\n                               .toUtf8();\n    const int offset = bytes.startsWith(\"\\\\x\") ? 2 : 0;\n    const int incr = offset + 2;\n    const int bytes_size = qMin(bytes.size() / incr, size);\n    if (!ok || !bytes_size) {\n        return;\n    }\n    {\n        auto *buf = (uint8_t *)malloc(static_cast<size_t>(bytes_size));\n        if (!buf) {\n            return;\n        }\n        for (int i = 0, j = 0, sz = bytes.size(); i < sz; i += incr, j++) {\n            buf[j] = static_cast<uint8_t>(bytes.mid(i + offset, 2).toInt(nullptr, 16));\n        }\n        RzCoreLocked core(Core());\n        rz_core_write_at(core, getLocationAddress(), buf, bytes_size);\n        free(buf);\n    }\n    refresh();\n}\n\nvoid HexWidget::w_writeZeros()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n\n    int size = 1;\n    if (!selection.isEmpty() && selection.size() <= INT_MAX) {\n        size = static_cast<int>(selection.size());\n    }\n\n    bool ok = false;\n    int len = QInputDialog::getInt(this, tr(\"Write zeros\"), tr(\"Number of zeros:\"), size, 1,\n                                   0x7FFFFFFF, 1, &ok);\n    if (!ok) {\n        return;\n    }\n    {\n        writeZeros(getLocationAddress(), len);\n    }\n    refresh();\n}\n\nvoid HexWidget::w_write64()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    Base64EnDecodedWriteDialog d;\n    int ret = d.exec();\n    if (ret == QDialog::Rejected) {\n        return;\n    }\n    QByteArray str = d.getData();\n\n    if (d.getMode() == Base64EnDecodedWriteDialog::Decode\n        && (QString(str).contains(QRegularExpression(\"[^a-zA-Z0-9+/=]\")) || str.length() % 4 != 0\n            || str.isEmpty())) {\n        QMessageBox::critical(\n                this, tr(\"Error\"),\n                tr(\"Error occured during decoding your input.\\n\"\n                   \"Please, make sure, that it is a valid base64 string and try again.\"));\n        return;\n    }\n\n    {\n        RzCoreLocked core(Core());\n        if (d.getMode() == Base64EnDecodedWriteDialog::Encode) {\n            rz_core_write_base64_at(core, getLocationAddress(), str.toHex().constData());\n        } else {\n            rz_core_write_base64d_at(core, getLocationAddress(), str.constData());\n        }\n    }\n    refresh();\n}\n\nvoid HexWidget::w_writeRandom()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n\n    int size = 1;\n    if (!selection.isEmpty() && selection.size() <= INT_MAX) {\n        size = static_cast<int>(selection.size());\n    }\n\n    bool ok = false;\n    int nbytes = QInputDialog::getInt(this, tr(\"Write random bytes\"), tr(\"Number of bytes:\"), size,\n                                      1, 0x7FFFFFFF, 1, &ok);\n    if (!ok) {\n        return;\n    }\n\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_random_at(core, getLocationAddress(), nbytes);\n    }\n    refresh();\n}\n\nvoid HexWidget::w_duplFromOffset()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    DuplicateFromOffsetDialog d;\n    int ret = d.exec();\n    if (ret == QDialog::Rejected) {\n        return;\n    }\n    RVA src = d.getOffset();\n    int len = (int)d.getNBytes();\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_duplicate_at(core, getLocationAddress(), src, len);\n    }\n    refresh();\n}\n\nvoid HexWidget::w_writePascalString()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    bool ok = false;\n    QString str = QInputDialog::getText(this, tr(\"Write Pascal string\"), tr(\"String:\"),\n                                        QLineEdit::Normal, \"\", &ok);\n    if (!ok || str.isEmpty()) {\n        return;\n    }\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_length_string_at(core, getLocationAddress(), str.toUtf8().constData());\n    }\n    refresh();\n}\n\nvoid HexWidget::w_writeWideString()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    bool ok = false;\n    QString str = QInputDialog::getText(this, tr(\"Write wide string\"), tr(\"String:\"),\n                                        QLineEdit::Normal, \"\", &ok);\n    if (!ok || str.isEmpty()) {\n        return;\n    }\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_string_wide_at(core, getLocationAddress(), str.toUtf8().constData());\n    }\n    refresh();\n}\n\nvoid HexWidget::w_writeCString()\n{\n    if (!ioModesController.prepareForWriting()) {\n        return;\n    }\n    bool ok = false;\n    QString str = QInputDialog::getText(this, tr(\"Write zero-terminated string\"), tr(\"String:\"),\n                                        QLineEdit::Normal, \"\", &ok);\n    if (!ok || str.isEmpty()) {\n        return;\n    }\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_string_zero_at(core, getLocationAddress(), str.toUtf8().constData());\n    }\n    refresh();\n}\n\nvoid HexWidget::onKeyboardEditTriggered(bool enabled)\n{\n    if (!enabled) {\n        return;\n    }\n    if (!ioModesController.prepareForWriting()) {\n        actionKeyboardEdit->setChecked(false);\n    }\n}\n\nvoid HexWidget::onKeyboardEditChanged(bool enabled)\n{\n    if (!enabled) {\n        finishEditingWord();\n        navigationMode = HexNavigationMode::Words;\n        editWordState = EditWordState::Read;\n    } else {\n        editWordState = EditWordState::WriteNotStarted;\n        navigationMode = defaultNavigationMode();\n    }\n    updateCursorMeta();\n    updateViewport();\n}\n\nvoid HexWidget::updateItemLength()\n{\n    itemPrefixLen = 0;\n    itemPrefix.clear();\n\n    switch (itemFormat) {\n    case ItemFormatHex:\n        itemCharLen = 2 * itemByteLen;\n        if (itemByteLen > 1 && showExHex) {\n            itemPrefixLen = hexPrefix.length();\n            itemPrefix = hexPrefix;\n        }\n        break;\n    case ItemFormatOct:\n        itemCharLen = (itemByteLen * 8 + 3) / 3;\n        break;\n    case ItemFormatDec:\n        switch (itemByteLen) {\n        case 1:\n            itemCharLen = 3;\n            break;\n        case 2:\n            itemCharLen = 5;\n            break;\n        case 4:\n            itemCharLen = 10;\n            break;\n        case 8:\n            itemCharLen = 20;\n            break;\n        }\n        break;\n    case ItemFormatSignedDec:\n        switch (itemByteLen) {\n        case 1:\n            itemCharLen = 4;\n            break;\n        case 2:\n            itemCharLen = 6;\n            break;\n        case 4:\n            itemCharLen = 11;\n            break;\n        case 8:\n            itemCharLen = 20;\n            break;\n        }\n        break;\n    case ItemFormatFloat:\n        if (itemByteLen < 4)\n            itemByteLen = 4;\n        // FIXME\n        itemCharLen = 3 * itemByteLen;\n        break;\n    }\n\n    itemCharLen += itemPrefixLen;\n\n    updateCounts();\n}\n\nvoid HexWidget::drawHeader(QPainter &painter)\n{\n    if (!showHeader)\n        return;\n\n    int offset = 0;\n    QRectF rect(itemArea.left(), 0, itemWidth(), lineHeight);\n\n    painter.setPen(addrColor);\n\n    for (int j = 0; j < itemColumns; ++j) {\n        for (int k = 0; k < itemGroupSize; ++k, offset += itemByteLen) {\n            painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight,\n                             QString::number(offset, 16).toUpper());\n            rect.translate(itemWidth(), 0);\n        }\n        rect.translate(columnSpacingWidth(), 0);\n    }\n\n    rect.moveLeft(asciiArea.left());\n    rect.setWidth(charWidth);\n    for (int j = 0; j < itemRowByteLen(); ++j) {\n        painter.drawText(rect, Qt::AlignVCenter | Qt::AlignRight,\n                         QString::number(j % 16, 16).toUpper());\n        rect.translate(charWidth, 0);\n    }\n}\n\nvoid HexWidget::drawCursor(QPainter &painter, bool shadow)\n{\n    if (shadow) {\n        QPen pen(Qt::gray);\n        pen.setStyle(Qt::DashLine);\n        painter.setPen(pen);\n        qreal shadowWidth = charWidth;\n        if (cursorOnAscii) {\n            shadowWidth = itemWidth();\n        } else if (editWordState >= EditWordState::WriteNotEdited) {\n            shadowWidth = itemByteLen * charWidth;\n        }\n        shadowCursor.screenPos.setWidth(shadowWidth);\n        painter.drawRect(shadowCursor.screenPos);\n        painter.setPen(Qt::SolidLine);\n    }\n\n    painter.setPen(cursor.cachedColor);\n    QRectF charRect(cursor.screenPos);\n    charRect.setWidth(charWidth);\n    painter.fillRect(charRect, backgroundColor);\n    QColor markColor = Core()->getBlendedMarksColorAt(cursor.address);\n    if (markColor.isValid()) {\n        painter.fillRect(charRect, markColor);\n    }\n    painter.drawText(charRect, Qt::AlignVCenter, cursor.cachedChar);\n    if (cursor.isVisible) {\n        painter.setCompositionMode(QPainter::RasterOp_SourceXorDestination);\n        painter.fillRect(cursor.screenPos, QColor(0xff, 0xff, 0xff));\n    }\n}\n\nvoid HexWidget::drawAddrArea(QPainter &painter)\n{\n    uint64_t offset = startAddress;\n    QString addrString;\n    QSizeF areaSize((addrCharLen + (showExAddr ? 2 : 0)) * charWidth, lineHeight);\n    QRectF strRect(addrArea.topLeft(), areaSize);\n\n    painter.setPen(addrColor);\n    for (int line = 0; line < visibleLines && offset <= data->maxIndex();\n         ++line, strRect.translate(0, lineHeight), offset += itemRowByteLen()) {\n        addrString = QString(\"%1\").arg(offset, addrCharLen, 16, QLatin1Char('0'));\n        if (showExAddr)\n            addrString.prepend(hexPrefix);\n        painter.drawText(strRect, Qt::AlignVCenter, addrString);\n    }\n\n    painter.setPen(borderColor);\n\n    qreal vLineOffset = itemArea.left() - charWidth;\n    painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));\n}\n\nvoid HexWidget::fillMarks(QPainter &painter, bool ascii)\n{\n    uint64_t endAddress = startAddress + visibleLines * itemColumns * itemGroupSize * itemByteLen;\n    endAddress = std::min(endAddress, data->maxIndex());\n    const auto marks = Core()->getMarks();\n    for (const auto &mark : marks) {\n        ut64 from = mark.from;\n        ut64 to = mark.to;\n\n        // Skip marks not visible in current viewport\n        if (to < startAddress || from > endAddress) {\n            continue;\n        }\n\n        const auto shapes = rangePolygons(from, to, ascii);\n\n        QColor color(mark.color);\n        if (!color.isValid()) {\n            continue;\n        }\n        color.setAlphaF(MARK_ALPHA_F);\n        painter.setBrush(color);\n        painter.setPen(Qt::NoPen);\n\n        for (const auto &shape : shapes) {\n            painter.drawPolygon(shape);\n        }\n    }\n}\n\nvoid HexWidget::drawItemArea(QPainter &painter)\n{\n    QRectF itemRect(itemArea.topLeft(), QSizeF(itemWidth(), lineHeight));\n    QColor itemColor;\n    QString itemString;\n\n    fillSelectionBackground(painter);\n    fillMarks(painter, false);\n\n    bool haveEditWord = false;\n    QRectF editWordRect;\n    QColor editWordColor;\n\n    uint64_t itemAddr = startAddress;\n    for (int line = 0; line < visibleLines; ++line) {\n        itemRect.moveLeft(itemArea.left());\n        for (int j = 0; j < itemColumns; ++j) {\n            for (int k = 0; k < itemGroupSize && itemAddr <= data->maxIndex();\n                 ++k, itemAddr += itemByteLen) {\n\n                itemString = renderItem(itemAddr - startAddress, &itemColor);\n\n                if (!getFlagsAndComment(itemAddr).isEmpty()) {\n                    QColor markerColor(borderColor);\n                    markerColor.setAlphaF(0.5);\n                    painter.setPen(markerColor);\n                    for (const auto &shape : rangePolygons(itemAddr, itemAddr, false)) {\n                        painter.drawPolyline(shape);\n                    }\n                }\n                if (selection.contains(itemAddr) && !cursorOnAscii) {\n                    itemColor = palette().highlightedText().color();\n                }\n                if (isItemDifferentAt(itemAddr)) {\n                    itemColor.setRgb(diffColor.rgb());\n                }\n\n                if (editWordState <= EditWordState::WriteNotStarted || cursor.address != itemAddr) {\n                    painter.setPen(itemColor);\n                    painter.drawText(itemRect, Qt::AlignVCenter, itemString);\n                    itemRect.translate(itemWidth(), 0);\n                    if (cursor.address == itemAddr) {\n                        auto &itemCursor = cursorOnAscii ? shadowCursor : cursor;\n                        int itemCharPos = 0;\n                        if (editWordState > EditWordState::Read) {\n                            itemCharPos += itemPrefixLen;\n                        }\n                        if (itemCharPos < itemString.length()) {\n                            itemCursor.cachedChar = itemString.at(itemCharPos);\n                        } else {\n                            itemCursor.cachedChar = ' ';\n                        }\n                        itemCursor.cachedColor = itemColor;\n                    }\n                } else {\n                    haveEditWord = true;\n                    editWordRect = itemRect;\n                    editWordColor = itemColor;\n\n                    auto &itemCursor = cursor;\n                    itemCursor.cachedChar =\n                            editWordPos < editWord.length() ? editWord[editWordPos] : QChar(' ');\n                    itemCursor.cachedColor = itemColor;\n                    itemCursor.screenPos.moveTopLeft(itemRect.topLeft());\n                    itemCursor.screenPos.translate(charWidth * (editWordPos + itemPrefixLen), 0);\n\n                    itemRect.translate(itemWidth(), 0);\n                }\n            }\n            itemRect.translate(columnSpacingWidth(), 0);\n        }\n        itemRect.translate(0, lineHeight);\n    }\n    if (haveEditWord) {\n        auto length = std::max<int>(itemCharLen, editWord.length());\n        auto rect = editWordRect;\n        rect.setWidth(length * charWidth);\n        painter.fillRect(rect, backgroundColor);\n\n        painter.setPen(editWordColor);\n        editWordRect.setWidth(4000);\n        painter.drawText(editWordRect, Qt::AlignVCenter | Qt::AlignLeft, itemPrefix + editWord);\n    }\n\n    painter.setPen(borderColor);\n\n    qreal vLineOffset = asciiArea.left() - charWidth;\n    painter.drawLine(QLineF(vLineOffset, 0, vLineOffset, viewport()->height()));\n}\n\nvoid HexWidget::drawAsciiArea(QPainter &painter)\n{\n    QRectF charRect(asciiArea.topLeft(), QSizeF(charWidth, lineHeight));\n\n    fillSelectionBackground(painter, true);\n    fillMarks(painter, true);\n    painter.setBrush(Qt::NoBrush);\n\n    uint64_t address = startAddress;\n    QChar ascii;\n    QColor color;\n    for (int line = 0; line < visibleLines; ++line, charRect.translate(0, lineHeight)) {\n        charRect.moveLeft(asciiArea.left());\n        for (int j = 0; j < itemRowByteLen() && address <= data->maxIndex(); ++j, ++address) {\n            ascii = renderAscii(address - startAddress, &color);\n            if (selection.contains(address) && cursorOnAscii) {\n                color = palette().highlightedText().color();\n            }\n            if (isItemDifferentAt(address)) {\n                color.setRgb(diffColor.rgb());\n            }\n            painter.setPen(color);\n            /* Dots look ugly. Use fillRect() instead of drawText(). */\n            if (ascii == '.') {\n                qreal a = cursor.screenPos.width();\n                QPointF p = charRect.bottomLeft();\n                p.rx() += (charWidth - a) / 2 + 1;\n                p.ry() += -2 * a;\n                painter.fillRect(QRectF(p, QSizeF(a, a)), color);\n            } else {\n                painter.drawText(charRect, Qt::AlignVCenter, ascii);\n            }\n            charRect.translate(charWidth, 0);\n            if (cursor.address == address) {\n                auto &itemCursor = cursorOnAscii ? cursor : shadowCursor;\n                itemCursor.cachedChar = ascii;\n                itemCursor.cachedColor = color;\n            }\n        }\n    }\n}\n\nvoid HexWidget::fillSelectionBackground(QPainter &painter, bool ascii)\n{\n    if (selection.isEmpty()) {\n        return;\n    }\n    const auto parts = rangePolygons(selection.start(), selection.end(), ascii);\n    for (const auto &shape : parts) {\n        QColor highlightColor = palette().color(QPalette::Highlight);\n        if (ascii == cursorOnAscii) {\n            painter.setBrush(highlightColor);\n            painter.drawPolygon(shape);\n        } else {\n            painter.setPen(highlightColor);\n            painter.drawPolyline(shape);\n        }\n    }\n}\n\nQVector<QPolygonF> HexWidget::rangePolygons(RVA start, RVA last, bool ascii)\n{\n    if (last < startAddress || start > lastVisibleAddr()) {\n        return {};\n    }\n\n    QRectF rect;\n    const QRectF area = QRectF(ascii ? asciiArea : itemArea);\n\n    /* Convert absolute values to relative */\n    int startOffset = std::max(uint64_t(start), startAddress) - startAddress;\n    int endOffset = std::min(uint64_t(last), lastVisibleAddr()) - startAddress;\n\n    QVector<QPolygonF> parts;\n\n    auto getRectangle = [&](int offset) {\n        return QRectF(ascii ? asciiRectangle(offset) : itemRectangle(offset));\n    };\n\n    auto startRect = getRectangle(startOffset);\n    auto endRect = getRectangle(endOffset);\n    bool startJagged = false;\n    bool endJagged = false;\n    if (!ascii) {\n        if (int startFraction = startOffset % itemByteLen) {\n            startRect.setLeft(startRect.left() + startFraction * startRect.width() / itemByteLen);\n            startJagged = true;\n        }\n        if (int endFraction = itemByteLen - 1 - (endOffset % itemByteLen)) {\n            endRect.setRight(endRect.right() - endFraction * endRect.width() / itemByteLen);\n            endJagged = true;\n        }\n    }\n    if (endOffset - startOffset + 1 <= rowSizeBytes) {\n        if (startOffset / rowSizeBytes == endOffset / rowSizeBytes) { // single row\n            rect = startRect;\n            rect.setRight(endRect.right());\n            parts.push_back(QPolygonF(rect));\n        } else {\n            // two separate rectangles\n            rect = startRect;\n            rect.setRight(area.right());\n            parts.push_back(QPolygonF(rect));\n            rect = endRect;\n            rect.setLeft(area.left());\n            parts.push_back(QPolygonF(rect));\n        }\n    } else {\n        // single multiline shape\n        QPolygonF shape;\n        shape << startRect.topLeft();\n        rect = getRectangle(startOffset + rowSizeBytes - 1 - startOffset % rowSizeBytes);\n        shape << rect.topRight();\n        if (endOffset % rowSizeBytes != rowSizeBytes - 1) {\n            rect = getRectangle(endOffset - endOffset % rowSizeBytes - 1);\n            shape << rect.bottomRight() << endRect.topRight();\n        }\n        shape << endRect.bottomRight();\n        shape << getRectangle(endOffset - endOffset % rowSizeBytes).bottomLeft();\n        if (startOffset % rowSizeBytes) {\n            rect = getRectangle(startOffset - startOffset % rowSizeBytes + rowSizeBytes);\n            shape << rect.topLeft() << startRect.bottomLeft();\n        }\n        shape << shape.first(); // close the shape\n        parts.push_back(shape);\n    }\n    if (!ascii && (startJagged || endJagged) && parts.length() >= 1) {\n\n        QPolygonF top;\n        top.reserve(3);\n        top << QPointF(0, 0) << QPointF(charWidth, lineHeight / 3) << QPointF(0, lineHeight / 2);\n        QPolygonF bottom;\n        bottom.reserve(3);\n        bottom << QPointF(0, lineHeight / 2) << QPointF(-charWidth, 2 * lineHeight / 3)\n               << QPointF(0, lineHeight);\n\n        // small adjustment to make sure that edges don't overlap with rect edges, QPolygonF doesn't\n        // handle it properly\n        QPointF adjustment(charWidth / 16, 0);\n        top.translate(-adjustment);\n        bottom.translate(adjustment);\n\n        if (startJagged) {\n            auto movedTop = top.translated(startRect.topLeft());\n            auto movedBottom = bottom.translated(startRect.topLeft());\n            parts[0] = parts[0].subtracted(movedTop).united(movedBottom);\n        }\n        if (endJagged) {\n            auto movedTop = top.translated(endRect.topRight());\n            auto movedBottom = bottom.translated(endRect.topRight());\n            parts.last() = parts.last().subtracted(movedBottom).united(movedTop);\n        }\n    }\n    return parts;\n}\n\nvoid HexWidget::updateMetrics()\n{\n    QFontMetricsF fontMetrics(this->monospaceFont);\n    lineHeight = fontMetrics.height();\n#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)\n    charWidth = fontMetrics.width('A');\n#else\n    charWidth = fontMetrics.horizontalAdvance('A');\n#endif\n\n    updateCounts();\n    updateAreasHeight();\n\n    qreal cursorWidth = std::max(charWidth / 3, 1.);\n    cursor.screenPos.setHeight(lineHeight);\n    shadowCursor.screenPos.setHeight(lineHeight);\n\n    cursor.screenPos.setWidth(cursorWidth);\n    if (cursorOnAscii) {\n        cursor.screenPos.moveTopLeft(asciiArea.topLeft());\n\n        shadowCursor.screenPos.setWidth(itemWidth());\n        shadowCursor.screenPos.moveTopLeft(itemArea.topLeft());\n    } else {\n        cursor.screenPos.moveTopLeft(itemArea.topLeft());\n        shadowCursor.screenPos.setWidth(charWidth);\n        shadowCursor.screenPos.moveTopLeft(asciiArea.topLeft());\n    }\n}\n\nvoid HexWidget::updateAreasPosition()\n{\n    const qreal spacingWidth = areaSpacingWidth();\n\n    qreal yOffset = showHeader ? lineHeight : 0;\n\n    addrArea.setTopLeft(QPointF(0, yOffset));\n    addrArea.setWidth((addrCharLen + (showExAddr ? 2 : 0)) * charWidth);\n\n    itemArea.setTopLeft(QPointF(addrArea.right() + spacingWidth, yOffset));\n    itemArea.setWidth(itemRowWidth());\n\n    asciiArea.setTopLeft(QPointF(itemArea.right() + spacingWidth, yOffset));\n    asciiArea.setWidth(asciiRowWidth());\n\n    updateWidth();\n}\n\nvoid HexWidget::updateAreasHeight()\n{\n    visibleLines = static_cast<int>((viewport()->height() - itemArea.top()) / lineHeight);\n\n    qreal height = visibleLines * lineHeight;\n    addrArea.setHeight(height);\n    itemArea.setHeight(height);\n    asciiArea.setHeight(height);\n}\n\nbool HexWidget::moveCursor(int offset, bool select, OverflowMove overflowMove)\n{\n    BasicCursor addr(cursor.address);\n    if (overflowMove == OverflowMove::Ignore) {\n        if (addr.moveChecked(offset)) {\n            if (addr.address > data->maxIndex()) {\n                addr.address = data->maxIndex();\n                addr.pastEnd = true;\n            }\n            setCursorAddr(addr, select);\n            return true;\n        }\n        return false;\n    } else {\n        addr += offset;\n        if (addr.address > data->maxIndex()) {\n            addr.address = data->maxIndex();\n        }\n        setCursorAddr(addr, select);\n        return true;\n    }\n}\n\nvoid HexWidget::moveCursorKeepEditOffset(int byteOffset, bool select, OverflowMove overflowMove)\n{\n    int wordOffset = editWordPos;\n    moveCursor(byteOffset, select, overflowMove);\n    // preserve position within word when moving vertically in hex or oct modes\n    if (!cursorOnAscii && !select && wordOffset > 0 && navigationMode == HexNavigationMode::AnyChar\n        && editWordState > EditWordState::Read) {\n        startEditWord();\n        editWordPos = wordOffset;\n    }\n}\n\nvoid HexWidget::setCursorAddr(BasicCursor addr, bool select)\n{\n    finishEditingWord();\n    if (!select) {\n        bool clearingSelection = !selection.isEmpty();\n        selection.init(addr);\n        if (clearingSelection)\n            emit selectionChanged(getSelection());\n    }\n    emit positionChanged(addr.address);\n\n    cursor.address = addr.address;\n    if (!cursorOnAscii) {\n        cursor.address -= cursor.address % itemByteLen;\n    }\n\n    /* Pause cursor repainting */\n    cursorEnabled = false;\n\n    if (select) {\n        selection.update(addr);\n        emit selectionChanged(getSelection());\n    }\n\n    uint64_t addressValue = cursor.address;\n    /* Update data cache if necessary */\n    if (!(addressValue >= startAddress && addressValue <= lastVisibleAddr())) {\n        /* Align start address */\n        addressValue -= (addressValue % itemRowByteLen());\n\n        /* FIXME: handling Page Up/Down */\n        uint64_t rowAfterVisibleAddress = startAddress + bytesPerScreen();\n        if (addressValue == rowAfterVisibleAddress && addressValue > startAddress) {\n            // when pressing down add only one new row\n            startAddress += itemRowByteLen();\n        } else {\n            startAddress = addressValue;\n        }\n\n        fetchData();\n\n        if (startAddress > (data->maxIndex() - bytesPerScreen()) + 1) {\n            startAddress = (data->maxIndex() - bytesPerScreen()) + 1;\n        }\n    }\n\n    updateCursorMeta();\n\n    /* Draw cursor */\n    cursor.isVisible = !select;\n    updateViewport();\n\n    /* Resume cursor repainting */\n    cursorEnabled = selection.isEmpty();\n}\n\nvoid HexWidget::updateCursorMeta()\n{\n    QPointF point;\n    QPointF pointAscii;\n\n    int offset = cursor.address - startAddress;\n    int itemOffset = offset;\n    int asciiOffset;\n\n    /* Calc common Y coordinate */\n    point.ry() = (itemOffset / itemRowByteLen()) * lineHeight;\n    pointAscii.setY(point.y());\n    itemOffset %= itemRowByteLen();\n    asciiOffset = itemOffset;\n\n    /* Calc X coordinate on the item area */\n    point.rx() = (itemOffset / itemGroupByteLen()) * columnExWidth();\n    itemOffset %= itemGroupByteLen();\n    point.rx() += (itemOffset / itemByteLen) * itemWidth();\n\n    /* Calc X coordinate on the ascii area */\n    pointAscii.rx() = asciiOffset * charWidth;\n\n    point += itemArea.topLeft();\n    pointAscii += asciiArea.topLeft();\n\n    if (editWordState > EditWordState::Read && !cursorOnAscii) {\n        point.rx() += itemPrefixLen * charWidth;\n    }\n\n    cursor.screenPos.moveTopLeft(cursorOnAscii ? pointAscii : point);\n    shadowCursor.screenPos.moveTopLeft(cursorOnAscii ? point : pointAscii);\n}\n\nvoid HexWidget::setCursorOnAscii(bool ascii)\n{\n    cursorOnAscii = ascii;\n}\n\nQColor HexWidget::itemColor(uint8_t byte)\n{\n    QColor color(defColor);\n\n    if (byte == 0x00)\n        color = b0x00Color;\n    else if (byte == 0x7f)\n        color = b0x7fColor;\n    else if (byte == 0xff)\n        color = b0xffColor;\n    else if (IS_PRINTABLE(byte)) {\n        color = printableColor;\n    }\n\n    return color;\n}\n\ntemplate<class T>\nstatic T fromBigEndian(const void *src)\n{\n#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)\n    return qFromBigEndian<T>(src);\n#else\n    T result;\n    memcpy(&result, src, sizeof(T));\n    return qFromBigEndian<T>(result);\n#endif\n}\n\ntemplate<class T>\nstatic T fromLittleEndian(const void *src)\n{\n#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)\n    return qFromLittleEndian<T>(src);\n#else\n    T result;\n    memcpy(&result, src, sizeof(T));\n    return qFromLittleEndian<T>(result);\n#endif\n}\n\nQVariant HexWidget::readItem(int offset, QColor *color)\n{\n    quint8 byte;\n    quint16 word;\n    quint32 dword;\n    quint64 qword;\n    float float32;\n    double float64;\n\n    quint8 bytes[sizeof(uint64_t)];\n    data->copy(bytes, startAddress + offset, static_cast<size_t>(itemByteLen));\n    const bool signedItem = itemFormat == ItemFormatSignedDec;\n\n    if (color) {\n        *color = defColor;\n    }\n\n    switch (itemByteLen) {\n    case 1:\n        byte = bytes[0];\n        if (color)\n            *color = itemColor(byte);\n        if (!signedItem)\n            return QVariant(static_cast<quint64>(byte));\n        return QVariant(static_cast<qint64>(static_cast<qint8>(byte)));\n    case 2:\n        if (itemBigEndian)\n            word = fromBigEndian<quint16>(bytes);\n        else\n            word = fromLittleEndian<quint16>(bytes);\n\n        if (!signedItem)\n            return QVariant(static_cast<quint64>(word));\n        return QVariant(static_cast<qint64>(static_cast<qint16>(word)));\n    case 4:\n        if (itemBigEndian)\n            dword = fromBigEndian<quint32>(bytes);\n        else\n            dword = fromLittleEndian<quint32>(bytes);\n\n        if (itemFormat == ItemFormatFloat) {\n            memcpy(&float32, &dword, sizeof(float32));\n            return QVariant(float32);\n        }\n        if (!signedItem)\n            return QVariant(static_cast<quint64>(dword));\n        return QVariant(static_cast<qint64>(static_cast<qint32>(dword)));\n    case 8:\n        if (itemBigEndian)\n            qword = fromBigEndian<quint64>(bytes);\n        else\n            qword = fromLittleEndian<quint64>(bytes);\n        if (itemFormat == ItemFormatFloat) {\n            memcpy(&float64, &qword, sizeof(float64));\n            return QVariant(float64);\n        }\n        if (!signedItem)\n            return QVariant(qword);\n        return QVariant(static_cast<qint64>(qword));\n    }\n\n    return QVariant();\n}\n\nQString HexWidget::renderItem(int offset, QColor *color)\n{\n    QString item;\n    QVariant itemVal = readItem(offset, color);\n    int itemLen = itemCharLen - itemPrefixLen; /* Reserve space for prefix */\n\n    // FIXME: handle broken itemVal ( QVariant() )\n    switch (itemFormat) {\n    case ItemFormatHex:\n        item = QString(\"%1\").arg(itemVal.toULongLong(), itemLen, 16, QLatin1Char('0'));\n        if (itemByteLen > 1 && showExHex)\n            item.prepend(hexPrefix);\n        break;\n    case ItemFormatOct:\n        item = QString(\"%1\").arg(itemVal.toULongLong(), itemLen, 8, QLatin1Char('0'));\n        break;\n    case ItemFormatDec:\n        item = QString(\"%1\").arg(itemVal.toULongLong(), itemLen, 10);\n        break;\n    case ItemFormatSignedDec:\n        item = QString(\"%1\").arg(itemVal.toLongLong(), itemLen, 10);\n        break;\n    case ItemFormatFloat:\n        item = QString(\"%1\").arg(itemVal.toDouble(), itemLen, 'g', itemByteLen == 4 ? 6 : 15);\n        break;\n    }\n\n    return item;\n}\n\nQChar HexWidget::renderAscii(int offset, QColor *color)\n{\n    uchar byte;\n    data->copy(&byte, startAddress + offset, sizeof(byte));\n    if (color) {\n        *color = itemColor(byte);\n    }\n    if (!IS_PRINTABLE(byte)) {\n        byte = '.';\n    }\n    return QChar(byte);\n}\n\n/**\n * @brief Gets the available flags and comment at a specific address.\n * @param address Address of Item to be checked.\n * @return String containing the flags and comment available at the address.\n */\nQString HexWidget::getFlagsAndComment(uint64_t address)\n{\n    QString flagNames = Core()->listFlagsAsStringAt(address);\n    QString metaData = flagNames.isEmpty() ? \"\" : \"Flags: \" + flagNames.trimmed();\n\n    QString comment = Core()->getCommentAt(address);\n    if (!comment.isEmpty()) {\n        if (!metaData.isEmpty()) {\n            metaData.append(\"\\n\");\n        }\n        metaData.append(\"Comment: \" + comment.trimmed());\n    }\n\n    return metaData;\n}\n\nbool HexWidget::canKeyboardEdit()\n{\n    return ioModesController.canWrite() && actionKeyboardEdit->isChecked();\n}\n\ntemplate<class T, class BigValue>\nstatic bool checkRange(BigValue v)\n{\n    return v >= std::numeric_limits<T>::min() && v <= std::numeric_limits<T>::max();\n}\n\ntemplate<class T, class BigInteger>\nstatic bool checkAndWrite(BigInteger value, uint8_t *buf, bool littleEndian)\n{\n    if (!checkRange<T>(value)) {\n        return false;\n    }\n    if (littleEndian) {\n        qToLittleEndian((T)value, buf);\n    } else {\n        qToBigEndian((T)value, buf);\n    }\n    return true;\n}\n\ntemplate<class UType, class SType>\nstatic bool checkAndWriteWithSign(const QVariant &value, uint8_t *buf, bool isSigned,\n                                  bool littleEndian)\n{\n    if (isSigned) {\n        return checkAndWrite<SType>(value.toLongLong(), buf, littleEndian);\n    } else {\n        return checkAndWrite<UType>(value.toULongLong(), buf, littleEndian);\n    }\n}\n\nbool HexWidget::parseWord(QString word, uint8_t *buf, size_t bufferSize) const\n{\n    bool parseOk = false;\n    if (bufferSize < size_t(itemByteLen)) {\n        return false;\n    }\n    if (itemFormat == ItemFormatFloat) {\n        if (itemByteLen == 4) {\n            float value = word.toFloat(&parseOk);\n            if (!parseOk) {\n                return false;\n            }\n            if (itemBigEndian) {\n                rz_write_be_float(buf, value);\n            } else {\n                rz_write_le_float(buf, value);\n            }\n            return true;\n        } else if (itemByteLen == 8) {\n            double value = word.toDouble(&parseOk);\n            if (!parseOk) {\n                return false;\n            }\n            if (itemBigEndian) {\n                rz_write_be_double(buf, value);\n            } else {\n                rz_write_le_double(buf, value);\n            }\n            return true;\n        }\n        return false;\n    } else {\n        QVariant value;\n        bool isSigned = false;\n        switch (itemFormat) {\n        case ItemFormatHex:\n            value = word.toULongLong(&parseOk, 16);\n            break;\n        case ItemFormatOct:\n            value = word.toULongLong(&parseOk, 8);\n            break;\n        case ItemFormatDec:\n            value = word.toULongLong(&parseOk, 10);\n            break;\n        case ItemFormatSignedDec:\n            isSigned = true;\n            value = word.toLongLong(&parseOk, 10);\n            break;\n        default:\n            break;\n        }\n        if (!parseOk) {\n            return false;\n        }\n\n        switch (itemByteLen) {\n        case 1:\n            return checkAndWriteWithSign<uint8_t, int8_t>(value, buf, isSigned, !itemBigEndian);\n        case 2:\n            return checkAndWriteWithSign<uint16_t, int16_t>(value, buf, isSigned, !itemBigEndian);\n        case 4:\n            return checkAndWriteWithSign<quint32, qint32>(value, buf, isSigned, !itemBigEndian);\n        case 8:\n            return checkAndWriteWithSign<quint64, qint64>(value, buf, isSigned, !itemBigEndian);\n        }\n    }\n    return false;\n}\n\nbool HexWidget::flushCurrentlyEditedWord()\n{\n    if (editWordState < EditWordState::WriteEdited) {\n        return true;\n    }\n    uint8_t buf[16];\n    if (parseWord(editWord, buf, sizeof(buf))) {\n        data->write(buf, cursor.address, itemByteLen);\n        return true;\n    }\n    editWordState = EditWordState::WriteNotEdited;\n    return false;\n}\n\nbool HexWidget::finishEditingWord(bool force)\n{\n    if (editWordState == EditWordState::WriteEdited) {\n        if (!flushCurrentlyEditedWord() && !force) {\n            qWarning() << \"Not a valid number in current format or size\" << editWord;\n            showWarningRect(itemRectangle(cursor.address - startAddress).adjusted(-1, -1, 1, 1));\n            return false;\n        }\n    }\n    editWord.clear();\n    editWordPos = 0;\n    editWordState = canKeyboardEdit() ? EditWordState::WriteNotStarted : EditWordState::Read;\n    navigationMode = defaultNavigationMode();\n    return true;\n}\n\nvoid HexWidget::cancelEditedWord()\n{\n    editWordPos = 0;\n    editWordState = canKeyboardEdit() ? EditWordState::WriteNotStarted : EditWordState::Read;\n    editWord.clear();\n    navigationMode = defaultNavigationMode();\n    updateCursorMeta();\n    updateViewport();\n}\n\nvoid HexWidget::maybeFlushCharEdit()\n{\n    if (editWordState < EditWordState::WriteEdited) {\n        return;\n    }\n    if ((itemFormat == ItemFormatHex && earlyEditFlush >= EarlyEditFlush::EditNibble)\n        || (isFixedWidth() && earlyEditFlush >= EarlyEditFlush::EditFixedWidthChar)) {\n        flushCurrentlyEditedWord();\n        if (!flushCurrentlyEditedWord()) {\n            showWarningRect(itemRectangle(cursor.address - startAddress).adjusted(-1, -1, 1, 1));\n        }\n    }\n    updateViewport();\n}\n\nvoid HexWidget::startEditWord()\n{\n    if (!canKeyboardEdit()) {\n        return;\n    }\n    if (editWordState >= EditWordState::WriteNotEdited) {\n        return;\n    }\n    editWordPos = 0;\n    editWordState = EditWordState::WriteNotEdited;\n    navigationMode = defaultNavigationMode();\n    editWord = renderItem(cursor.address - startAddress).trimmed();\n    if (itemPrefixLen > 0) {\n        editWord = editWord.mid(itemPrefixLen);\n    }\n    updateViewport();\n}\n\nvoid HexWidget::fetchData()\n{\n    data.swap(oldData);\n    data->fetch(startAddress, bytesPerScreen());\n}\n\nBasicCursor HexWidget::screenPosToAddr(const QPoint &point, bool middle, int *wordOffset) const\n{\n    QPointF pt = point - itemArea.topLeft();\n\n    int relativeAddress = 0;\n    int line = static_cast<int>(pt.y() / lineHeight);\n    relativeAddress += line * itemRowByteLen();\n    int column = static_cast<int>(pt.x() / columnExWidth());\n    relativeAddress += column * itemGroupByteLen();\n    pt.rx() -= column * columnExWidth();\n    auto roundingOffset = middle ? itemWidth() / 2 : 0;\n    int posInGroup = static_cast<int>((pt.x() + roundingOffset) / itemWidth());\n    if (!middle) {\n        posInGroup = std::min(posInGroup, itemGroupSize - 1);\n    }\n    relativeAddress += posInGroup * itemByteLen;\n    pt.rx() -= posInGroup * itemWidth();\n    BasicCursor result(startAddress);\n    result += relativeAddress;\n\n    if (!middle && wordOffset != nullptr) {\n        int charPos = static_cast<int>((pt.x() / charWidth) + 0.5);\n        charPos -= itemPrefixLen;\n        charPos = std::max(0, charPos);\n        *wordOffset = charPos;\n    }\n    return result;\n}\n\nBasicCursor HexWidget::asciiPosToAddr(const QPoint &point, bool middle) const\n{\n    QPointF pt = point - asciiArea.topLeft();\n\n    int relativeAddress = 0;\n    relativeAddress += static_cast<int>(pt.y() / lineHeight) * itemRowByteLen();\n    auto roundingOffset = middle ? (charWidth / 2) : 0;\n    relativeAddress += static_cast<int>((pt.x() + (roundingOffset)) / charWidth);\n    BasicCursor result(startAddress);\n    result += relativeAddress;\n    return result;\n}\n\nBasicCursor HexWidget::currentAreaPosToAddr(const QPoint &point, bool middle) const\n{\n    return cursorOnAscii ? asciiPosToAddr(point, middle) : screenPosToAddr(point, middle);\n}\n\nBasicCursor HexWidget::mousePosToAddr(const QPoint &point, bool middle) const\n{\n    return asciiArea.contains(point) ? asciiPosToAddr(point, middle)\n                                     : screenPosToAddr(point, middle);\n}\n\nQRectF HexWidget::itemRectangle(int offset)\n{\n    qreal x;\n    qreal y;\n\n    qreal width = itemWidth();\n    y = (offset / itemRowByteLen()) * lineHeight;\n    offset %= itemRowByteLen();\n\n    x = (offset / itemGroupByteLen()) * columnExWidth();\n    offset %= itemGroupByteLen();\n    x += (offset / itemByteLen) * itemWidth();\n    if (offset == 0) {\n        x -= charWidth / 2;\n        width += charWidth / 2;\n    }\n    if (static_cast<int>(offset) == itemGroupByteLen() - 1) {\n        width += charWidth / 2;\n    }\n\n    x += itemArea.x();\n    y += itemArea.y();\n\n    return QRectF(x, y, width, lineHeight);\n}\n\nQRectF HexWidget::asciiRectangle(int offset)\n{\n    QPointF p;\n\n    p.ry() = (offset / itemRowByteLen()) * lineHeight;\n    offset %= itemRowByteLen();\n\n    p.rx() = offset * charWidth;\n\n    p += asciiArea.topLeft();\n\n    return QRectF(p, QSizeF(charWidth, lineHeight));\n}\n\nRVA HexWidget::getLocationAddress()\n{\n    return !selection.isEmpty() ? selection.start() : cursor.address;\n}\n\nvoid HexWidget::hideWarningRect()\n{\n    warningRectVisible = false;\n    updateViewport();\n}\n\nvoid HexWidget::showWarningRect(QRectF rect)\n{\n    warningRect = rect;\n    warningRectVisible = true;\n    warningTimer.start(WARNING_TIME_MS);\n    updateViewport();\n}\n\nvoid HexWidget::updateViewport()\n{\n    vScrollBar->setPosition(startAddress);\n    viewport()->update();\n}\n\nvoid HexWidget::scrollLines(int lines, bool clampToScrollBarRange)\n{\n    int64_t delta = -lines * itemRowByteLen();\n\n    if (lines == 0) {\n        return;\n    }\n\n    if (delta < 0 && startAddress < static_cast<uint64_t>(-delta)) {\n        startAddress = 0;\n    } else if (delta > 0 && data->maxIndex() < static_cast<uint64_t>(bytesPerScreen())) {\n        startAddress = 0;\n    } else if ((data->maxIndex() - startAddress)\n               <= static_cast<uint64_t>(bytesPerScreen() + delta - 1)) {\n        startAddress = (data->maxIndex() - bytesPerScreen()) + 1;\n    } else {\n        startAddress += delta;\n    }\n\n    if (clampToScrollBarRange) {\n        startAddress = vScrollBar->clampAddressToRange(startAddress);\n    }\n    fetchData();\n\n    updateCursorStatus();\n    updateViewport();\n}\n\nvoid HexWidget::setStartAddress(RVA address)\n{\n    RVA aligned = address - (address % itemRowByteLen());\n\n    uint64_t maxIdx = data->maxIndex();\n    uint64_t screenBytes = bytesPerScreen();\n    if (maxIdx > screenBytes) {\n        RVA maxStart = (maxIdx - screenBytes + 1);\n        maxStart -= (maxStart % itemRowByteLen());\n        aligned = std::min(aligned, maxStart);\n    } else {\n        aligned = 0;\n    }\n\n    if (aligned == startAddress) {\n        return;\n    }\n    startAddress = aligned;\n    fetchData();\n\n    updateCursorStatus();\n    updateViewport();\n}\n\nvoid HexWidget::updateCursorStatus()\n{\n    if (cursor.address >= startAddress && cursor.address <= lastVisibleAddr()) {\n        /* Don't enable cursor blinking if selection isn't empty */\n        cursorEnabled = selection.isEmpty();\n        updateCursorMeta();\n    } else {\n        cursorEnabled = false;\n    }\n}\n"
  },
  {
    "path": "src/widgets/HexWidget.h",
    "content": "#ifndef HEXWIDGET_H\n#define HEXWIDGET_H\n\n#include \"Cutter.h\"\n#include \"dialogs/HexdumpRangeDialog.h\"\n#include \"common/IOModesController.h\"\n\n#include <QScrollArea>\n#include <QTimer>\n#include <QMenu>\n#include <memory>\n\nstruct BasicCursor\n{\n    uint64_t address;\n    bool pastEnd;\n    explicit BasicCursor(uint64_t pos) : address(pos), pastEnd(false) {}\n    BasicCursor() : address(0), pastEnd(false) {}\n    BasicCursor &operator+=(int64_t offset)\n    {\n        if (offset < 0 && uint64_t(-offset) > address) {\n            address = 0;\n            pastEnd = false;\n        } else if (offset > 0 && uint64_t(offset) > (UINT64_MAX - address)) {\n            address = UINT64_MAX;\n            pastEnd = true;\n        } else {\n            address += uint64_t(offset);\n            pastEnd = false;\n        }\n        return *this;\n    }\n    BasicCursor &operator+=(int offset)\n    {\n        *this += int64_t(offset);\n        return *this;\n    }\n\n    bool moveChecked(int offset)\n    {\n        auto oldAddress = address;\n        *this += offset;\n        return address - oldAddress == uint64_t(offset);\n    }\n\n    BasicCursor &operator+=(uint64_t offset)\n    {\n        if (uint64_t(offset) > (UINT64_MAX - address)) {\n            address = UINT64_MAX;\n            pastEnd = true;\n        } else {\n            address += offset;\n            pastEnd = false;\n        }\n        return *this;\n    }\n    bool operator<(const BasicCursor &r) const\n    {\n        return address < r.address || (pastEnd < r.pastEnd);\n    }\n};\n\nstruct HexCursor\n{\n    HexCursor()\n    {\n        isVisible = false;\n        onAsciiArea = false;\n    }\n\n    bool isVisible;\n    bool onAsciiArea;\n    QTimer blinkTimer;\n    QRectF screenPos;\n    uint64_t address;\n    QString cachedChar;\n    QColor cachedColor;\n\n    void blink() { isVisible = !isVisible; }\n    void setBlinkPeriod(int msec) { blinkTimer.setInterval(msec / 2); }\n    void startBlinking() { blinkTimer.start(); }\n    void stopBlinking() { blinkTimer.stop(); }\n};\n\nclass AbstractData\n{\npublic:\n    virtual ~AbstractData() = default;\n    virtual void fetch(uint64_t addr, int len) = 0;\n    virtual bool copy(void *out, uint64_t adr, size_t len) = 0;\n    virtual bool write(const uint8_t *in, uint64_t adr, size_t len) = 0;\n    virtual uint64_t maxIndex() = 0;\n    virtual uint64_t minIndex() = 0;\n};\n\nclass BufferData : public AbstractData\n{\npublic:\n    BufferData() { m_buffer.fill(0, 1); }\n\n    explicit BufferData(const QByteArray &buffer)\n    {\n        if (buffer.isEmpty()) {\n            m_buffer.fill(0, 1);\n        } else {\n            m_buffer = buffer;\n        }\n    }\n\n    ~BufferData() override = default;\n\n    void fetch(uint64_t, int) override {}\n\n    bool copy(void *out, uint64_t addr, size_t len) override\n    {\n        if (addr < static_cast<uint64_t>(m_buffer.size())\n            && (static_cast<uint64_t>(m_buffer.size()) - addr) < len) {\n            memcpy(out, m_buffer.constData() + addr, len);\n            return true;\n        }\n        return false;\n    }\n\n    bool write(const uint8_t *in, uint64_t addr, size_t len) override\n    {\n        if (addr < static_cast<uint64_t>(m_buffer.size())\n            && (static_cast<uint64_t>(m_buffer.size()) - addr) < len) {\n            memcpy(m_buffer.data() + addr, in, len);\n            return true;\n        }\n        return false;\n    }\n\n    uint64_t maxIndex() override { return m_buffer.size() - 1; }\n\nprivate:\n    QByteArray m_buffer;\n};\n\nclass MemoryData : public AbstractData\n{\npublic:\n    MemoryData() = default;\n    ~MemoryData() override = default;\n    static constexpr size_t BLOCK_SIZE = 4096;\n\n    void fetch(uint64_t address, int length) override\n    {\n        // FIXME: reuse data if possible\n        const uint64_t blockSize = 0x1000ULL;\n        uint64_t alignedAddr = address & ~(blockSize - 1);\n        int offset = address - alignedAddr;\n        int len = (offset + length + (blockSize - 1)) & ~(blockSize - 1);\n        m_firstBlockAddr = alignedAddr;\n        m_lastValidAddr = length ? alignedAddr + len - 1 : 0;\n        if (m_lastValidAddr < m_firstBlockAddr) {\n            m_lastValidAddr = -1;\n            len = m_lastValidAddr - m_firstBlockAddr + 1;\n        }\n        m_blocks.clear();\n        uint64_t addr = alignedAddr;\n        for (ut64 i = 0; i < len / blockSize; ++i, addr += blockSize) {\n            m_blocks.append(Core()->ioRead(addr, blockSize));\n        }\n    }\n\n    bool copy(void *out, uint64_t addr, size_t len) override\n    {\n        if (addr < m_firstBlockAddr\n            || addr > m_lastValidAddr\n            /* do not merge with previous check to handle overflows */\n            || (m_lastValidAddr - addr + 1) < len || m_blocks.isEmpty()) {\n            memset(out, 0xff, len);\n            return false;\n        }\n\n        int totalOffset = addr - m_firstBlockAddr;\n        int blockId = totalOffset / BLOCK_SIZE;\n        int blockOffset = totalOffset % BLOCK_SIZE;\n        size_t first_part = BLOCK_SIZE - blockOffset;\n        if (first_part >= len) {\n            memcpy(out, m_blocks.at(blockId).constData() + blockOffset, len);\n        } else {\n            memcpy(out, m_blocks.at(blockId).constData() + blockOffset, first_part);\n            memcpy(static_cast<char *>(out) + first_part, m_blocks.at(blockId + 1).constData(),\n                   len - first_part);\n        }\n        return true;\n    }\n\n    void writeToCache(const uint8_t *in, uint64_t adr, size_t len)\n    {\n        if (adr < m_firstBlockAddr) {\n            uint64_t prefix = m_firstBlockAddr - adr;\n            if (prefix <= len) {\n                return;\n            }\n            in = in + prefix;\n            adr += prefix;\n            len -= prefix;\n        }\n        if (adr > m_lastValidAddr) {\n            return;\n        }\n        int offset = (int)(adr - m_firstBlockAddr);\n        int blockId = offset / BLOCK_SIZE;\n        int blockOffset = offset % BLOCK_SIZE;\n        while (len > 0 && blockId < m_blocks.size()) {\n            size_t l = BLOCK_SIZE - blockOffset;\n            l = std::min(l, len);\n            memcpy(m_blocks[blockId].data() + blockOffset, in, l);\n            len -= l;\n            blockOffset = 0;\n            adr += l;\n            in += l;\n            blockId += 1;\n        }\n    }\n\n    bool write(const uint8_t *in, uint64_t adr, size_t len) override\n    {\n        RzCoreLocked core(Core());\n        rz_core_write_at(core, adr, in, len);\n        writeToCache(in, adr, len);\n        emit Core()->instructionChanged(adr);\n        return true;\n    }\n\n    uint64_t maxIndex() override { return std::numeric_limits<uint64_t>::max(); }\n\n    uint64_t minIndex() override { return m_firstBlockAddr; }\n\nprivate:\n    QVector<QByteArray> m_blocks;\n    uint64_t m_firstBlockAddr = 0;\n    uint64_t m_lastValidAddr = 0;\n};\n\nclass HexSelection\n{\npublic:\n    HexSelection()\n    {\n        m_empty = true;\n        m_start = m_end = 0;\n    }\n\n    inline void init(BasicCursor addr)\n    {\n        m_empty = true;\n        m_init = addr;\n    }\n\n    void set(uint64_t start, uint64_t end)\n    {\n        m_empty = false;\n        m_init = BasicCursor(start);\n        m_start = start;\n        m_end = end;\n    }\n\n    void update(BasicCursor addr)\n    {\n        m_empty = false;\n        if (m_init < addr) {\n            m_start = m_init.address;\n            m_end = addr.address;\n            if (!addr.pastEnd)\n                m_end -= 1;\n        } else if (addr < m_init) {\n            m_start = addr.address;\n            m_end = m_init.address;\n            if (!m_init.pastEnd)\n                m_end -= 1;\n        } else {\n            m_start = m_end = m_init.address;\n            m_empty = true;\n        }\n    }\n\n    bool intersects(uint64_t start, uint64_t end)\n    {\n        return !m_empty && m_end >= start && m_start <= end;\n    }\n\n    bool contains(uint64_t pos) const { return !m_empty && m_start <= pos && pos <= m_end; }\n\n    uint64_t size() const\n    {\n        uint64_t size = 0;\n        if (!isEmpty())\n            size = m_end - m_start + 1;\n        return size;\n    }\n\n    inline bool isEmpty() const { return m_empty; }\n    inline uint64_t start() const { return m_start; }\n    inline uint64_t end() const { return m_end; }\n\nprivate:\n    BasicCursor m_init;\n    uint64_t m_start;\n    uint64_t m_end;\n    bool m_empty;\n};\n\nclass AddressRangeScrollBar;\n\nclass HexWidget : public QScrollArea\n{\n    Q_OBJECT\n\npublic:\n    explicit HexWidget(QWidget *parent = nullptr);\n    ~HexWidget() override = default;\n\n    void setMonospaceFont(const QFont &font);\n\n    enum AddrWidth { AddrWidth32 = 8, AddrWidth64 = 16 };\n    enum ItemSize { ItemSizeByte = 1, ItemSizeWord = 2, ItemSizeDword = 4, ItemSizeQword = 8 };\n    enum ItemFormat {\n        ItemFormatHex,\n        ItemFormatOct,\n        ItemFormatDec,\n        ItemFormatSignedDec,\n        ItemFormatFloat\n    };\n    enum class ColumnMode { Fixed, PowerOf2 };\n    enum class EditWordState { Read, WriteNotStarted, WriteNotEdited, WriteEdited };\n    enum class HexNavigationMode { Words, WordChar, AnyChar };\n\n    void setItemSize(int nbytes);\n    void setItemFormat(ItemFormat format);\n    void setItemEndianness(bool bigEndian);\n    void setItemGroupSize(int size);\n    /**\n     * @brief Sets line size in bytes.\n     * Changes column mode to fixed. Command can be rejected if current item format is bigger than\n     * requested size.\n     * @param bytes line size in bytes.\n     */\n    void setFixedLineSize(int bytes);\n    void setColumnMode(ColumnMode mode);\n\n    /**\n     * @brief Select non empty inclusive range [start; end]\n     * @param start\n     * @param end\n     */\n    void selectRange(RVA start, RVA end);\n    void clearSelection();\n\n    struct Selection\n    {\n        bool empty;\n        RVA startAddress;\n        RVA endAddress;\n    };\n    Selection getSelection();\npublic slots:\n    void seek(uint64_t address);\n    void refresh();\n    void updateColors();\nsignals:\n    void selectionChanged(HexWidget::Selection selection);\n    void positionChanged(RVA start);\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n    void resizeEvent(QResizeEvent *event) override;\n    void mouseMoveEvent(QMouseEvent *event) override;\n    void mousePressEvent(QMouseEvent *event) override;\n    void mouseDoubleClickEvent(QMouseEvent *event) override;\n    void mouseReleaseEvent(QMouseEvent *event) override;\n    void wheelEvent(QWheelEvent *event) override;\n    void keyPressEvent(QKeyEvent *event) override;\n    void contextMenuEvent(QContextMenuEvent *event) override;\n    bool event(QEvent *event) override;\n\nprivate slots:\n    void onCursorBlinked();\n    void onHexPairsModeEnabled(bool enable);\n    void copy();\n    void copyAddress();\n    void onRangeDialogAccepted();\n    void onActionAddCommentTriggered();\n    void onActionDeleteCommentTriggered();\n    void onActionAddFlagTriggered();\n    void onActionAddMarkTriggered();\n    void onActionDeleteMarkTriggered(const QString &name);\n    void onActionEditMarkTriggered(const QString &name);\n\n    // Write command slots\n    void w_writeString();\n    void w_increaseDecrease();\n    void w_writeBytes();\n    void w_writeZeros();\n    void w_write64();\n    void w_writeRandom();\n    void w_duplFromOffset();\n    void w_writePascalString();\n    void w_writeWideString();\n    void w_writeCString();\n\n    void onKeyboardEditTriggered(bool enabled);\n    void onKeyboardEditChanged(bool enabled);\n\nprivate:\n    void updateItemLength();\n    void updateCounts();\n    void drawHeader(QPainter &painter);\n    void drawCursor(QPainter &painter, bool shadow = false);\n    void drawAddrArea(QPainter &painter);\n    void drawItemArea(QPainter &painter);\n    void drawAsciiArea(QPainter &painter);\n    void fillSelectionBackground(QPainter &painter, bool ascii = false);\n    void fillMarks(QPainter &painter, bool ascii);\n    void updateMetrics();\n    void updateAreasPosition();\n    void updateAreasHeight();\n    enum class OverflowMove { Clamp, Ignore };\n    bool moveCursor(int offset, bool select = false,\n                    OverflowMove overflowMove = OverflowMove::Clamp);\n    void moveCursorKeepEditOffset(int byteOffset, bool select, OverflowMove overflowMove);\n    void setCursorAddr(BasicCursor addr, bool select = false);\n    void updateCursorMeta();\n    void setCursorOnAscii(bool ascii);\n    bool isItemDifferentAt(uint64_t address);\n    QColor itemColor(uint8_t byte);\n    QVariant readItem(int offset, QColor *color = nullptr);\n    QString renderItem(int offset, QColor *color = nullptr);\n    QChar renderAscii(int offset, QColor *color = nullptr);\n    QString getFlagsAndComment(uint64_t address);\n    /**\n     * @brief Get the location on which operations such as Writing should apply.\n     * @return Start of selection if multiple bytes are selected. Otherwise, the curren seek of the\n     * widget.\n     */\n    RVA getLocationAddress();\n\n    void fetchData();\n    /**\n     * @brief Convert mouse position to address.\n     * @param point mouse position in widget\n     * @param middle start next position from middle of symbol. Use middle=true for vertical cursor\n     * position between symbols, middle=false for insert mode cursor and getting symbol under\n     * cursor.\n     * @return\n     */\n    BasicCursor screenPosToAddr(const QPoint &point, bool middle = false,\n                                int *wordOffset = nullptr) const;\n    BasicCursor asciiPosToAddr(const QPoint &point, bool middle = false) const;\n    BasicCursor currentAreaPosToAddr(const QPoint &point, bool middle = false) const;\n    BasicCursor mousePosToAddr(const QPoint &point, bool middle = false) const;\n    /**\n     * @brief Rectangle for single item in data area.\n     * @param offset relative to first byte on screen\n     * @return\n     */\n    QRectF itemRectangle(int offset);\n    /**\n     * @brief Rectangle for single item in ascii area.\n     * @param offset relative to first byte on screen\n     * @return\n     */\n    QRectF asciiRectangle(int offset);\n    QVector<QPolygonF> rangePolygons(RVA start, RVA last, bool ascii);\n    void updateWidth();\n\n    inline qreal itemWidth() const { return itemCharLen * charWidth; }\n\n    inline int itemGroupCharLen() const { return itemCharLen * itemGroupSize; }\n\n    inline int columnExCharLen() const { return itemGroupCharLen() + columnSpacing; }\n\n    inline int itemGroupByteLen() const { return itemByteLen * itemGroupSize; }\n\n    inline qreal columnWidth() const { return itemGroupCharLen() * charWidth; }\n\n    inline qreal columnExWidth() const { return columnExCharLen() * charWidth; }\n\n    inline qreal columnSpacingWidth() const { return columnSpacing * charWidth; }\n\n    inline int itemRowCharLen() const { return itemColumns * columnExCharLen() - columnSpacing; }\n\n    inline int itemRowByteLen() const { return rowSizeBytes; }\n\n    inline int bytesPerScreen() const { return itemRowByteLen() * visibleLines; }\n\n    inline qreal itemRowWidth() const { return itemRowCharLen() * charWidth; }\n\n    inline qreal asciiRowWidth() const { return itemRowByteLen() * charWidth; }\n\n    inline qreal areaSpacingWidth() const { return areaSpacing * charWidth; }\n\n    inline uint64_t lastVisibleAddr() const { return (startAddress - 1) + bytesPerScreen(); }\n\n    const QRectF &currentArea() const { return cursorOnAscii ? asciiArea : itemArea; }\n    bool isFixedWidth() const;\n\n    bool canKeyboardEdit();\n    bool flushCurrentlyEditedWord();\n    bool finishEditingWord(bool force = true);\n    void maybeFlushCharEdit();\n    void cancelEditedWord();\n    void startEditWord();\n    bool validCharForEdit(QChar digit);\n    void movePrevEditCharAny();\n    void typeOverwriteModeChar(QChar c);\n    HexNavigationMode defaultNavigationMode();\n    void refreshWordEditState();\n    bool parseWord(QString word, uint8_t *buf, size_t bufferSize) const;\n    bool handleAsciiWrite(QKeyEvent *event);\n    bool handleNumberWrite(QKeyEvent *event);\n\n    void writeZeros(uint64_t address, uint64_t length);\n\n    void hideWarningRect();\n    void showWarningRect(QRectF rect);\n\n    void updateViewport();\n    void scrollLines(int lines, bool clampToScrollBarRange = false);\n    /**\n     * @brief Sets the given address as the first visible address of the view\n     * No action is taken if the address is already at the top\n     * @param address Target RVA to display at the top\n     */\n    void setStartAddress(RVA address);\n\n    /**\n     * @brief Updates cursor visibility and metadata\n     * The cursor is only visible if it is within the visible address range and selection is empty\n     */\n    void updateCursorStatus();\n\n    bool cursorEnabled;\n    bool cursorOnAscii;\n    HexCursor cursor;\n    HexCursor shadowCursor;\n\n    HexSelection selection;\n    bool updatingSelection;\n\n    QRectF addrArea;\n    QRectF itemArea;\n    QRectF asciiArea;\n\n    int itemByteLen = 1;\n    int itemGroupSize = 1; ///< Items per group (default: 1), 2 in case of hexpair mode\n    int rowSizeBytes = 16; ///< Line size in bytes\n    int itemColumns = 16; ///< Number of columns, single column consists of itemGroupSize items\n    int itemCharLen = 2;\n    int itemPrefixLen = 0;\n    ColumnMode columnMode;\n\n    ItemFormat itemFormat;\n\n    bool itemBigEndian;\n    QString itemPrefix;\n\n    int visibleLines;\n    uint64_t startAddress;\n    qreal charWidth;\n    qreal lineHeight;\n    int addrCharLen;\n    QFont monospaceFont;\n\n    bool showHeader;\n    bool showAscii;\n    bool showExHex;\n    bool showExAddr;\n\n    QColor borderColor;\n    QColor backgroundColor;\n    QColor defColor;\n    QColor addrColor;\n    QColor diffColor;\n    QColor b0x00Color;\n    QColor b0x7fColor;\n    QColor b0xffColor;\n    QColor printableColor;\n    QColor warningColor;\n\n    HexdumpRangeDialog rangeDialog;\n\n    /* Spacings in characters */\n    const int columnSpacing = 1;\n    const int areaSpacing = 2;\n\n    const QString hexPrefix = QStringLiteral(\"0x\");\n\n    QMenu *rowSizeMenu;\n    QAction *actionRowSizePowerOf2;\n    QList<QAction *> actionsItemSize;\n    QList<QAction *> actionsItemFormat;\n    QAction *actionItemBigEndian;\n    QAction *actionHexPairs;\n    QAction *actionCopy;\n    QAction *actionCopyAddress;\n    QAction *actionComment;\n    QAction *actionDeleteComment;\n    QAction *actionAddFlag;\n    QAction *actionAddMark;\n    QAction *actionSelectRange;\n    QAction *actionKeyboardEdit;\n    QList<QAction *> actionsWriteString;\n    QList<QAction *> actionsWriteOther;\n\n    std::unique_ptr<AbstractData> oldData;\n    std::unique_ptr<AbstractData> data;\n    IOModesController ioModesController;\n\n    int editWordPos = 0;\n    QString editWord;\n    EditWordState editWordState = EditWordState::Read;\n    HexNavigationMode navigationMode = HexNavigationMode::Words;\n    enum class EarlyEditFlush {\n        OnFinish,\n        EditNibble,\n        EditFixedWidthChar,\n        /* AllFormats(not implemented) */\n    };\n    EarlyEditFlush earlyEditFlush = EarlyEditFlush::EditFixedWidthChar;\n\n    bool warningRectVisible = false;\n    QRectF warningRect;\n    QTimer warningTimer;\n\n    AddressRangeScrollBar *vScrollBar;\n};\n\n#endif // HEXWIDGET_H\n"
  },
  {
    "path": "src/widgets/HexdumpWidget.cpp",
    "content": "#include \"HexdumpWidget.h\"\n#include \"ui_HexdumpWidget.h\"\n\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n#include \"common/TempConfig.h\"\n#include \"common/SyntaxHighlighter.h\"\n#include \"core/MainWindow.h\"\n\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QElapsedTimer>\n#include <QTextDocumentFragment>\n#include <QMenu>\n#include <QClipboard>\n#include <QScrollBar>\n#include <QInputDialog>\n#include <QShortcut>\n\nHexdumpWidget::HexdumpWidget(MainWindow *main)\n    : MemoryDockWidget(MemoryWidgetType::Hexdump, main), ui(new Ui::HexdumpWidget)\n{\n    ui->setupUi(this);\n\n    setObjectName(main ? main->getUniqueObjectName(getWidgetType()) : getWidgetType());\n    updateWindowTitle();\n\n    ui->copyMD5->setIcon(QIcon(\":/img/icons/copy.svg\"));\n    ui->copySHA1->setIcon(QIcon(\":/img/icons/copy.svg\"));\n    ui->copySHA256->setIcon(QIcon(\":/img/icons/copy.svg\"));\n    ui->copyCRC32->setIcon(QIcon(\":/img/icons/copy.svg\"));\n\n    ui->splitter->setChildrenCollapsible(false);\n\n    QToolButton *closeButton = new QToolButton;\n    QIcon closeIcon = QIcon(\":/img/icons/delete.svg\");\n    closeButton->setIcon(closeIcon);\n    closeButton->setAutoRaise(true);\n\n    ui->hexSideTab_2->setCornerWidget(closeButton);\n    syntaxHighLighter = Config()->createSyntaxHighlighter(ui->hexDisasTextEdit->document());\n\n    ui->openSideViewB->hide(); // hide button at startup since side view is visible\n\n    connect(closeButton, &QToolButton::clicked, this, [this] { showSidePanel(false); });\n\n    connect(ui->openSideViewB, &QToolButton::clicked, this, [this] { showSidePanel(true); });\n\n    // Set placeholders for the line-edit components\n    QString placeholder = tr(\"Select bytes to display information\");\n    ui->bytesMD5->setPlaceholderText(placeholder);\n    ui->bytesEntropy->setPlaceholderText(placeholder);\n    ui->bytesSHA1->setPlaceholderText(placeholder);\n    ui->bytesSHA256->setPlaceholderText(placeholder);\n    ui->bytesCRC32->setPlaceholderText(placeholder);\n    ui->hexDisasTextEdit->setPlaceholderText(placeholder);\n\n    setupFonts();\n\n    ui->openSideViewB->setStyleSheet(\"\"\n                                     \"QToolButton {\"\n                                     \"   border : 0px;\"\n                                     \"   padding : 0px;\"\n                                     \"   margin : 0px;\"\n                                     \"}\"\n                                     \"QToolButton:hover {\"\n                                     \"  border : 1px solid;\"\n                                     \"  border-width : 1px;\"\n                                     \"  border-color : #3daee9\"\n                                     \"}\");\n\n    refreshDeferrer = createReplacingRefreshDeferrer<RVA>(\n            false, [this](const RVA *offset) { refresh(offset ? *offset : RVA_INVALID); });\n\n    this->ui->hexTextView->addAction(&syncAction);\n\n    connect(Config(), &Configuration::fontsUpdated, this, &HexdumpWidget::fontsUpdated);\n    connect(Core(), &CutterCore::refreshAll, this, [this]() { refresh(); });\n    connect(Core(), &CutterCore::refreshCodeViews, this, [this]() { refresh(); });\n    connect(Core(), &CutterCore::instructionChanged, this, [this]() { refresh(); });\n    connect(Core(), &CutterCore::stackChanged, this, [this]() { refresh(); });\n    connect(Core(), &CutterCore::registersChanged, this, [this]() { refresh(); });\n\n    connect(seekable, &CutterSeekable::seekableSeekChanged, this, &HexdumpWidget::onSeekChanged);\n    connect(ui->hexTextView, &HexWidget::positionChanged, this, [this](RVA addr) {\n        if (!sent_seek) {\n            sent_seek = true;\n            seekable->seek(addr);\n            sent_seek = false;\n        }\n    });\n    connect(ui->hexTextView, &HexWidget::selectionChanged, this, &HexdumpWidget::selectionChanged);\n    connect(ui->hexSideTab_2, &QTabWidget::currentChanged, this,\n            &HexdumpWidget::refreshSelectionInfo);\n    ui->hexTextView->installEventFilter(this);\n\n    initParsing();\n    selectHexPreview();\n\n    // apply initial offset\n    refresh(seekable->getOffset());\n}\n\nvoid HexdumpWidget::onSeekChanged(RVA addr)\n{\n    if (sent_seek) {\n        sent_seek = false;\n        return;\n    }\n    refresh(addr);\n}\n\nHexdumpWidget::~HexdumpWidget() {}\n\nQString HexdumpWidget::getWidgetType()\n{\n    return \"Hexdump\";\n}\n\nvoid HexdumpWidget::refresh()\n{\n    refresh(RVA_INVALID);\n}\n\nvoid HexdumpWidget::refresh(RVA addr)\n{\n    if (!refreshDeferrer->attemptRefresh(addr == RVA_INVALID ? nullptr : new RVA(addr))) {\n        return;\n    }\n    sent_seek = true;\n    if (addr != RVA_INVALID) {\n        ui->hexTextView->seek(addr);\n    } else {\n        ui->hexTextView->refresh();\n        refreshSelectionInfo();\n    }\n    sent_seek = false;\n}\n\nvoid HexdumpWidget::initParsing()\n{\n    // Fill the plugins combo for the hexdump sidebar\n    ui->parseTypeComboBox->addItem(tr(\"Disassembly\"), \"pda\");\n    ui->parseTypeComboBox->addItem(tr(\"String\"), \"pcs\");\n    ui->parseTypeComboBox->addItem(tr(\"Assembler\"), \"pca\");\n    ui->parseTypeComboBox->addItem(tr(\"C bytes\"), \"pc\");\n    ui->parseTypeComboBox->addItem(tr(\"C half-words (2 byte)\"), \"pch\");\n    ui->parseTypeComboBox->addItem(tr(\"C words (4 byte)\"), \"pcw\");\n    ui->parseTypeComboBox->addItem(tr(\"C dwords (8 byte)\"), \"pcd\");\n    ui->parseTypeComboBox->addItem(tr(\"Python\"), \"pcp\");\n    ui->parseTypeComboBox->addItem(tr(\"JSON\"), \"pcj\");\n    ui->parseTypeComboBox->addItem(tr(\"JavaScript\"), \"pcJ\");\n    ui->parseTypeComboBox->addItem(tr(\"Yara\"), \"pcy\");\n\n    ui->parseArchComboBox->insertItems(0, Core()->getAsmPluginNames());\n\n    ui->parseEndianComboBox->setCurrentIndex(Core()->getConfigb(\"cfg.bigendian\") ? 1 : 0);\n}\n\nvoid HexdumpWidget::selectionChanged(HexWidget::Selection selection)\n{\n    if (selection.empty) {\n        clearParseWindow();\n    } else {\n        updateParseWindow(selection.startAddress,\n                          selection.endAddress - selection.startAddress + 1);\n    }\n}\n\nvoid HexdumpWidget::on_parseArchComboBox_currentTextChanged(const QString & /*arg1*/)\n{\n    refreshSelectionInfo();\n}\n\nvoid HexdumpWidget::on_parseBitsComboBox_currentTextChanged(const QString & /*arg1*/)\n{\n    refreshSelectionInfo();\n}\n\nvoid HexdumpWidget::setupFonts()\n{\n    QFont font = Config()->getFont();\n    ui->hexDisasTextEdit->setFont(font);\n    ui->hexTextView->setMonospaceFont(font);\n}\n\nvoid HexdumpWidget::refreshSelectionInfo()\n{\n    selectionChanged(ui->hexTextView->getSelection());\n}\n\nvoid HexdumpWidget::fontsUpdated()\n{\n    setupFonts();\n}\n\nvoid HexdumpWidget::clearParseWindow()\n{\n    ui->hexDisasTextEdit->setPlainText(\"\");\n    ui->bytesEntropy->setText(\"\");\n    ui->bytesMD5->setText(\"\");\n    ui->bytesSHA1->setText(\"\");\n    ui->bytesSHA256->setText(\"\");\n    ui->bytesCRC32->setText(\"\");\n}\n\nvoid HexdumpWidget::showSidePanel(bool show)\n{\n    ui->hexSideTab_2->setVisible(show);\n    ui->openSideViewB->setHidden(show);\n    if (show) {\n        refreshSelectionInfo();\n    }\n}\n\nQString HexdumpWidget::getWindowTitle() const\n{\n    return tr(\"Hexdump\");\n}\n\nvoid HexdumpWidget::updateParseWindow(RVA start_address, int size)\n{\n    if (!ui->hexSideTab_2->isVisible()) {\n        return;\n    }\n\n    if (ui->hexSideTab_2->currentIndex() == 0) {\n        // scope for TempConfig\n\n        // Get selected combos\n        QString arch = ui->parseArchComboBox->currentText();\n        QString bits = ui->parseBitsComboBox->currentText();\n        QString selectedCommand = ui->parseTypeComboBox->currentData().toString();\n        QString commandResult = \"\";\n        bool bigEndian = ui->parseEndianComboBox->currentIndex() == 1;\n\n        TempConfig tempConfig;\n        tempConfig.set(\"asm.arch\", arch).set(\"asm.bits\", bits).set(\"cfg.bigendian\", bigEndian);\n\n        ui->hexDisasTextEdit->setPlainText(\n                selectedCommand != \"\" ? Core()->cmdRawAt(\n                        QString(\"%1 @! %2\").arg(selectedCommand).arg(size), start_address)\n                                      : \"\");\n    } else {\n        // Fill the information tab hashes and entropy\n        RzHashSize digest_size = 0;\n        RzCoreLocked core(Core());\n        ut64 old_offset = core->offset;\n        rz_core_seek(core, start_address, true);\n        ut8 *block = core->block;\n        char *digest = rz_hash_cfg_calculate_small_block_string(core->hash, \"md5\", block, size,\n                                                                &digest_size, false);\n        ui->bytesMD5->setText(QString(digest));\n        free(digest);\n        digest = rz_hash_cfg_calculate_small_block_string(core->hash, \"sha1\", block, size,\n                                                          &digest_size, false);\n        ui->bytesSHA1->setText(QString(digest));\n        free(digest);\n        digest = rz_hash_cfg_calculate_small_block_string(core->hash, \"sha256\", block, size,\n                                                          &digest_size, false);\n        ui->bytesSHA256->setText(QString(digest));\n        free(digest);\n        digest = rz_hash_cfg_calculate_small_block_string(core->hash, \"crc32\", block, size,\n                                                          &digest_size, false);\n        ui->bytesCRC32->setText(QString(digest));\n        free(digest);\n        digest = rz_hash_cfg_calculate_small_block_string(core->hash, \"entropy\", block, size,\n                                                          &digest_size, false);\n        ui->bytesEntropy->setText(QString(digest));\n        free(digest);\n        rz_core_seek(core, old_offset, true);\n        ui->bytesMD5->setCursorPosition(0);\n        ui->bytesSHA1->setCursorPosition(0);\n        ui->bytesSHA256->setCursorPosition(0);\n        ui->bytesCRC32->setCursorPosition(0);\n    }\n}\n\nvoid HexdumpWidget::on_parseTypeComboBox_currentTextChanged(const QString &)\n{\n    QString currentParseTypeText = ui->parseTypeComboBox->currentData().toString();\n    if (currentParseTypeText == \"pda\" || currentParseTypeText == \"pci\") {\n        ui->hexSideFrame_2->show();\n    } else {\n        ui->hexSideFrame_2->hide();\n    }\n    refreshSelectionInfo();\n}\n\nvoid HexdumpWidget::on_parseEndianComboBox_currentTextChanged(const QString &)\n{\n    refreshSelectionInfo();\n}\n\nvoid HexdumpWidget::on_hexSideTab_2_currentChanged(int /*index*/)\n{\n    /*\n    if (index == 2) {\n        // Add data to HTML Polar functions graph\n        QFile html(\":/html/bar.html\");\n        if(!html.open(QIODevice::ReadOnly)) {\n            QMessageBox::information(0,\"error\",html.errorString());\n        }\n        QString code = html.readAll();\n        html.close();\n        this->histoWebView->setHtml(code);\n        this->histoWebView->show();\n    } else {\n        this->histoWebView->hide();\n    }\n    */\n}\n\nvoid HexdumpWidget::resizeEvent(QResizeEvent *event)\n{\n    // Heuristics to hide sidebar when it hides the content of the hexdump. 600px looks just \"okay\"\n    // Only applied when widget width is decreased to avoid unwanted behavior\n    if (event->oldSize().width() > event->size().width() && event->size().width() < 600) {\n        showSidePanel(false);\n    }\n    QDockWidget::resizeEvent(event);\n    refresh();\n}\n\nQWidget *HexdumpWidget::widgetToFocusOnRaise()\n{\n    return ui->hexTextView;\n}\n\nvoid HexdumpWidget::on_copyMD5_clicked()\n{\n    QString md5 = ui->bytesMD5->text();\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(md5);\n    Core()->message(\"MD5 copied to clipboard: \" + md5);\n}\n\nvoid HexdumpWidget::on_copySHA1_clicked()\n{\n    QString sha1 = ui->bytesSHA1->text();\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(sha1);\n    Core()->message(\"SHA1 copied to clipboard: \" + sha1);\n}\n\nvoid HexdumpWidget::on_copySHA256_clicked()\n{\n    QString sha256 = ui->bytesSHA256->text();\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(sha256);\n    Core()->message(\"SHA256 copied to clipboard: \" + sha256);\n}\n\nvoid HexdumpWidget::on_copyCRC32_clicked()\n{\n    QString crc32 = ui->bytesCRC32->text();\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(crc32);\n    Core()->message(\"CRC32 copied to clipboard: \" + crc32);\n}\n\nvoid HexdumpWidget::selectHexPreview()\n{\n    // Pre-select arch and bits in the hexdump sidebar\n    QString arch = Core()->getConfig(\"asm.arch\");\n    QString bits = Core()->getConfig(\"asm.bits\");\n\n    if (ui->parseArchComboBox->findText(arch) != -1) {\n        ui->parseArchComboBox->setCurrentIndex(ui->parseArchComboBox->findText(arch));\n    }\n\n    if (ui->parseBitsComboBox->findText(bits) != -1) {\n        ui->parseBitsComboBox->setCurrentIndex(ui->parseBitsComboBox->findText(bits));\n    }\n}\n"
  },
  {
    "path": "src/widgets/HexdumpWidget.h",
    "content": "#ifndef HEXDUMPWIDGET_H\n#define HEXDUMPWIDGET_H\n\n#include <QDebug>\n#include <QTextEdit>\n#include <QMouseEvent>\n#include <QAction>\n\n#include <array>\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"MemoryDockWidget.h\"\n#include \"common/CutterSeekable.h\"\n#include \"common/Highlighter.h\"\n#include \"common/SvgIconEngine.h\"\n#include \"HexWidget.h\"\n\n#include \"Dashboard.h\"\n\nnamespace Ui {\nclass HexdumpWidget;\n}\n\nclass RefreshDeferrer;\nclass QSyntaxHighlighter;\n\nclass HexdumpWidget : public MemoryDockWidget\n{\n    Q_OBJECT\npublic:\n    explicit HexdumpWidget(MainWindow *main);\n    ~HexdumpWidget() override;\n    Highlighter *highlighter;\n\n    static QString getWidgetType();\n\npublic slots:\n    void initParsing();\n\nprotected:\n    virtual void resizeEvent(QResizeEvent *event) override;\n    QWidget *widgetToFocusOnRaise() override;\n\nprivate:\n    std::unique_ptr<Ui::HexdumpWidget> ui;\n\n    bool sent_seek = false;\n\n    RefreshDeferrer *refreshDeferrer;\n    QSyntaxHighlighter *syntaxHighLighter;\n\n    void refresh();\n    void refresh(RVA addr);\n    void selectHexPreview();\n\n    void setupFonts();\n\n    void refreshSelectionInfo();\n    void updateParseWindow(RVA start_address, int size);\n    void clearParseWindow();\n    void showSidePanel(bool show);\n\n    QString getWindowTitle() const override;\n\nprivate slots:\n    void onSeekChanged(RVA addr);\n\n    void selectionChanged(HexWidget::Selection selection);\n\n    void on_parseArchComboBox_currentTextChanged(const QString &arg1);\n    void on_parseBitsComboBox_currentTextChanged(const QString &arg1);\n    void on_parseTypeComboBox_currentTextChanged(const QString &arg1);\n    void on_parseEndianComboBox_currentTextChanged(const QString &arg1);\n\n    void fontsUpdated();\n\n    void on_hexSideTab_2_currentChanged(int index);\n    void on_copyMD5_clicked();\n    void on_copySHA1_clicked();\n    void on_copySHA256_clicked();\n    void on_copyCRC32_clicked();\n};\n\n#endif // HEXDUMPWIDGET_H\n"
  },
  {
    "path": "src/widgets/HexdumpWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>HexdumpWidget</class>\n <widget class=\"QDockWidget\" name=\"HexdumpWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>959</width>\n    <height>807</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Hexdump</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QSplitter\" name=\"splitter\">\n      <property name=\"orientation\">\n       <enum>Qt::Horizontal</enum>\n      </property>\n      <widget class=\"HexWidget\" name=\"hexTextView\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n         <horstretch>1</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"frameShape\">\n        <enum>QFrame::NoFrame</enum>\n       </property>\n       <property name=\"widgetResizable\">\n        <bool>true</bool>\n       </property>\n      </widget>\n      <widget class=\"QTabWidget\" name=\"hexSideTab_2\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"tabPosition\">\n        <enum>QTabWidget::North</enum>\n       </property>\n       <property name=\"currentIndex\">\n        <number>1</number>\n       </property>\n       <property name=\"usesScrollButtons\">\n        <bool>true</bool>\n       </property>\n       <property name=\"documentMode\">\n        <bool>false</bool>\n       </property>\n       <widget class=\"QWidget\" name=\"tabDIsasm_2\">\n        <attribute name=\"title\">\n         <string>Parsing</string>\n        </attribute>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_23\">\n         <property name=\"spacing\">\n          <number>6</number>\n         </property>\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_16\">\n           <property name=\"spacing\">\n            <number>5</number>\n           </property>\n           <property name=\"rightMargin\">\n            <number>0</number>\n           </property>\n           <item>\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_17\">\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QComboBox\" name=\"parseTypeComboBox\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"label_6\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"text\">\n                <string>Endian</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QComboBox\" name=\"parseEndianComboBox\">\n               <property name=\"sizeAdjustPolicy\">\n                <enum>QComboBox::AdjustToContents</enum>\n               </property>\n               <item>\n                <property name=\"text\">\n                 <string>Little</string>\n                </property>\n               </item>\n               <item>\n                <property name=\"text\">\n                 <string>Big</string>\n                </property>\n               </item>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item>\n            <widget class=\"QFrame\" name=\"hexSideFrame_2\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>0</width>\n               <height>0</height>\n              </size>\n             </property>\n             <property name=\"frameShape\">\n              <enum>QFrame::NoFrame</enum>\n             </property>\n             <property name=\"frameShadow\">\n              <enum>QFrame::Plain</enum>\n             </property>\n             <property name=\"lineWidth\">\n              <number>0</number>\n             </property>\n             <layout class=\"QHBoxLayout\" name=\"horizontalLayout_21\">\n              <property name=\"sizeConstraint\">\n               <enum>QLayout::SetMinimumSize</enum>\n              </property>\n              <property name=\"leftMargin\">\n               <number>5</number>\n              </property>\n              <property name=\"topMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"rightMargin\">\n               <number>5</number>\n              </property>\n              <property name=\"bottomMargin\">\n               <number>5</number>\n              </property>\n              <item>\n               <widget class=\"QLabel\" name=\"hexArchLabel_2\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"text\">\n                 <string>Arch</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QComboBox\" name=\"parseArchComboBox\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"sizeAdjustPolicy\">\n                 <enum>QComboBox::AdjustToContents</enum>\n                </property>\n                <property name=\"frame\">\n                 <bool>false</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"hexBitsLabel_2\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"text\">\n                 <string>Bits</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QComboBox\" name=\"parseBitsComboBox\">\n                <property name=\"sizeAdjustPolicy\">\n                 <enum>QComboBox::AdjustToContents</enum>\n                </property>\n                <item>\n                 <property name=\"text\">\n                  <string>16</string>\n                 </property>\n                </item>\n                <item>\n                 <property name=\"text\">\n                  <string>32</string>\n                 </property>\n                </item>\n                <item>\n                 <property name=\"text\">\n                  <string>64</string>\n                 </property>\n                </item>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QPlainTextEdit\" name=\"hexDisasTextEdit\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"font\">\n              <font>\n               <family>Anonymous Pro</family>\n               <pointsize>13</pointsize>\n              </font>\n             </property>\n             <property name=\"frameShape\">\n              <enum>QFrame::NoFrame</enum>\n             </property>\n             <property name=\"lineWidth\">\n              <number>0</number>\n             </property>\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n             <property name=\"plainText\">\n              <string/>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"tabHistogram_2\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <attribute name=\"title\">\n         <string>Information</string>\n        </attribute>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n         <item>\n          <layout class=\"QGridLayout\" name=\"gridLayout\" columnstretch=\"0,0,0\">\n           <property name=\"sizeConstraint\">\n            <enum>QLayout::SetDefaultConstraint</enum>\n           </property>\n           <property name=\"rightMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"horizontalSpacing\">\n            <number>8</number>\n           </property>\n           <property name=\"verticalSpacing\">\n            <number>10</number>\n           </property>\n           <item row=\"3\" column=\"0\">\n            <widget class=\"QLabel\" name=\"labelSHA256\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"midLineWidth\">\n              <number>4</number>\n             </property>\n             <property name=\"text\">\n              <string>SHA256:</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"bytesMD5\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"frame\">\n              <bool>true</bool>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignJustify|Qt::AlignVCenter</set>\n             </property>\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"labelSHA1\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"text\">\n              <string>SHA1:</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"2\" alignment=\"Qt::AlignRight\">\n            <widget class=\"QToolButton\" name=\"copySHA256\">\n             <property name=\"toolTip\">\n              <string>Copy SHA256</string>\n             </property>\n             <property name=\"layoutDirection\">\n              <enum>Qt::RightToLeft</enum>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"2\">\n            <widget class=\"QToolButton\" name=\"copySHA1\">\n             <property name=\"toolTip\">\n              <string>Copy SHA1</string>\n             </property>\n             <property name=\"layoutDirection\">\n              <enum>Qt::RightToLeft</enum>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"bytesCRC32\">\n             <property name=\"frame\">\n              <bool>true</bool>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignJustify|Qt::AlignVCenter</set>\n             </property>\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"2\" alignment=\"Qt::AlignRight\">\n            <widget class=\"QToolButton\" name=\"copyCRC32\">\n             <property name=\"toolTip\">\n              <string>Copy CRC32</string>\n             </property>\n             <property name=\"layoutDirection\">\n              <enum>Qt::RightToLeft</enum>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"1\" colspan=\"2\">\n            <widget class=\"QLineEdit\" name=\"bytesEntropy\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"frame\">\n              <bool>true</bool>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignJustify|Qt::AlignVCenter</set>\n             </property>\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"0\" alignment=\"Qt::AlignLeft\">\n            <widget class=\"QLabel\" name=\"labelEntropy\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"text\">\n              <string>Entropy:</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"bytesSHA1\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"frame\">\n              <bool>true</bool>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignJustify|Qt::AlignVCenter</set>\n             </property>\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"labelMD5\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"text\">\n              <string>MD5:</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"0\">\n            <widget class=\"QLabel\" name=\"labelCRC32\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"text\">\n              <string>CRC32:</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"2\">\n            <widget class=\"QToolButton\" name=\"copyMD5\">\n             <property name=\"toolTip\">\n              <string>Copy MD5</string>\n             </property>\n             <property name=\"layoutDirection\">\n              <enum>Qt::RightToLeft</enum>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"bytesSHA256\">\n             <property name=\"frame\">\n              <bool>true</bool>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignJustify|Qt::AlignVCenter</set>\n             </property>\n             <property name=\"readOnly\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <spacer name=\"verticalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>20</width>\n             <height>40</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </widget>\n      </widget>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"QToolButton\" name=\"openSideViewB\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Preferred\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"maximumSize\">\n       <size>\n        <width>24</width>\n        <height>16777215</height>\n       </size>\n      </property>\n      <property name=\"styleSheet\">\n       <string notr=\"true\"/>\n      </property>\n      <property name=\"text\">\n       <string notr=\"true\"/>\n      </property>\n      <property name=\"icon\">\n       <iconset resource=\"../resources.qrc\">\n        <normaloff>:/img/icons/previous.svg</normaloff>:/img/icons/previous.svg</iconset>\n      </property>\n      <property name=\"iconSize\">\n       <size>\n        <width>16</width>\n        <height>12</height>\n       </size>\n      </property>\n      <property name=\"autoRaise\">\n       <bool>true</bool>\n      </property>\n      <property name=\"arrowType\">\n       <enum>Qt::NoArrow</enum>\n      </property>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>HexWidget</class>\n   <extends>QScrollArea</extends>\n   <header>HexWidget.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"../resources.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/ImportsWidget.cpp",
    "content": "#include \"ImportsWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QPainter>\n#include <QPen>\n#include <QShortcut>\n#include <QTreeWidget>\n\nImportsModel::ImportsModel(QObject *parent) : AddressableItemModel(parent) {}\n\nint ImportsModel::rowCount(const QModelIndex &parent) const\n{\n    return parent.isValid() ? 0 : imports.count();\n}\n\nint ImportsModel::columnCount(const QModelIndex &) const\n{\n    return ImportsModel::ColumnCount;\n}\n\nQVariant ImportsModel::data(const QModelIndex &index, int role) const\n{\n    const ImportDescription &import = imports.at(index.row());\n    switch (role) {\n    case Qt::ForegroundRole:\n        if (index.column() < ImportsModel::ColumnCount) {\n            // Red color for unsafe functions\n            if (banned.match(import.name).hasMatch())\n                return Config()->getColor(\"gui.item_unsafe\");\n            // Grey color for symbols at offset 0 which can only be filled at runtime\n            if (import.plt == 0)\n                return Config()->getColor(\"gui.item_invalid\");\n        }\n        break;\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case ImportsModel::AddressColumn:\n            return RzAddressString(import.plt);\n        case ImportsModel::TypeColumn:\n            return import.type;\n        case ImportsModel::SafetyColumn:\n            return banned.match(import.name).hasMatch() ? tr(\"Unsafe\") : QStringLiteral(\"\");\n        case ImportsModel::LibraryColumn:\n            return import.libname;\n        case ImportsModel::NameColumn:\n            return import.name;\n        case ImportsModel::CommentColumn:\n            return Core()->getCommentAt(import.plt);\n        default:\n            break;\n        }\n        break;\n    case ImportsModel::ImportDescriptionRole:\n        return QVariant::fromValue(import);\n    case ImportsModel::AddressRole:\n        return import.plt;\n    default:\n        break;\n    }\n    return QVariant();\n}\n\nQVariant ImportsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    if (role == Qt::DisplayRole) {\n        switch (section) {\n        case ImportsModel::AddressColumn:\n            return tr(\"Address\");\n        case ImportsModel::TypeColumn:\n            return tr(\"Type\");\n        case ImportsModel::SafetyColumn:\n            return tr(\"Safety\");\n        case ImportsModel::LibraryColumn:\n            return tr(\"Library\");\n        case ImportsModel::NameColumn:\n            return tr(\"Name\");\n        case ImportsModel::CommentColumn:\n            return tr(\"Comment\");\n        default:\n            break;\n        }\n    }\n    return QVariant();\n}\n\nRVA ImportsModel::address(const QModelIndex &index) const\n{\n    const ImportDescription &import = imports.at(index.row());\n    return import.plt;\n}\n\nQString ImportsModel::name(const QModelIndex &index) const\n{\n    const ImportDescription &import = imports.at(index.row());\n    return import.name;\n}\n\nQString ImportsModel::libname(const QModelIndex &index) const\n{\n    const ImportDescription &import = imports.at(index.row());\n    return import.libname;\n}\n\nvoid ImportsModel::reload()\n{\n    beginResetModel();\n    imports = Core()->getAllImports();\n    endResetModel();\n}\n\nImportsProxyModel::ImportsProxyModel(ImportsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool ImportsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    auto import = index.data(ImportsModel::ImportDescriptionRole).value<ImportDescription>();\n\n    return qhelpers::filterStringContains(import.name, this);\n}\n\nbool ImportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    if (!left.isValid() || !right.isValid())\n        return false;\n\n    if (left.parent().isValid() || right.parent().isValid())\n        return false;\n\n    auto leftImport = left.data(ImportsModel::ImportDescriptionRole).value<ImportDescription>();\n    auto rightImport = right.data(ImportsModel::ImportDescriptionRole).value<ImportDescription>();\n\n    switch (left.column()) {\n    case ImportsModel::AddressColumn:\n        return leftImport.plt < rightImport.plt;\n    case ImportsModel::TypeColumn:\n        return leftImport.type < rightImport.type;\n    case ImportsModel::SafetyColumn:\n        break;\n    case ImportsModel::LibraryColumn:\n        if (leftImport.libname != rightImport.libname)\n            return leftImport.libname < rightImport.libname;\n    // fallthrough\n    case ImportsModel::NameColumn:\n        return leftImport.name < rightImport.name;\n    case ImportsModel::CommentColumn:\n        return Core()->getCommentAt(leftImport.plt) < Core()->getCommentAt(rightImport.plt);\n\n    default:\n        break;\n    }\n\n    return false;\n}\n\n/*\n * Imports Widget\n */\n\nImportsWidget::ImportsWidget(MainWindow *main)\n    : ListDockWidget(main),\n      importsModel(new ImportsModel(this)),\n      importsProxyModel(new ImportsProxyModel(importsModel, this))\n{\n    setWindowTitle(tr(\"Imports\"));\n    setObjectName(\"ImportsWidget\");\n\n    setModels(importsProxyModel);\n    // Sort by library name by default to create a solid context per each group of imports\n    ui->treeView->sortByColumn(ImportsModel::LibraryColumn, Qt::AscendingOrder);\n    QShortcut *toggle_shortcut = Shortcuts()->makeQShortcut(\"Imports.toggle\", main);\n    connect(toggle_shortcut, &QShortcut::activated, this, [=]() { toggleDockWidget(true); });\n\n    connect(Core(), &CutterCore::codeRebased, this, &ImportsWidget::refreshImports);\n    connect(Core(), &CutterCore::refreshAll, this, &ImportsWidget::refreshImports);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(importsModel, ImportsModel::CommentColumn); });\n}\n\nImportsWidget::~ImportsWidget() {}\n\nvoid ImportsWidget::refreshImports()\n{\n    importsModel->reload();\n    qhelpers::adjustColumns(ui->treeView, 4, 0);\n}\n"
  },
  {
    "path": "src/widgets/ImportsWidget.h",
    "content": "#ifndef IMPORTSWIDGET_H\n#define IMPORTSWIDGET_H\n\n#include <memory>\n\n#include <QAbstractTableModel>\n#include <QSortFilterProxyModel>\n#include <QRegularExpression>\n#include <QStyledItemDelegate>\n#include <QTreeWidgetItem>\n\n#include \"CutterDockWidget.h\"\n#include \"core/Cutter.h\"\n#include \"widgets/ListDockWidget.h\"\n#include \"common/AddressableItemModel.h\"\n\nclass MainWindow;\nclass QTreeWidget;\nclass ImportsWidget;\n\nclass ImportsModel : public AddressableItemModel<QAbstractTableModel>\n{\n    Q_OBJECT\n\n    friend ImportsWidget;\n\nprivate:\n    const QRegularExpression banned = QRegularExpression(\n            QStringLiteral(\"\\\\A(\\\\w\\\\.)*(system|strcpy|strcpyA|strcpyW|wcscpy|_tcscpy|_mbscpy|\"\n                           \"StrCpy|StrCpyA|StrCpyW|lstrcpy|lstrcpyA|lstrcpyW\"\n                           \"DCIEnum|DCIOpenProvider|DCISendCommand|DCIBeginAccess\"\n                           \"|_tccpy|_mbccpy|_ftcscpy|strcat|strcatA|strcatW|wcscat|_tcscat|_mbscat|\"\n                           \"StrCat|StrCatA|StrCatW|lstrcat|lstrcatA|\"\n                           \"lstrcatW|StrCatBuff|StrCatBuffA|StrCatBuffW|StrCatChainW|_tccat|_\"\n                           \"mbccat|_ftcscat|sprintfW|sprintfA|wsprintf|wsprintfW|\"\n                           \"wsprintfA|sprintf|swprintf|_stprintf|wvsprintf|wvsprintfA|wvsprintfW|\"\n                           \"vsprintf|_vstprintf|vswprintf|strncpy|wcsncpy|\"\n                           \"_tcsncpy|_mbsncpy|_mbsnbcpy|StrCpyN|StrCpyNA|StrCpyNW|StrNCpy|strcpynA|\"\n                           \"StrNCpyA|StrNCpyW|lstrcpyn|lstrcpynA|lstrcpynW|\"\n                           \"strncat|wcsncat|_tcsncat|_mbsncat|_mbsnbcat|StrCatN|StrCatNA|StrCatNW|\"\n                           \"StrNCat|StrNCatA|StrNCatW|lstrncat|lstrcatnA|\"\n                           \"lstrcatnW|lstrcatn|gets|_getts|_gettws|IsBadWritePtr|IsBadHugeWritePtr|\"\n                           \"IsBadReadPtr|IsBadHugeReadPtr|IsBadCodePtr|\"\n                           \"IsBadStringPtr|memcpy|RtlCopyMemory|CopyMemory|wmemcpy|wnsprintf|\"\n                           \"wnsprintfA|wnsprintfW|_snwprintf|_snprintf|_sntprintf|\"\n                           \"_vsnprintf|vsnprintf|_vsnwprintf|_vsntprintf|wvnsprintf|wvnsprintfA|\"\n                           \"wvnsprintfW|strtok|_tcstok|wcstok|_mbstok|makepath|\"\n                           \"_tmakepath| \"\n                           \"_makepath|_wmakepath|_splitpath|_tsplitpath|_wsplitpath|scanf|wscanf|_\"\n                           \"tscanf|sscanf|swscanf|_stscanf|snscanf|\"\n                           \"snwscanf|_sntscanf|_itoa|_itow|_i64toa|_i64tow|_ui64toa|_ui64tot|_\"\n                           \"ui64tow|_ultoa|_ultot|_ultow|CharToOem|CharToOemA|CharToOemW|\"\n                           \"OemToChar|OemToCharA|OemToCharW|CharToOemBuffA|CharToOemBuffW|alloca|_\"\n                           \"alloca|strlen|wcslen|_mbslen|_mbstrlen|StrLen|lstrlen|\"\n                           \"ChangeWindowMessageFilter|ChangeWindowMessageFilterEx)\\\\z\"));\n    QList<ImportDescription> imports;\n\npublic:\n    enum Column {\n        AddressColumn = 0,\n        TypeColumn,\n        LibraryColumn,\n        NameColumn,\n        SafetyColumn,\n        CommentColumn,\n        ColumnCount\n    };\n    enum Role { ImportDescriptionRole = Qt::UserRole, AddressRole };\n\n    ImportsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent) const override;\n    int columnCount(const QModelIndex &parent) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n    QString libname(const QModelIndex &index) const;\n    void reload();\n};\n\nclass ImportsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    ImportsProxyModel(ImportsModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass ImportsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit ImportsWidget(MainWindow *main);\n    ~ImportsWidget();\n\nprivate slots:\n    void refreshImports();\n\nprivate:\n    ImportsModel *importsModel;\n    ImportsProxyModel *importsProxyModel;\n\n    void highlightUnsafe();\n};\n\n#endif // IMPORTSWIDGET_H\n"
  },
  {
    "path": "src/widgets/ListDockWidget.cpp",
    "content": "#include \"ListDockWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QMenu>\n#include <QResizeEvent>\n#include <QShortcut>\n\nListDockWidget::ListDockWidget(MainWindow *main, SearchBarPolicy searchBarPolicy)\n    : CutterDockWidget(main),\n      ui(new Ui::ListDockWidget),\n      tree(new CutterTreeWidget(this)),\n      searchBarPolicy(searchBarPolicy)\n{\n    ui->setupUi(this);\n\n    // Add Status Bar footer\n    tree->addStatusBar(ui->verticalLayout);\n\n    if (searchBarPolicy != SearchBarPolicy::Hide) {\n        // Ctrl-F to show/hide the filter entry\n        QShortcut *searchShortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n        connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,\n                &QuickFilterView::showFilter);\n        searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n        // Esc to clear the filter entry\n        QShortcut *clearShortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n        connect(clearShortcut, &QShortcut::activated, [this]() {\n            ui->quickFilterView->clearFilter();\n            ui->treeView->setFocus();\n        });\n        clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n    }\n\n    qhelpers::setVerticalScrollMode(ui->treeView);\n\n    ui->treeView->setMainWindow(mainWindow);\n\n    if (searchBarPolicy != SearchBarPolicy::ShowByDefault) {\n        ui->quickFilterView->closeFilter();\n    }\n}\n\nListDockWidget::~ListDockWidget() {}\n\nvoid ListDockWidget::showCount(bool show)\n{\n    tree->showStatusBar(show);\n}\n\nvoid ListDockWidget::setModels(AddressableFilterProxyModel *objectFilterProxyModel)\n{\n    this->objectFilterProxyModel = objectFilterProxyModel;\n\n    ui->treeView->setModel(objectFilterProxyModel);\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, objectFilterProxyModel,\n            &QSortFilterProxyModel::setFilterWildcard);\n    connect(ui->quickFilterView, &QuickFilterView::filterClosed, ui->treeView,\n            static_cast<void (QWidget::*)()>(&QWidget::setFocus));\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this,\n            [this] { tree->showItemsNumber(this->objectFilterProxyModel->rowCount()); });\n}\n"
  },
  {
    "path": "src/widgets/ListDockWidget.h",
    "content": "#ifndef LISTDOCKWIDGET_H\n#define LISTDOCKWIDGET_H\n\n#include <memory>\n#include <QAbstractItemModel>\n#include <QSortFilterProxyModel>\n#include <QMenu>\n\n#include \"core/Cutter.h\"\n#include \"common/AddressableItemModel.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeWidget.h\"\n#include \"menus/AddressableItemContextMenu.h\"\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass CommentsWidget;\n\nnamespace Ui {\nclass ListDockWidget;\n}\n\nclass CUTTER_EXPORT ListDockWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    enum class SearchBarPolicy {\n        ShowByDefault,\n        HideByDefault,\n        Hide,\n    };\n\n    explicit ListDockWidget(MainWindow *main,\n                            SearchBarPolicy searchBarPolicy = SearchBarPolicy::ShowByDefault);\n    ~ListDockWidget() override;\n\n    void showCount(bool show);\n\nprotected:\n    void setModels(AddressableFilterProxyModel *objectFilterProxyModel);\n\n    std::unique_ptr<Ui::ListDockWidget> ui;\n\nprivate:\n    AddressableFilterProxyModel *objectFilterProxyModel = nullptr;\n    CutterTreeWidget *tree;\n    SearchBarPolicy searchBarPolicy;\n};\n\n#endif // LISTDOCKWIDGET_H\n"
  },
  {
    "path": "src/widgets/ListDockWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ListDockWidget</class>\n <widget class=\"QDockWidget\" name=\"ListDockWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>645</width>\n    <height>250</height>\n   </rect>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"AddressableItemList&lt;&gt;\" name=\"treeView\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n      <attribute name=\"headerShowSortIndicator\" stdset=\"0\">\n       <bool>true</bool>\n      </attribute>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"QuickFilterView\" name=\"quickFilterView\" native=\"true\"/>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>AddressableItemList&lt;&gt;</class>\n   <extends>QTreeView</extends>\n   <header>widgets/AddressableItemList.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>QuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/QuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/MemoryDockWidget.cpp",
    "content": "#include \"MemoryDockWidget.h\"\n#include \"common/CutterSeekable.h\"\n#include \"MainWindow.h\"\n#include <QAction>\n#include <QEvent>\n#include <QMenu>\n#include <QContextMenuEvent>\n\nMemoryDockWidget::MemoryDockWidget(MemoryWidgetType type, MainWindow *parent)\n    : AddressableDockWidget(parent), mType(type)\n{\n    if (parent) {\n        parent->addMemoryDockWidget(this);\n    }\n}\n\nbool MemoryDockWidget::tryRaiseMemoryWidget()\n{\n    if (!seekable->isSynchronized()) {\n        return false;\n    }\n\n    if (mType == MemoryWidgetType::Graph && Core()->isGraphEmpty()) {\n        return false;\n    }\n    raiseMemoryWidget();\n\n    return true;\n}\n\nbool MemoryDockWidget::eventFilter(QObject *object, QEvent *event)\n{\n    if (mainWindow && event->type() == QEvent::FocusIn) {\n        mainWindow->setCurrentMemoryWidget(this);\n    }\n    return CutterDockWidget::eventFilter(object, event);\n}\n"
  },
  {
    "path": "src/widgets/MemoryDockWidget.h",
    "content": "#ifndef MEMORYDOCKWIDGET_H\n#define MEMORYDOCKWIDGET_H\n\n#include \"AddressableDockWidget.h\"\n#include \"core/Cutter.h\"\n\n#include <QAction>\n\n/* Disassembly/Graph/Hexdump/Decompiler view priority */\nenum class MemoryWidgetType { Disassembly, Graph, Hexdump, Decompiler, CallGraph, GlobalCallGraph };\n\nclass CUTTER_EXPORT MemoryDockWidget : public AddressableDockWidget\n{\n    Q_OBJECT\npublic:\n    MemoryDockWidget(MemoryWidgetType type, MainWindow *parent);\n    ~MemoryDockWidget() override {}\n\n    bool tryRaiseMemoryWidget();\n    MemoryWidgetType getType() const { return mType; }\n    bool eventFilter(QObject *object, QEvent *event) override;\n\nprivate:\n    MemoryWidgetType mType;\n};\n\n#endif // MEMORYDOCKWIDGET_H\n"
  },
  {
    "path": "src/widgets/MemoryMapWidget.cpp",
    "content": "#include \"MemoryMapWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include <QShortcut>\n\nMemoryMapModel::MemoryMapModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent)\n{\n}\n\nint MemoryMapModel::rowCount(const QModelIndex &) const\n{\n    return memoryMaps.count();\n}\n\nint MemoryMapModel::columnCount(const QModelIndex &) const\n{\n    return MemoryMapModel::ColumnCount;\n}\n\nQVariant MemoryMapModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= memoryMaps.count())\n        return QVariant();\n\n    const MemoryMapDescription &memoryMap = memoryMaps.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case AddrStartColumn:\n            return RzAddressString(memoryMap.addrStart);\n        case AddrEndColumn:\n            return RzAddressString(memoryMap.addrEnd);\n        case NameColumn:\n            return memoryMap.name;\n        case PermColumn:\n            return memoryMap.permission;\n        case CommentColumn:\n            return Core()->getCommentAt(memoryMap.addrStart);\n        default:\n            return QVariant();\n        }\n    case MemoryDescriptionRole:\n        return QVariant::fromValue(memoryMap);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant MemoryMapModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case AddrStartColumn:\n            return tr(\"Offset start\");\n        case AddrEndColumn:\n            return tr(\"Offset end\");\n        case NameColumn:\n            return tr(\"Name\");\n        case PermColumn:\n            return tr(\"Permissions\");\n        case CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA MemoryMapModel::address(const QModelIndex &index) const\n{\n    const MemoryMapDescription &memoryMap = memoryMaps.at(index.row());\n    return memoryMap.addrStart;\n}\n\nMemoryProxyModel::MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n}\n\nbool MemoryProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    MemoryMapDescription item =\n            index.data(MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();\n    return qhelpers::filterStringContains(item.name, this);\n}\n\nbool MemoryProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    MemoryMapDescription leftMemMap =\n            left.data(MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();\n    MemoryMapDescription rightMemMap =\n            right.data(MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();\n\n    switch (left.column()) {\n    case MemoryMapModel::AddrStartColumn:\n        return leftMemMap.addrStart < rightMemMap.addrStart;\n    case MemoryMapModel::AddrEndColumn:\n        return leftMemMap.addrEnd < rightMemMap.addrEnd;\n    case MemoryMapModel::NameColumn:\n        return leftMemMap.name < rightMemMap.name;\n    case MemoryMapModel::PermColumn:\n        return leftMemMap.permission < rightMemMap.permission;\n    case MemoryMapModel::CommentColumn:\n        return Core()->getCommentAt(leftMemMap.addrStart)\n                < Core()->getCommentAt(rightMemMap.addrStart);\n    default:\n        break;\n    }\n\n    return leftMemMap.addrStart < rightMemMap.addrStart;\n}\n\nMemoryMapWidget::MemoryMapWidget(MainWindow *main)\n    : ListDockWidget(main, ListDockWidget::SearchBarPolicy::HideByDefault)\n{\n    setWindowTitle(tr(\"Memory Map\"));\n    setObjectName(\"MemoryMapWidget\");\n\n    memoryModel = new MemoryMapModel(this);\n    memoryProxyModel = new MemoryProxyModel(memoryModel, this);\n    setModels(memoryProxyModel);\n    ui->treeView->sortByColumn(MemoryMapModel::AddrStartColumn, Qt::AscendingOrder);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { refreshMemoryMap(); });\n\n    connect(Core(), &CutterCore::refreshAll, this, &MemoryMapWidget::refreshMemoryMap);\n    connect(Core(), &CutterCore::registersChanged, this, &MemoryMapWidget::refreshMemoryMap);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(memoryModel, MemoryMapModel::CommentColumn); });\n\n    showCount(false);\n}\n\nMemoryMapWidget::~MemoryMapWidget() = default;\n\nvoid MemoryMapWidget::refreshMemoryMap()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    if (Core()->currentlyEmulating) {\n        return;\n    }\n    memoryModel->beginResetModel();\n    memoryModel->memoryMaps = Core()->getMemoryMap();\n    memoryModel->endResetModel();\n\n    ui->treeView->resizeColumnToContents(0);\n    ui->treeView->resizeColumnToContents(1);\n    ui->treeView->resizeColumnToContents(2);\n}\n"
  },
  {
    "path": "src/widgets/MemoryMapWidget.h",
    "content": "#pragma once\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"ListDockWidget.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidget;\nclass MemoryMapWidget;\n\nnamespace Ui {\nclass MemoryMapWidget;\n}\n\nclass MainWindow;\nclass QTreeWidgetItem;\n\nclass MemoryMapModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend MemoryMapWidget;\n\nprivate:\n    QList<MemoryMapDescription> memoryMaps;\n\npublic:\n    enum Column {\n        AddrStartColumn = 0,\n        AddrEndColumn,\n        NameColumn,\n        PermColumn,\n        CommentColumn,\n        ColumnCount\n    };\n    enum Role { MemoryDescriptionRole = Qt::UserRole };\n\n    MemoryMapModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n};\n\nclass MemoryProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass MemoryMapWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit MemoryMapWidget(MainWindow *main);\n    ~MemoryMapWidget();\n\nprivate slots:\n\n    void refreshMemoryMap();\n\nprivate:\n    MemoryMapModel *memoryModel;\n    MemoryProxyModel *memoryProxyModel;\n\n    RefreshDeferrer *refreshDeferrer;\n};\n"
  },
  {
    "path": "src/widgets/Omnibar.cpp",
    "content": "#include \"Omnibar.h\"\n#include \"core/MainWindow.h\"\n#include \"CutterSeekable.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QStringListModel>\n#include <QCompleter>\n#include <QShortcut>\n#include <QAbstractItemView>\n\nOmnibar::Omnibar(MainWindow *main, QWidget *parent) : QLineEdit(parent), main(main)\n{\n    // QLineEdit basic features\n    this->setMinimumHeight(16);\n    this->setFrame(false);\n    this->setPlaceholderText(tr(\"Type flag name or address here\"));\n    this->setStyleSheet(\"border-radius: 5px; padding: 0 8px; margin: 5px 0;\");\n    this->setTextMargins(10, 0, 0, 0);\n    this->setClearButtonEnabled(true);\n\n    connect(this, &QLineEdit::returnPressed, this, &Omnibar::on_gotoEntry_returnPressed);\n\n    // Esc clears omnibar\n    QShortcut *clear_shortcut = Shortcuts()->makeQShortcut(\"Omnibar.clear\", this);\n    connect(clear_shortcut, &QShortcut::activated, this, &Omnibar::clear);\n    clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut);\n}\n\nvoid Omnibar::setupCompleter()\n{\n    // Set gotoEntry completer for jump history\n    QCompleter *completer = new QCompleter(flags, this);\n    completer->setMaxVisibleItems(20);\n    completer->setCompletionMode(QCompleter::PopupCompletion);\n    completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);\n    completer->setCaseSensitivity(Qt::CaseInsensitive);\n    completer->setFilterMode(Qt::MatchContains);\n\n    this->setCompleter(completer);\n}\n\nvoid Omnibar::refresh(const QStringList &flagList)\n{\n    flags = flagList;\n\n    setupCompleter();\n}\n\nvoid Omnibar::restoreCompleter()\n{\n    QCompleter *completer = this->completer();\n    if (!completer) {\n        return;\n    }\n    completer->setFilterMode(Qt::MatchContains);\n}\n\nvoid Omnibar::clear()\n{\n    QLineEdit::clear();\n\n    // Close the potential shown completer popup\n    clearFocus();\n    setFocus();\n}\n\nvoid Omnibar::on_gotoEntry_returnPressed()\n{\n    QString str = this->text();\n    if (!str.isEmpty()) {\n        if (auto memoryWidget = main->getLastMemoryWidget()) {\n            RVA offset = Core()->math(str);\n            memoryWidget->getSeekable()->seek(offset);\n            memoryWidget->raiseMemoryWidget();\n        } else {\n            Core()->seekAndShow(str);\n        }\n    }\n\n    this->setText(\"\");\n    this->clearFocus();\n    this->restoreCompleter();\n}\n"
  },
  {
    "path": "src/widgets/Omnibar.h",
    "content": "#ifndef OMNIBAR_H\n#define OMNIBAR_H\n\n#include <QLineEdit>\n\nclass MainWindow;\n\nclass Omnibar : public QLineEdit\n{\n    Q_OBJECT\npublic:\n    explicit Omnibar(MainWindow *main, QWidget *parent = nullptr);\n\n    void refresh(const QStringList &flagList);\n\nprivate slots:\n    void on_gotoEntry_returnPressed();\n\n    void restoreCompleter();\n\npublic slots:\n    void clear();\n\nprivate:\n    void setupCompleter();\n\n    MainWindow *main;\n    QStringList flags;\n};\n\n#endif // OMNIBAR_H\n"
  },
  {
    "path": "src/widgets/OverviewView.cpp",
    "content": "#include \"OverviewView.h\"\n#include <QPainter>\n#include <QMouseEvent>\n\n#include \"core/Cutter.h\"\n#include \"common/Colors.h\"\n#include \"common/Configuration.h\"\n#include \"common/TempConfig.h\"\n\nOverviewView::OverviewView(QWidget *parent) : GraphView(parent)\n{\n    connect(Config(), &Configuration::colorsUpdated, this, &OverviewView::colorsUpdatedSlot);\n    colorsUpdatedSlot();\n}\n\nvoid OverviewView::setData(int baseWidth, int baseHeight,\n                           std::unordered_map<ut64, GraphBlock> baseBlocks,\n                           DisassemblerGraphView::EdgeConfigurationMapping baseEdgeConfigurations)\n{\n    width = baseWidth;\n    height = baseHeight;\n    blocks = baseBlocks;\n    edgeConfigurations = baseEdgeConfigurations;\n    scaleAndCenter();\n    setCacheDirty();\n    viewport()->update();\n}\n\nvoid OverviewView::centreRect()\n{\n    qreal w = rangeRect.width();\n    qreal h = rangeRect.height();\n    initialDiff = QPointF(w / 2, h / 2);\n}\n\nOverviewView::~OverviewView() {}\n\nvoid OverviewView::scaleAndCenter()\n{\n    qreal wScale = (qreal)viewport()->width() / width;\n    qreal hScale = (qreal)viewport()->height() / height;\n    setViewScale(std::min(wScale, hScale));\n    center();\n}\n\nvoid OverviewView::refreshView()\n{\n    scaleAndCenter();\n    viewport()->update();\n}\n\nvoid OverviewView::drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive)\n{\n    Q_UNUSED(interactive)\n    QRectF blockRect(block.x, block.y, block.width, block.height);\n\n    p.setPen(Qt::black);\n    p.setBrush(Qt::gray);\n    p.drawRect(blockRect);\n    p.setBrush(QColor(0, 0, 0, 100));\n    p.drawRect(blockRect.translated(2, 2));\n\n    // Draw basic block highlighting/tracing\n    auto bb = Core()->getBBHighlighter()->getBasicBlock(block.entry);\n    if (bb) {\n        QColor color(bb->color);\n        color.setAlphaF(0.5);\n        p.setBrush(color);\n    } else {\n        p.setBrush(disassemblyBackgroundColor);\n    }\n    p.setPen(QPen(graphNodeColor, 1));\n    p.drawRect(blockRect);\n}\n\nvoid OverviewView::paintEvent(QPaintEvent *event)\n{\n    GraphView::paintEvent(event);\n    if (rangeRect.width() == 0 && rangeRect.height() == 0) {\n        return;\n    }\n    QPainter p(viewport());\n    p.setPen(graphSelectionBorder);\n    p.setBrush(graphSelectionFill);\n    p.drawRect(rangeRect);\n}\n\nvoid OverviewView::mousePressEvent(QMouseEvent *event)\n{\n    mouseActive = true;\n    auto pos = qhelpers::mouseEventPos(event);\n    if (rangeRect.contains(pos)) {\n        initialDiff = pos - rangeRect.topLeft();\n    } else {\n        QPointF size(rangeRect.width(), rangeRect.height());\n        initialDiff = size * 0.5;\n        rangeRect.moveCenter(pos);\n        viewport()->update();\n        emit mouseMoved();\n    }\n}\n\nvoid OverviewView::mouseReleaseEvent(QMouseEvent *event)\n{\n    mouseActive = false;\n    GraphView::mouseReleaseEvent(event);\n}\n\nvoid OverviewView::mouseMoveEvent(QMouseEvent *event)\n{\n    if (!mouseActive) {\n        return;\n    }\n    QPointF topLeft = qhelpers::mouseEventPos(event) - initialDiff;\n    rangeRect.setTopLeft(topLeft);\n    viewport()->update();\n    emit mouseMoved();\n}\n\nvoid OverviewView::wheelEvent(QWheelEvent *event)\n{\n    event->ignore();\n}\n\nGraphView::EdgeConfiguration OverviewView::edgeConfiguration(GraphView::GraphBlock &from,\n                                                             GraphView::GraphBlock *to,\n                                                             bool interactive)\n{\n    Q_UNUSED(interactive)\n    EdgeConfiguration ec;\n    auto baseEcIt = edgeConfigurations.find({ from.entry, to->entry });\n    if (baseEcIt != edgeConfigurations.end())\n        ec = baseEcIt->second;\n    ec.width_scale = 1.0 / getViewScale();\n    return ec;\n}\n\nvoid OverviewView::colorsUpdatedSlot()\n{\n    disassemblyBackgroundColor = ConfigColor(\"gui.overview.node\");\n    graphNodeColor = ConfigColor(\"gui.border\");\n    backgroundColor = ConfigColor(\"gui.background\");\n    graphSelectionFill = ConfigColor(\"gui.overview.fill\");\n    graphSelectionBorder = ConfigColor(\"gui.overview.border\");\n    setCacheDirty();\n    refreshView();\n}\n\nvoid OverviewView::setRangeRect(QRectF rect)\n{\n    rangeRect = rect;\n    viewport()->update();\n}\n"
  },
  {
    "path": "src/widgets/OverviewView.h",
    "content": "#ifndef OVERVIEWVIEW_H\n#define OVERVIEWVIEW_H\n\n#include <QWidget>\n#include <QPainter>\n#include <QRect>\n#include \"widgets/GraphView.h\"\n#include \"widgets/DisassemblerGraphView.h\"\n\nclass OverviewView : public GraphView\n{\n    Q_OBJECT\n\nsignals:\n    /**\n     * @brief signal when mouse is pressed or moved so that\n     * Graph can refresh its contents corresponded with Overview\n     */\n    void mouseMoved();\n\npublic:\n    OverviewView(QWidget *parent);\n    ~OverviewView() override;\n\n    /**\n     * @brief Graph access this function to set minimum set of the data\n     * @param baseWidth width of Graph when it computed the blocks\n     * @param baseHeigh height of Graph when it computed the blocks\n     * @param baseBlocks computed blocks passed by Graph\n     * @param baseEdgeConfigurations computed by DisassamblerGraphview\n     */\n    void setData(int baseWidth, int baseHeight, std::unordered_map<ut64, GraphBlock> baseBlocks,\n                 DisassemblerGraphView::EdgeConfigurationMapping baseEdgeConfigurations);\n\n    void centreRect();\n\n    /**\n     * @brief keep the current addr of the fcn of Graph\n     * Everytime overview updates its contents, it compares this value with the one in Graph\n     * if they aren't same, then Overview needs to update the pixmap cache.\n     */\n    ut64 currentFcnAddr = RVA_INVALID; // TODO: make this less public\npublic slots:\n    /**\n     * @brief scale and center all nodes in, then run update\n     */\n    void refreshView();\n\nprivate slots:\n    /**\n     * @brief update Colors.\n     * for example this will be called when the theme is changed.\n     */\n    void colorsUpdatedSlot();\n\nprotected:\n    /**\n     * @brief mousePressEvent to start moving the rect.\n     */\n    void mousePressEvent(QMouseEvent *event) override;\n    /**\n     * @brief mouseReleaseEvent to tell not to move the rect anymore.\n     */\n    void mouseReleaseEvent(QMouseEvent *event) override;\n    /**\n     * @brief mouseMoveEvent to move the rect.\n     */\n    void mouseMoveEvent(QMouseEvent *event) override;\n    /**\n     * @brief override this to prevent scrolling\n     */\n    void wheelEvent(QWheelEvent *event) override;\n\n    /**\n     * @brief override the paintEvent to draw the rect on Overview\n     */\n    void paintEvent(QPaintEvent *event) override;\n\nprivate:\n    /**\n     * @brief this will be handled in mouse events to move the rect properly\n     * along with the mouse.\n     */\n    bool mouseActive = false;\n\n    /**\n     * @brief save the initial distance\n     * between the point where the mouse was pressed and the point of the rect\n     * so as to change the rect point properly along with mouse.\n     */\n    QPointF initialDiff;\n\n    /**\n     * @brief a rect on Overview to show where you are on Graph\n     */\n    QRectF rangeRect;\n\n    /**\n     * @brief calculate the scale to fit the all nodes in and center them in the viewport\n     */\n    void scaleAndCenter();\n\n    /**\n     * @brief draw the computed blocks passed by Graph\n     */\n    virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override;\n\n    /**\n     * @brief override the edgeConfiguration so as to\n     * adjust the width of the edges by the scale\n     * @return EdgeConfiguration\n     */\n    virtual GraphView::EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from,\n                                                           GraphView::GraphBlock *to,\n                                                           bool interactive) override;\n\n    /**\n     * @brief base background color changing depending on the theme\n     */\n    QColor disassemblyBackgroundColor;\n\n    /**\n     * @brief color for each node changing depending on the theme\n     */\n    QColor graphNodeColor;\n\n    /**\n     * @brief fill color of the selection rectangle\n     */\n    QColor graphSelectionFill;\n\n    /**\n     * @brief border color of the selection rectangle\n     */\n    QColor graphSelectionBorder;\n\n    /**\n     * @brief edgeConfigurations edge styles computed by DisassemblerGraphView\n     */\n    DisassemblerGraphView::EdgeConfigurationMapping edgeConfigurations;\n\npublic:\n    QRectF getRangeRect() { return rangeRect; }\n    void setRangeRect(QRectF rect);\n};\n\n#endif // OVERVIEWVIEW_H\n"
  },
  {
    "path": "src/widgets/OverviewWidget.cpp",
    "content": "#include \"core/MainWindow.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include \"OverviewWidget.h\"\n#include \"GraphWidget.h\"\n#include \"OverviewView.h\"\n\nOverviewWidget::OverviewWidget(MainWindow *main) : CutterDockWidget(main)\n{\n    setWindowTitle(tr(\"Graph Overview\"));\n    setObjectName(\"Graph Overview\");\n    setAllowedAreas(Qt::AllDockWidgetAreas);\n    graphView = new OverviewView(this);\n    setWidget(graphView);\n    targetGraphWidget = nullptr;\n\n    connect(graphView, &OverviewView::mouseMoved, this, &OverviewWidget::updateTargetView);\n\n    graphDataRefreshDeferrer = createRefreshDeferrer([this]() { updateGraphData(); });\n\n    // Zoom shortcuts\n    QShortcut *shortcut_zoom_in = Shortcuts()->makeQShortcut(\"Overview.zoomIn\", this);\n    shortcut_zoom_in->setContext(Qt::WidgetWithChildrenShortcut);\n    connect(shortcut_zoom_in, &QShortcut::activated, this, [this]() { zoomTarget(1); });\n    QShortcut *shortcut_zoom_out = Shortcuts()->makeQShortcut(\"Overview.zoomOut\", this);\n    shortcut_zoom_out->setContext(Qt::WidgetWithChildrenShortcut);\n    connect(shortcut_zoom_out, &QShortcut::activated, this, [this]() { zoomTarget(-1); });\n}\n\nOverviewWidget::~OverviewWidget() {}\n\nvoid OverviewWidget::resizeEvent(QResizeEvent *event)\n{\n    graphView->refreshView();\n    updateRangeRect();\n    QDockWidget::resizeEvent(event);\n    emit resized();\n}\n\nvoid OverviewWidget::showEvent(QShowEvent *event)\n{\n    CutterDockWidget::showEvent(event);\n    setUserOpened(true);\n}\n\nvoid OverviewWidget::closeEvent(QCloseEvent *event)\n{\n    CutterDockWidget::closeEvent(event);\n    setUserOpened(false);\n}\n\nvoid OverviewWidget::setIsAvailable(bool isAvailable)\n{\n    if (this->isAvailable == isAvailable) {\n        return;\n    }\n    this->isAvailable = isAvailable;\n    if (!isAvailable) {\n        hide();\n    } else if (userOpened) {\n        show();\n    }\n    emit isAvailableChanged(isAvailable);\n}\n\nvoid OverviewWidget::setUserOpened(bool userOpened)\n{\n    if (this->userOpened == userOpened) {\n        return;\n    }\n    this->userOpened = userOpened;\n    emit userOpenedChanged(userOpened);\n}\n\nvoid OverviewWidget::zoomTarget(int d)\n{\n    if (!targetGraphWidget) {\n        return;\n    }\n    targetGraphWidget->getGraphView()->zoom(QPointF(0.5, 0.5), d);\n}\n\nvoid OverviewWidget::setTargetGraphWidget(GraphWidget *widget)\n{\n    if (widget == targetGraphWidget) {\n        return;\n    }\n    if (targetGraphWidget) {\n        disconnect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::viewRefreshed, this,\n                   &OverviewWidget::updateGraphData);\n        disconnect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::resized, this,\n                   &OverviewWidget::updateRangeRect);\n        disconnect(targetGraphWidget->getGraphView(), &GraphView::viewOffsetChanged, this,\n                   &OverviewWidget::updateRangeRect);\n        disconnect(targetGraphWidget->getGraphView(), &GraphView::viewScaleChanged, this,\n                   &OverviewWidget::updateRangeRect);\n        disconnect(targetGraphWidget, &GraphWidget::graphClosed, this,\n                   &OverviewWidget::targetClosed);\n    }\n    targetGraphWidget = widget;\n    if (targetGraphWidget) {\n        connect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::viewRefreshed, this,\n                &OverviewWidget::updateGraphData);\n        connect(targetGraphWidget->getGraphView(), &DisassemblerGraphView::resized, this,\n                &OverviewWidget::updateRangeRect);\n        connect(targetGraphWidget->getGraphView(), &GraphView::viewOffsetChanged, this,\n                &OverviewWidget::updateRangeRect);\n        connect(targetGraphWidget->getGraphView(), &GraphView::viewScaleChanged, this,\n                &OverviewWidget::updateRangeRect);\n        connect(targetGraphWidget, &GraphWidget::graphClosed, this, &OverviewWidget::targetClosed);\n    }\n    updateGraphData();\n    updateRangeRect();\n    setIsAvailable(targetGraphWidget != nullptr);\n}\n\nvoid OverviewWidget::wheelEvent(QWheelEvent *event)\n{\n    zoomTarget(event->angleDelta().y() / 90);\n    graphView->centreRect();\n}\n\nvoid OverviewWidget::targetClosed()\n{\n    setTargetGraphWidget(nullptr);\n}\n\nvoid OverviewWidget::updateTargetView()\n{\n    if (!targetGraphWidget) {\n        return;\n    }\n    qreal curScale = graphView->getViewScale();\n    int rectx = graphView->getRangeRect().x();\n    int recty = graphView->getRangeRect().y();\n    int overview_offset_x = graphView->getViewOffset().x();\n    int overview_offset_y = graphView->getViewOffset().y();\n    QPoint newOffset;\n    newOffset.rx() = rectx / curScale + overview_offset_x;\n    newOffset.ry() = recty / curScale + overview_offset_y;\n    targetGraphWidget->getGraphView()->setViewOffset(newOffset);\n    targetGraphWidget->getGraphView()->viewport()->update();\n}\n\nvoid OverviewWidget::updateGraphData()\n{\n    if (!graphDataRefreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n    if (targetGraphWidget && !targetGraphWidget->getGraphView()->isGraphEmpty()) {\n        graphView->currentFcnAddr = targetGraphWidget->getGraphView()->currentFcnAddr;\n        auto &mainGraphView = *targetGraphWidget->getGraphView();\n        graphView->setData(mainGraphView.getWidth(), mainGraphView.getHeight(),\n                           mainGraphView.getBlocks(), mainGraphView.getEdgeConfigurations());\n    } else {\n        graphView->currentFcnAddr = RVA_INVALID;\n        graphView->setData(0, 0, {}, {});\n        graphView->setRangeRect(QRectF(0, 0, 0, 0));\n    }\n}\n\nvoid OverviewWidget::updateRangeRect()\n{\n    if (targetGraphWidget) {\n        qreal curScale = graphView->getViewScale();\n        qreal baseScale = targetGraphWidget->getGraphView()->getViewScale();\n        qreal w = targetGraphWidget->getGraphView()->viewport()->width() * curScale / baseScale;\n        qreal h = targetGraphWidget->getGraphView()->viewport()->height() * curScale / baseScale;\n        int graph_offset_x = targetGraphWidget->getGraphView()->getViewOffset().x();\n        int graph_offset_y = targetGraphWidget->getGraphView()->getViewOffset().y();\n        int overview_offset_x = graphView->getViewOffset().x();\n        int overview_offset_y = graphView->getViewOffset().y();\n        int rangeRectX = graph_offset_x * curScale - overview_offset_x * curScale;\n        int rangeRectY = graph_offset_y * curScale - overview_offset_y * curScale;\n        graphView->setRangeRect(QRectF(rangeRectX, rangeRectY, w, h));\n    } else {\n        graphView->setRangeRect(QRectF(0, 0, 0, 0));\n    }\n}\n"
  },
  {
    "path": "src/widgets/OverviewWidget.h",
    "content": "#ifndef OVERVIEWWIDGET_H\n#define OVERVIEWWIDGET_H\n\n#include \"CutterDockWidget.h\"\n\nclass MainWindow;\nclass OverviewView;\nclass GraphWidget;\n\nclass OverviewWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit OverviewWidget(MainWindow *main);\n    ~OverviewWidget();\n\nprivate:\n    OverviewView *graphView;\n    bool isAvailable = false;\n    bool userOpened = false;\n\n    GraphWidget *targetGraphWidget;\n\n    RefreshDeferrer *graphDataRefreshDeferrer;\n\n    /**\n     * @brief this takes care of scaling the overview when the widget is resized\n     */\n    void resizeEvent(QResizeEvent *event) override;\n\n    void setIsAvailable(bool isAvailable);\n    void setUserOpened(bool userOpened);\n    void zoomTarget(int d);\n\nprivate slots:\n    void showEvent(QShowEvent *event) override;\n    void closeEvent(QCloseEvent *event) override;\n\n    /**\n     * @brief update the view in the target widget when the range rect in the overview is moved\n     */\n    void updateTargetView();\n\n    /**\n     * @brief update the content of the graph (blocks, edges) in the contained graphView from the\n     * target widget\n     */\n    void updateGraphData();\n\n    /**\n     * @brief update the rect to show the current view in the target widget\n     */\n    void updateRangeRect();\n\n    void targetClosed();\n\nsignals:\n    /**\n     * @brief emit signal to update the rect\n     */\n    void resized();\n\n    /**\n     * @sa getIsAvailable()\n     */\n    void isAvailableChanged(bool isAvailable);\n\n    /**\n     * @sa getUserOpened()\n     */\n    void userOpenedChanged(bool userOpened);\n\npublic:\n    GraphWidget *getTargetGraphWidget() { return targetGraphWidget; }\n    void setTargetGraphWidget(GraphWidget *widget);\n\n    /**\n     * @brief whether this widget makes sense to be show, i.e. the menu entry should be enabled\n     */\n    bool getIsAvailable() const { return isAvailable; }\n\n    /**\n     * @brief whether this widget is desired to be shown in general\n     *\n     * Will be false when the user closed the overview explicitly.\n     * Also corresponds to the checked state of the menu entry for this widget.\n     */\n    bool getUserOpened() const { return userOpened; }\n\n    OverviewView *getGraphView() const { return graphView; }\n    void wheelEvent(QWheelEvent *event) override;\n};\n\n#endif // OverviewWIDGET_H\n"
  },
  {
    "path": "src/widgets/ProcessesWidget.cpp",
    "content": "#include <QShortcut>\n#include \"ProcessesWidget.h\"\n#include \"ui_ProcessesWidget.h\"\n#include \"common/JsonModel.h\"\n#include \"QuickFilterView.h\"\n#include <rz_debug.h>\n\n#include \"core/MainWindow.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#define DEBUGGED_PID (-1)\n\nProcessesWidget::ProcessesWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::ProcessesWidget)\n{\n    ui->setupUi(this);\n\n    // Setup processes model\n    modelProcesses = new QStandardItemModel(1, 4, this);\n    modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_PID,\n                                            new QStandardItem(tr(\"PID\")));\n    modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_UID,\n                                            new QStandardItem(tr(\"UID\")));\n    modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_STATUS,\n                                            new QStandardItem(tr(\"Status\")));\n    modelProcesses->setHorizontalHeaderItem(ProcessesWidget::COLUMN_PATH,\n                                            new QStandardItem(tr(\"Path\")));\n    ui->viewProcesses->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);\n    ui->viewProcesses->verticalHeader()->setVisible(false);\n    ui->viewProcesses->setFont(Config()->getFont());\n\n    modelFilter = new ProcessesFilterModel(this);\n    modelFilter->setSourceModel(modelProcesses);\n    ui->viewProcesses->setModel(modelFilter);\n\n    // CTRL+F switches to the filter view and opens it in case it's hidden\n    QShortcut *searchShortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,\n            &QuickFilterView::showFilter);\n    searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    // ESC switches back to the processes table and clears the buffer\n    QShortcut *clearShortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n    connect(clearShortcut, &QShortcut::activated, this, [this]() {\n        ui->quickFilterView->clearFilter();\n        ui->viewProcesses->setFocus();\n    });\n    clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { updateContents(); });\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, modelFilter,\n            &ProcessesFilterModel::setFilterWildcard);\n    connect(Core(), &CutterCore::refreshAll, this, &ProcessesWidget::updateContents);\n    connect(Core(), &CutterCore::registersChanged, this, &ProcessesWidget::updateContents);\n    connect(Core(), &CutterCore::debugTaskStateChanged, this, &ProcessesWidget::updateContents);\n    // Seek doesn't necessarily change when switching processes\n    connect(Core(), &CutterCore::switchedProcess, this, &ProcessesWidget::updateContents);\n    connect(Config(), &Configuration::fontsUpdated, this, &ProcessesWidget::fontsUpdatedSlot);\n    connect(ui->viewProcesses, &QTableView::activated, this, &ProcessesWidget::onActivated);\n}\n\nProcessesWidget::~ProcessesWidget() {}\n\nvoid ProcessesWidget::updateContents()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    if (!Core()->currentlyDebugging) {\n        // Remove rows from the previous debugging session\n        modelProcesses->removeRows(0, modelProcesses->rowCount());\n        return;\n    }\n\n    if (Core()->isDebugTaskInProgress()) {\n        ui->viewProcesses->setDisabled(true);\n    } else {\n        setProcessesGrid();\n        ui->viewProcesses->setDisabled(false);\n    }\n}\n\nQString ProcessesWidget::translateStatus(const char status)\n{\n    switch (status) {\n    case RZ_DBG_PROC_STOP:\n        return \"Stopped\";\n    case RZ_DBG_PROC_RUN:\n        return \"Running\";\n    case RZ_DBG_PROC_SLEEP:\n        return \"Sleeping\";\n    case RZ_DBG_PROC_ZOMBIE:\n        return \"Zombie\";\n    case RZ_DBG_PROC_DEAD:\n        return \"Dead\";\n    case RZ_DBG_PROC_RAISED:\n        return \"Raised event\";\n    default:\n        return \"Unknown status\";\n    }\n}\n\nvoid ProcessesWidget::setProcessesGrid()\n{\n    int i = 0;\n    QFont font;\n\n    for (const auto &processesItem : Core()->getProcesses(DEBUGGED_PID)) {\n        st64 pid = processesItem.pid;\n        st64 uid = processesItem.uid;\n        QString status = translateStatus(processesItem.status);\n        QString path = processesItem.path;\n        bool current = processesItem.current;\n\n        // Use bold font to highlight active thread\n        font.setBold(current);\n\n        QStandardItem *rowPid = new QStandardItem(QString::number(pid));\n        QStandardItem *rowUid = new QStandardItem(QString::number(uid));\n        QStandardItem *rowStatus = new QStandardItem(status);\n        QStandardItem *rowPath = new QStandardItem(path);\n\n        rowPid->setFont(font);\n        rowUid->setFont(font);\n        rowStatus->setFont(font);\n        rowPath->setFont(font);\n\n        modelProcesses->setItem(i, ProcessesWidget::COLUMN_PID, rowPid);\n        modelProcesses->setItem(i, ProcessesWidget::COLUMN_UID, rowUid);\n        modelProcesses->setItem(i, ProcessesWidget::COLUMN_STATUS, rowStatus);\n        modelProcesses->setItem(i, ProcessesWidget::COLUMN_PATH, rowPath);\n        i++;\n    }\n\n    // Remove irrelevant old rows\n    if (modelProcesses->rowCount() > i) {\n        modelProcesses->removeRows(i, modelProcesses->rowCount() - i);\n    }\n\n    modelFilter->setSourceModel(modelProcesses);\n    ui->viewProcesses->resizeColumnsToContents();\n}\n\nvoid ProcessesWidget::fontsUpdatedSlot()\n{\n    ui->viewProcesses->setFont(Config()->getFont());\n}\n\nvoid ProcessesWidget::onActivated(const QModelIndex &index)\n{\n    if (!index.isValid())\n        return;\n\n    int pid = modelFilter->data(index.sibling(index.row(), ProcessesWidget::COLUMN_PID)).toInt();\n    // Verify that the selected pid is still in the processes list since dp= will\n    // attach to any given id. If it isn't found simply update the UI.\n    for (const auto &value : Core()->getProcesses(DEBUGGED_PID)) {\n        if (pid == value.pid) {\n            QMessageBox msgBox(this);\n            switch (value.status) {\n            case RZ_DBG_PROC_ZOMBIE:\n            case RZ_DBG_PROC_DEAD:\n                msgBox.setText(tr(\"Unable to switch to the requested process.\"));\n                msgBox.exec();\n                break;\n            default:\n                Core()->setCurrentDebugProcess(pid);\n                break;\n            }\n        }\n    }\n    updateContents();\n}\n\nProcessesFilterModel::ProcessesFilterModel(QObject *parent) : QSortFilterProxyModel(parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool ProcessesFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    // All columns are checked for a match\n    for (int i = ProcessesWidget::COLUMN_PID; i <= ProcessesWidget::COLUMN_PATH; ++i) {\n        QModelIndex index = sourceModel()->index(row, i, parent);\n        if (qhelpers::filterStringContains(sourceModel()->data(index).toString(), this)) {\n            return true;\n        }\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "src/widgets/ProcessesWidget.h",
    "content": "#pragma once\n\n#include <QJsonObject>\n#include <memory>\n#include <QStandardItem>\n#include <QTableView>\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n\nclass MainWindow;\n\nnamespace Ui {\nclass ProcessesWidget;\n}\n\nclass ProcessesFilterModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    ProcessesFilterModel(QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n};\n\nclass ProcessesWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    enum ColumnIndex {\n        COLUMN_PID = 0,\n        COLUMN_UID,\n        COLUMN_STATUS,\n        COLUMN_PATH,\n    };\n\n    explicit ProcessesWidget(MainWindow *main);\n    ~ProcessesWidget();\n\nprivate slots:\n    void updateContents();\n    void setProcessesGrid();\n    void fontsUpdatedSlot();\n    void onActivated(const QModelIndex &index);\n\nprivate:\n    QString translateStatus(const char status);\n    std::unique_ptr<Ui::ProcessesWidget> ui;\n    QStandardItemModel *modelProcesses;\n    ProcessesFilterModel *modelFilter;\n    RefreshDeferrer *refreshDeferrer;\n};\n"
  },
  {
    "path": "src/widgets/ProcessesWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ProcessesWidget</class>\n <widget class=\"QDockWidget\" name=\"ProcessesWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>463</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Processes</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n      <property name=\"spacing\">\n       <number>10</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QTableView\" name=\"viewProcesses\">\n        <property name=\"sortingEnabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"cornerButtonEnabled\">\n         <bool>false</bool>\n        </property>\n        <property name=\"selectionMode\">\n         <enum>QAbstractItemView::SingleSelection</enum>\n        </property>\n        <property name=\"editTriggers\">\n         <enum>QAbstractItemView::NoEditTriggers</enum>\n        </property>\n        <property name=\"horizontalScrollMode\">\n         <enum>QAbstractItemView::ScrollPerPixel</enum>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QuickFilterView\" name=\"quickFilterView\" native=\"true\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n       </widget>\n      </item>\n     </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>QuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/QuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/QuickFilterView.cpp",
    "content": "\n#include \"QuickFilterView.h\"\n#include \"ui_QuickFilterView.h\"\n\nQuickFilterView::QuickFilterView(QWidget *parent, bool defaultOn)\n    : QWidget(parent), ui(new Ui::QuickFilterView())\n{\n    ui->setupUi(this);\n\n    debounceTimer = new QTimer(this);\n    debounceTimer->setSingleShot(true);\n\n    connect(ui->closeFilterButton, &QAbstractButton::clicked, this, &QuickFilterView::closeFilter);\n\n    connect(debounceTimer, &QTimer::timeout, this,\n            [this]() { emit filterTextChanged(ui->filterLineEdit->text()); });\n\n    connect(ui->filterLineEdit, &QLineEdit::textChanged, this,\n            [this]() { debounceTimer->start(150); });\n\n    if (!defaultOn) {\n        closeFilter();\n    }\n}\n\nQuickFilterView::~QuickFilterView() {}\n\nvoid QuickFilterView::showFilter()\n{\n    show();\n    ui->filterLineEdit->setFocus();\n}\n\nvoid QuickFilterView::clearFilter()\n{\n    if (ui->filterLineEdit->text().isEmpty()) {\n        closeFilter();\n    } else {\n        ui->filterLineEdit->setText(\"\");\n    }\n}\n\nvoid QuickFilterView::closeFilter()\n{\n    ui->filterLineEdit->setText(\"\");\n    hide();\n    emit filterClosed();\n}\n"
  },
  {
    "path": "src/widgets/QuickFilterView.h",
    "content": "\n#ifndef QUICKFILTERVIEW_H\n#define QUICKFILTERVIEW_H\n\n#include \"core/CutterCommon.h\"\n\n#include <memory>\n\n#include <QWidget>\n#include <QTimer>\n\nnamespace Ui {\nclass QuickFilterView;\n}\n\nclass CUTTER_EXPORT QuickFilterView : public QWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit QuickFilterView(QWidget *parent = nullptr, bool defaultOn = true);\n    ~QuickFilterView();\n\npublic slots:\n    void showFilter();\n    void closeFilter();\n    void clearFilter();\n\nsignals:\n    void filterTextChanged(const QString &text);\n    void filterClosed();\n\nprivate:\n    std::unique_ptr<Ui::QuickFilterView> ui;\n    QTimer *debounceTimer;\n};\n\n#endif // QUICKFILTERVIEW_H\n"
  },
  {
    "path": "src/widgets/QuickFilterView.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>QuickFilterView</class>\n <widget class=\"QWidget\" name=\"QuickFilterView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>327</width>\n    <height>25</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <property name=\"spacing\">\n    <number>0</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QLineEdit\" name=\"filterLineEdit\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"placeholderText\">\n      <string>Quick Filter</string>\n     </property>\n     <property name=\"clearButtonEnabled\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QToolButton\" name=\"closeFilterButton\">\n     <property name=\"font\">\n      <font>\n       <weight>75</weight>\n       <italic>false</italic>\n       <bold>true</bold>\n      </font>\n     </property>\n     <property name=\"styleSheet\">\n      <string notr=\"true\"></string>\n     </property>\n     <property name=\"text\">\n      <string>X</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/RegisterRefsWidget.cpp",
    "content": "#include \"RegisterRefsWidget.h\"\n#include \"ui_RegisterRefsWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QJsonObject>\n#include <QMenu>\n#include <QClipboard>\n#include <QShortcut>\n\nRegisterRefModel::RegisterRefModel(QObject *parent) : QAbstractListModel(parent) {}\n\nint RegisterRefModel::rowCount(const QModelIndex &) const\n{\n    return registerRefs.count();\n}\n\nint RegisterRefModel::columnCount(const QModelIndex &) const\n{\n    return RegisterRefModel::ColumnCount;\n}\n\nQVariant RegisterRefModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= registerRefs.count())\n        return QVariant();\n\n    const RegisterRefDescription &registerRef = registerRefs.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case RegColumn:\n            return registerRef.reg;\n        case ValueColumn:\n            return registerRef.value;\n        case RefColumn:\n            return registerRef.refDesc.ref;\n        case CommentColumn:\n            return Core()->getCommentAt(Core()->math(registerRef.value));\n        default:\n            return QVariant();\n        }\n    case Qt::ForegroundRole:\n        switch (index.column()) {\n        case RefColumn:\n            return registerRef.refDesc.refColor;\n        default:\n            return QVariant();\n        }\n    case RegisterRefDescriptionRole:\n        return QVariant::fromValue(registerRef);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant RegisterRefModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case RegColumn:\n            return tr(\"Register\");\n        case ValueColumn:\n            return tr(\"Value\");\n        case RefColumn:\n            return tr(\"Reference\");\n        case CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRegisterRefProxyModel::RegisterRefProxyModel(RegisterRefModel *sourceModel, QObject *parent)\n    : QSortFilterProxyModel(parent)\n{\n    setSourceModel(sourceModel);\n}\n\nbool RegisterRefProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    RegisterRefDescription item = index.data(RegisterRefModel::RegisterRefDescriptionRole)\n                                          .value<RegisterRefDescription>();\n    return qhelpers::filterStringContains(item.reg, this);\n}\n\nbool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    RegisterRefDescription leftRegRef =\n            left.data(RegisterRefModel::RegisterRefDescriptionRole).value<RegisterRefDescription>();\n    RegisterRefDescription rightRegRef = right.data(RegisterRefModel::RegisterRefDescriptionRole)\n                                                 .value<RegisterRefDescription>();\n\n    switch (left.column()) {\n    case RegisterRefModel::RegColumn:\n        return leftRegRef.reg < rightRegRef.reg;\n    case RegisterRefModel::RefColumn:\n        return leftRegRef.refDesc.ref < rightRegRef.refDesc.ref;\n    case RegisterRefModel::ValueColumn:\n        return leftRegRef.value < rightRegRef.value;\n    case RegisterRefModel::CommentColumn:\n        return Core()->getCommentAt(Core()->math(leftRegRef.value))\n                < Core()->getCommentAt(Core()->math(rightRegRef.value));\n    default:\n        break;\n    }\n\n    return leftRegRef.reg < rightRegRef.reg;\n}\n\nRegisterRefsWidget::RegisterRefsWidget(MainWindow *main)\n    : CutterDockWidget(main),\n      ui(new Ui::RegisterRefsWidget),\n      tree(new CutterTreeWidget(this)),\n      addressableItemContextMenu(this, main)\n{\n    ui->setupUi(this);\n\n    // Add Status Bar footer\n    tree->addStatusBar(ui->verticalLayout);\n\n    registerRefModel = new RegisterRefModel(this);\n    registerRefProxyModel = new RegisterRefProxyModel(registerRefModel, this);\n    ui->registerRefTreeView->setModel(registerRefProxyModel);\n    ui->registerRefTreeView->setAutoScroll(false);\n    ui->registerRefTreeView->sortByColumn(RegisterRefModel::RegColumn, Qt::AscendingOrder);\n\n    actionCopyValue = new QAction(tr(\"Copy register value\"), this);\n    actionCopyRef = new QAction(tr(\"Copy register reference\"), this);\n\n    addressableItemContextMenu.addAction(actionCopyValue);\n    addressableItemContextMenu.addAction(actionCopyRef);\n    addActions(addressableItemContextMenu.actions());\n\n    connect(ui->registerRefTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this,\n            &RegisterRefsWidget::onCurrentChanged);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { refreshRegisterRef(); });\n\n    // Ctrl-F to show/hide the filter entry\n    QShortcut *search_shortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(search_shortcut, &QShortcut::activated, ui->quickFilterView,\n            &QuickFilterView::showFilter);\n    search_shortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, registerRefProxyModel,\n            &QSortFilterProxyModel::setFilterWildcard);\n    connect(ui->quickFilterView, &QuickFilterView::filterClosed, ui->registerRefTreeView,\n            [this]() { ui->registerRefTreeView->setFocus(); });\n    setScrollMode();\n    connect(Core(), &CutterCore::refreshAll, this, &RegisterRefsWidget::refreshRegisterRef);\n    connect(Core(), &CutterCore::registersChanged, this, &RegisterRefsWidget::refreshRegisterRef);\n    connect(Core(), &CutterCore::commentsChanged, this, [this]() {\n        qhelpers::emitColumnChanged(registerRefModel, RegisterRefModel::CommentColumn);\n    });\n    connect(actionCopyValue, &QAction::triggered, this,\n            [this]() { copyClip(RegisterRefModel::ValueColumn); });\n    connect(actionCopyRef, &QAction::triggered, this,\n            [this]() { copyClip(RegisterRefModel::RefColumn); });\n    ui->registerRefTreeView->setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(ui->registerRefTreeView, &QMenu::customContextMenuRequested, this,\n            &RegisterRefsWidget::customMenuRequested);\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this,\n            [this] { tree->showItemsNumber(registerRefProxyModel->rowCount()); });\n}\n\nRegisterRefsWidget::~RegisterRefsWidget() = default;\n\nvoid RegisterRefsWidget::refreshRegisterRef()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    registerRefModel->beginResetModel();\n\n    registerRefModel->registerRefs.clear();\n    for (const RegisterRef &reg : Core()->getRegisterRefs()) {\n        RegisterRefDescription desc;\n\n        desc.value = RzAddressString(reg.value);\n        desc.reg = reg.name;\n        desc.refDesc = Core()->formatRefDesc(QSharedPointer<AddrRefs>::create(reg.ref));\n\n        registerRefModel->registerRefs.push_back(desc);\n    }\n\n    registerRefModel->endResetModel();\n\n    ui->registerRefTreeView->resizeColumnToContents(0);\n    ui->registerRefTreeView->resizeColumnToContents(1);\n    ui->registerRefTreeView->resizeColumnToContents(2);\n\n    tree->showItemsNumber(registerRefProxyModel->rowCount());\n}\n\nvoid RegisterRefsWidget::setScrollMode()\n{\n    qhelpers::setVerticalScrollMode(ui->registerRefTreeView);\n}\n\nvoid RegisterRefsWidget::on_registerRefTreeView_doubleClicked(const QModelIndex &index)\n{\n    RegisterRefDescription item = index.data(RegisterRefModel::RegisterRefDescriptionRole)\n                                          .value<RegisterRefDescription>();\n    Core()->seekAndShow(item.value);\n}\n\nvoid RegisterRefsWidget::customMenuRequested(QPoint pos)\n{\n    addressableItemContextMenu.exec(ui->registerRefTreeView->viewport()->mapToGlobal(pos));\n}\n\nvoid RegisterRefsWidget::onCurrentChanged(const QModelIndex &current, const QModelIndex &previous)\n{\n    Q_UNUSED(current)\n    Q_UNUSED(previous)\n    auto currentIndex = ui->registerRefTreeView->selectionModel()->currentIndex();\n\n    // Use the value column as the offset\n    QString offsetString;\n    if (currentIndex.column() != RegisterRefModel::RefColumn) {\n        offsetString = currentIndex.data().toString();\n    } else {\n        offsetString = currentIndex.sibling(currentIndex.row(), RegisterRefModel::ValueColumn)\n                               .data()\n                               .toString();\n    }\n\n    RVA offset = Core()->math(offsetString);\n    addressableItemContextMenu.setTarget(offset);\n}\n\nvoid RegisterRefsWidget::copyClip(int column)\n{\n    int row = ui->registerRefTreeView->selectionModel()->currentIndex().row();\n    QString value = ui->registerRefTreeView->selectionModel()\n                            ->currentIndex()\n                            .sibling(row, column)\n                            .data()\n                            .toString();\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(value);\n}\n"
  },
  {
    "path": "src/widgets/RegisterRefsWidget.h",
    "content": "#pragma once\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeWidget.h\"\n#include \"menus/AddressableItemContextMenu.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidget;\nclass RegisterRefsWidget;\n\nnamespace Ui {\nclass RegisterRefsWidget;\n}\n\nclass MainWindow;\nclass QTreeWidgetItem;\n\nstruct RegisterRefDescription\n{\n    QString reg;\n    QString value;\n    RefDescription refDesc;\n};\nQ_DECLARE_METATYPE(RegisterRefDescription)\n\nclass RegisterRefModel : public QAbstractListModel\n{\n    Q_OBJECT\n\n    friend RegisterRefsWidget;\n\nprivate:\n    QList<RegisterRefDescription> registerRefs;\n\npublic:\n    enum Column { RegColumn = 0, ValueColumn, RefColumn, CommentColumn, ColumnCount };\n    enum Role { RegisterRefDescriptionRole = Qt::UserRole };\n\n    RegisterRefModel(QObject *parent = 0);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const;\n\n    QVariant data(const QModelIndex &index, int role) const;\n    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\nclass RegisterRefProxyModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    RegisterRefProxyModel(RegisterRefModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass RegisterRefsWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit RegisterRefsWidget(MainWindow *main);\n    ~RegisterRefsWidget();\n\nprivate slots:\n    void on_registerRefTreeView_doubleClicked(const QModelIndex &index);\n    void refreshRegisterRef();\n    void copyClip(int column);\n    void customMenuRequested(QPoint pos);\n    void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);\n\nprivate:\n    std::unique_ptr<Ui::RegisterRefsWidget> ui;\n\n    RegisterRefModel *registerRefModel;\n    RegisterRefProxyModel *registerRefProxyModel;\n    CutterTreeWidget *tree;\n    void setScrollMode();\n\n    RefreshDeferrer *refreshDeferrer;\n\n    QAction *actionCopyValue;\n    QAction *actionCopyRef;\n    AddressableItemContextMenu addressableItemContextMenu;\n};\n"
  },
  {
    "path": "src/widgets/RegisterRefsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RegisterRefsWidget</class>\n <widget class=\"QDockWidget\" name=\"RegisterRefsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Register References</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"CutterTreeView\" name=\"registerRefTreeView\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"QuickFilterView\" name=\"quickFilterView\" native=\"true\"/>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>CutterTreeView</class>\n   <extends>QTreeView</extends>\n   <header>widgets/CutterTreeView.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>QuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/QuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/RegistersWidget.cpp",
    "content": "#include \"RegistersWidget.h\"\n#include \"ui_RegistersWidget.h\"\n#include \"common/JsonModel.h\"\n#include \"dialogs/RegisterProfileDialog.h\"\n\n#include \"core/MainWindow.h\"\n\n#include <QCollator>\n#include <QLabel>\n#include <QLineEdit>\n\nRegistersWidget::RegistersWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::RegistersWidget), addressContextMenu(this, main)\n{\n    ui->setupUi(this);\n\n    // setup register layout\n    registerLayout->setVerticalSpacing(0);\n    registerLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop);\n    ui->verticalLayout->addLayout(registerLayout);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { updateContents(); });\n\n    connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents);\n    connect(Core(), &CutterCore::registersChanged, this, &RegistersWidget::updateContents);\n    connect(ui->configureProfileBtn, &QPushButton::clicked, this,\n            &RegistersWidget::configureRegProfileClicked);\n\n    // Hide shortcuts because there is no way of selecting an item and triger them\n    for (auto &action : addressContextMenu.actions()) {\n        action->setShortcut(QKeySequence());\n        // setShortcutVisibleInContextMenu(false) doesn't work\n    }\n}\n\nRegistersWidget::~RegistersWidget() = default;\n\nvoid RegistersWidget::updateContents()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n    setRegisterGrid();\n}\n\nvoid RegistersWidget::setRegisterGrid()\n{\n    int i = 0;\n    int col = 0;\n    QString regValue;\n    QLabel *registerLabel;\n    QLineEdit *registerEditValue;\n    const auto registerRefs = Core()->getRegisterRefValues();\n\n    registerLen = registerRefs.size();\n    for (auto &reg : registerRefs) {\n        regValue = reg.value;\n        // check if we already filled this grid space with label/value\n        if (!registerLayout->itemAtPosition(i, col)) {\n            registerLabel = new QLabel;\n            registerLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);\n            registerLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);\n            registerLabel->setMaximumWidth(60);\n            registerLabel->setStyleSheet(\"font-weight: bold; font-family: mono;\");\n            registerEditValue = new QLineEdit;\n            registerEditValue->setMaximumWidth(140);\n            registerEditValue->setFont(Config()->getFont());\n            registerLabel->setContextMenuPolicy(Qt::CustomContextMenu);\n            connect(registerLabel, &QWidget::customContextMenuRequested, this,\n                    [this, registerEditValue, registerLabel](QPoint p) {\n                        openContextMenu(registerLabel->mapToGlobal(p), registerEditValue->text());\n                    });\n            registerEditValue->setContextMenuPolicy(Qt::CustomContextMenu);\n            connect(registerEditValue, &QWidget::customContextMenuRequested, this,\n                    [this, registerEditValue](QPoint p) {\n                        openContextMenu(registerEditValue->mapToGlobal(p),\n                                        registerEditValue->text());\n                    });\n            // add label and register value to grid\n            registerLayout->addWidget(registerLabel, i, col);\n            registerLayout->addWidget(registerEditValue, i, col + 1);\n            connect(registerEditValue, &QLineEdit::editingFinished, [=]() {\n                QString regNameString = registerLabel->text();\n                QString regValueString = registerEditValue->text();\n                Core()->setRegister(regNameString, regValueString);\n            });\n        } else {\n            QWidget *regNameWidget = registerLayout->itemAtPosition(i, col)->widget();\n            QWidget *regValueWidget = registerLayout->itemAtPosition(i, col + 1)->widget();\n            registerLabel = qobject_cast<QLabel *>(regNameWidget);\n            registerEditValue = qobject_cast<QLineEdit *>(regValueWidget);\n        }\n        // decide to highlight QLine Box in case of change of register value\n        if (regValue != registerEditValue->text() && !registerEditValue->text().isEmpty()) {\n            registerEditValue->setStyleSheet(\"border: 1px solid green;\");\n        } else {\n            // reset stylesheet\n            registerEditValue->setStyleSheet(\"\");\n        }\n        // define register label and value\n        registerLabel->setText(reg.name);\n\n        registerLabel->setToolTip(reg.ref);\n        registerEditValue->setToolTip(reg.ref);\n\n        registerEditValue->setPlaceholderText(regValue);\n        registerEditValue->setText(regValue);\n        i++;\n        // decide if we should change column\n        if (i >= (registerLen + numCols - 1) / numCols) {\n            i = 0;\n            col += 2;\n        }\n    }\n}\n\nvoid RegistersWidget::openContextMenu(QPoint point, QString address)\n{\n    addressContextMenu.setTarget(address.toULongLong(nullptr, 16));\n    addressContextMenu.exec(point);\n}\n\nvoid RegistersWidget::configureRegProfileClicked()\n{\n    RegisterProfileDialog dialog(this);\n    dialog.setProfileData(Core()->getRegisterProfile());\n    dialog.setProfilePath(currProfilePath);\n    dialog.fillProfilePaths(Config()->getRecentRegProfiles());\n\n    if (dialog.exec() != QDialog::Accepted) {\n        return;\n    }\n\n    Core()->setRegisterProfile(dialog.getProfileData());\n    currProfilePath = dialog.getProfilePath();\n\n    if (dialog.getLoadedProfile() != RegisterProfile::Default) {\n        Config()->addRecentRegProfile(dialog.getSerializedProfilePath());\n    }\n}\n"
  },
  {
    "path": "src/widgets/RegistersWidget.h",
    "content": "#pragma once\n\n#include <QTextEdit>\n#include <QPlainTextEdit>\n#include <QGridLayout>\n#include <QJsonObject>\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"menus/AddressableItemContextMenu.h\"\n\nclass MainWindow;\n\nnamespace Ui {\nclass RegistersWidget;\n}\n\nclass RegistersWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit RegistersWidget(MainWindow *main);\n    ~RegistersWidget();\n\nprivate slots:\n    void updateContents();\n    void setRegisterGrid();\n    void openContextMenu(QPoint point, QString address);\n\n    /**\n     * @brief Opens the RegisterProfileDialog to view or edit register profile\n     *\n     * Updates Core with the new profile and saves the custom path to recent settings if the user\n     * accepts the changes\n     */\n    void configureRegProfileClicked();\n\nprivate:\n    std::unique_ptr<Ui::RegistersWidget> ui;\n    QGridLayout *registerLayout = new QGridLayout;\n    AddressableItemContextMenu addressContextMenu;\n    int numCols = 2;\n    int registerLen = 0;\n    RefreshDeferrer *refreshDeferrer;\n    QString currProfilePath;\n};\n"
  },
  {
    "path": "src/widgets/RegistersWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RegistersWidget</class>\n <widget class=\"QDockWidget\" name=\"RegistersWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>463</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Registers</string>\n  </property>\n  <widget class=\"QWidget\" name=\"mainContainer\">\n   <layout class=\"QVBoxLayout\" name=\"mainLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"margin\" stdset=\"0\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QScrollArea\" name=\"scrollArea\">\n      <property name=\"frameShape\">\n       <enum>QFrame::Shape::NoFrame</enum>\n      </property>\n      <property name=\"widgetResizable\">\n       <bool>true</bool>\n      </property>\n      <widget class=\"QWidget\" name=\"dockWidgetContents\">\n       <property name=\"geometry\">\n        <rect>\n         <x>0</x>\n         <y>0</y>\n         <width>463</width>\n         <height>248</height>\n        </rect>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n        <property name=\"spacing\">\n         <number>10</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>6</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>6</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>6</number>\n        </property>\n       </layout>\n      </widget>\n     </widget>\n    </item>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"regProfileLayout\">\n      <property name=\"leftMargin\">\n       <number>6</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>4</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>6</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>4</number>\n      </property>\n      <item>\n       <widget class=\"QPushButton\" name=\"configureProfileBtn\">\n        <property name=\"text\">\n         <string>Configure Register Profile</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <spacer name=\"horizontalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Orientation::Horizontal</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>40</width>\n          <height>20</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/RelocsWidget.cpp",
    "content": "#include \"RelocsWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n\n#include <QShortcut>\n#include <QTreeWidget>\n\nRelocsModel::RelocsModel(QObject *parent) : AddressableItemModel<QAbstractTableModel>(parent) {}\n\nint RelocsModel::rowCount(const QModelIndex &parent) const\n{\n    return parent.isValid() ? 0 : relocs.count();\n}\n\nint RelocsModel::columnCount(const QModelIndex &) const\n{\n    return RelocsModel::ColumnCount;\n}\n\nQVariant RelocsModel::data(const QModelIndex &index, int role) const\n{\n    const RelocDescription &reloc = relocs.at(index.row());\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case RelocsModel::VAddrColumn:\n            return RzAddressString(reloc.vaddr);\n        case RelocsModel::TypeColumn:\n            return reloc.type;\n        case RelocsModel::NameColumn:\n            return reloc.name;\n        case RelocsModel::CommentColumn:\n            return Core()->getCommentAt(reloc.vaddr);\n        default:\n            break;\n        }\n        break;\n    case RelocsModel::RelocDescriptionRole:\n        return QVariant::fromValue(reloc);\n    case RelocsModel::AddressRole:\n        return reloc.vaddr;\n    default:\n        break;\n    }\n    return QVariant();\n}\n\nQVariant RelocsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    if (role == Qt::DisplayRole)\n        switch (section) {\n        case RelocsModel::VAddrColumn:\n            return tr(\"Address\");\n        case RelocsModel::TypeColumn:\n            return tr(\"Type\");\n        case RelocsModel::NameColumn:\n            return tr(\"Name\");\n        case RelocsModel::CommentColumn:\n            return tr(\"Comment\");\n        }\n    return QVariant();\n}\n\nRVA RelocsModel::address(const QModelIndex &index) const\n{\n    const RelocDescription &reloc = relocs.at(index.row());\n    return reloc.vaddr;\n}\n\nQString RelocsModel::name(const QModelIndex &index) const\n{\n    const RelocDescription &reloc = relocs.at(index.row());\n    return reloc.name;\n}\n\nvoid RelocsModel::reload()\n{\n    beginResetModel();\n    relocs = Core()->getAllRelocs();\n    endResetModel();\n}\n\nRelocsProxyModel::RelocsProxyModel(RelocsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool RelocsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    auto reloc = index.data(RelocsModel::RelocDescriptionRole).value<RelocDescription>();\n\n    return qhelpers::filterStringContains(reloc.name, this);\n}\n\nbool RelocsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    if (!left.isValid() || !right.isValid())\n        return false;\n\n    if (left.parent().isValid() || right.parent().isValid())\n        return false;\n\n    auto leftReloc = left.data(RelocsModel::RelocDescriptionRole).value<RelocDescription>();\n    auto rightReloc = right.data(RelocsModel::RelocDescriptionRole).value<RelocDescription>();\n\n    switch (left.column()) {\n    case RelocsModel::VAddrColumn:\n        return leftReloc.vaddr < rightReloc.vaddr;\n    case RelocsModel::TypeColumn:\n        return leftReloc.type < rightReloc.type;\n    case RelocsModel::NameColumn:\n        return leftReloc.name < rightReloc.name;\n    case RelocsModel::CommentColumn:\n        return Core()->getCommentAt(leftReloc.vaddr) < Core()->getCommentAt(rightReloc.vaddr);\n    default:\n        break;\n    }\n\n    return false;\n}\n\nRelocsWidget::RelocsWidget(MainWindow *main)\n    : ListDockWidget(main),\n      relocsModel(new RelocsModel(this)),\n      relocsProxyModel(new RelocsProxyModel(relocsModel, this))\n{\n    setWindowTitle(tr(\"Relocs\"));\n    setObjectName(\"RelocsWidget\");\n\n    setModels(relocsProxyModel);\n    ui->treeView->sortByColumn(RelocsModel::NameColumn, Qt::AscendingOrder);\n\n    connect(Core(), &CutterCore::codeRebased, this, &RelocsWidget::refreshRelocs);\n    connect(Core(), &CutterCore::refreshAll, this, &RelocsWidget::refreshRelocs);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(relocsModel, RelocsModel::CommentColumn); });\n}\n\nRelocsWidget::~RelocsWidget() {}\n\nvoid RelocsWidget::refreshRelocs()\n{\n    relocsModel->reload();\n    qhelpers::adjustColumns(ui->treeView, 3, 0);\n}\n"
  },
  {
    "path": "src/widgets/RelocsWidget.h",
    "content": "#ifndef RELOCSWIDGET_H\n#define RELOCSWIDGET_H\n\n#include <memory>\n#include <QAbstractTableModel>\n#include <QSortFilterProxyModel>\n\n#include \"CutterDockWidget.h\"\n#include \"core/Cutter.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass MainWindow;\nclass RelocsWidget;\n\nclass RelocsModel : public AddressableItemModel<QAbstractTableModel>\n{\n    Q_OBJECT\n\n    friend RelocsWidget;\n\nprivate:\n    QList<RelocDescription> relocs;\n\npublic:\n    enum Column { VAddrColumn = 0, TypeColumn, NameColumn, CommentColumn, ColumnCount };\n    enum Role { RelocDescriptionRole = Qt::UserRole, AddressRole };\n\n    RelocsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent) const override;\n    int columnCount(const QModelIndex &parent) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n\n    void reload();\n};\n\nclass RelocsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    RelocsProxyModel(RelocsModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass RelocsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit RelocsWidget(MainWindow *main);\n    ~RelocsWidget();\n\nprivate slots:\n    void refreshRelocs();\n\nprivate:\n    RelocsModel *relocsModel;\n    RelocsProxyModel *relocsProxyModel;\n};\n\n#endif // RELOCSWIDGET_H\n"
  },
  {
    "path": "src/widgets/ResourcesWidget.cpp",
    "content": "#include \"common/Helpers.h\"\n#include \"ResourcesWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include <QVBoxLayout>\n\nResourcesModel::ResourcesModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent)\n{\n}\n\nint ResourcesModel::rowCount(const QModelIndex &) const\n{\n    return resources.count();\n}\n\nint ResourcesModel::columnCount(const QModelIndex &) const\n{\n    return Columns::COUNT;\n}\n\nQVariant ResourcesModel::data(const QModelIndex &index, int role) const\n{\n    const ResourcesDescription &res = resources.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case NAME:\n            return res.name;\n        case VADDR:\n            return RzAddressString(res.vaddr);\n        case INDEX:\n            return QString::number(res.index);\n        case TYPE:\n            return res.type;\n        case SIZE:\n            return qhelpers::formatBytecount(res.size);\n        case LANG:\n            return res.lang;\n        case COMMENT:\n            return Core()->getCommentAt(res.vaddr);\n        default:\n            return QVariant();\n        }\n    case Qt::EditRole:\n        switch (index.column()) {\n        case NAME:\n            return res.name;\n        case VADDR:\n            return res.vaddr;\n        case INDEX:\n            return res.index;\n        case TYPE:\n            return res.type;\n        case SIZE:\n            return res.size;\n        case LANG:\n            return res.lang;\n        default:\n            return QVariant();\n        }\n    case Qt::UserRole:\n        return QVariant::fromValue(res);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant ResourcesModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case NAME:\n            return tr(\"Name\");\n        case VADDR:\n            return tr(\"Vaddr\");\n        case INDEX:\n            return tr(\"Index\");\n        case TYPE:\n            return tr(\"Type\");\n        case SIZE:\n            return tr(\"Size\");\n        case LANG:\n            return tr(\"Lang\");\n        case COMMENT:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA ResourcesModel::address(const QModelIndex &index) const\n{\n    const ResourcesDescription &res = resources.at(index.row());\n    return res.vaddr;\n}\n\nResourcesWidget::ResourcesWidget(MainWindow *main)\n    : ListDockWidget(main, ListDockWidget::SearchBarPolicy::HideByDefault)\n{\n    setObjectName(\"ResourcesWidget\");\n\n    model = new ResourcesModel(this);\n    filterModel = new AddressableFilterProxyModel(model, this);\n    filterModel->setSortRole(Qt::EditRole);\n    setModels(filterModel);\n\n    ui->treeView->sortByColumn(0, Qt::AscendingOrder);\n\n    showCount(false);\n\n    // Configure widget\n    this->setWindowTitle(tr(\"Resources\"));\n\n    connect(Core(), &CutterCore::refreshAll, this, &ResourcesWidget::refreshResources);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(model, ResourcesModel::COMMENT); });\n}\n\nvoid ResourcesWidget::refreshResources()\n{\n    model->beginResetModel();\n    model->resources = Core()->getAllResources();\n    model->endResetModel();\n}\n"
  },
  {
    "path": "src/widgets/ResourcesWidget.h",
    "content": "#ifndef RESOURCESWIDGET_H\n#define RESOURCESWIDGET_H\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeView.h\"\n#include \"common/AddressableItemModel.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass MainWindow;\nclass ResourcesWidget;\n\nclass ResourcesModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend ResourcesWidget;\n\nprivate:\n    QList<ResourcesDescription> resources;\n\npublic:\n    enum Columns { INDEX = 0, NAME, VADDR, TYPE, SIZE, LANG, COMMENT, COUNT };\n    explicit ResourcesModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n};\n\nclass ResourcesWidget : public ListDockWidget\n{\n    Q_OBJECT\n\nprivate:\n    ResourcesModel *model;\n    AddressableFilterProxyModel *filterModel;\n\npublic:\n    explicit ResourcesWidget(MainWindow *main);\n\nprivate slots:\n    void refreshResources();\n};\n\n#endif // RESOURCESWIDGET_H\n"
  },
  {
    "path": "src/widgets/RizinGraphWidget.cpp",
    "content": "#include \"RizinGraphWidget.h\"\n#include \"ui_RizinGraphWidget.h\"\n\n#include <QJsonValue>\n#include <QJsonArray>\n#include <QJsonObject>\n\nRizinGraphWidget::RizinGraphWidget(MainWindow *main)\n    : CutterDockWidget(main),\n      ui(new Ui::RizinGraphWidget),\n      graphView(new GenericRizinGraphView(this, main))\n{\n    ui->setupUi(this);\n    ui->verticalLayout->addWidget(graphView);\n    connect(ui->refreshButton, &QPushButton::pressed, this, [this]() { graphView->refreshView(); });\n    struct GraphType\n    {\n        QChar commandChar;\n        QString label;\n    } types[] = {\n        { 'a', tr(\"Data reference graph (aga)\") },\n        { 'A', tr(\"Global data references graph (agA)\") },\n        // {'c', tr(\"c - Function callgraph\")},\n        // {'C', tr(\"C - Global callgraph\")},\n        // {'f', tr(\"f - Basic blocks function graph\")},\n        { 'i', tr(\"Imports graph (agi)\") },\n        { 'r', tr(\"References graph (agr)\") },\n        { 'R', tr(\"Global references graph (agR)\") },\n        { 'x', tr(\"Cross references graph (agx)\") },\n        { 'I', tr(\"RzIL statement graph (agI)\") },\n        { 'g', tr(\"Custom graph (agg)\") },\n        { ' ', tr(\"User command\") },\n    };\n    for (auto &graphType : types) {\n        if (graphType.commandChar != ' ') {\n            ui->graphType->addItem(graphType.label, graphType.commandChar);\n        } else {\n            ui->graphType->addItem(graphType.label, QVariant());\n        }\n    }\n    connect<void (QComboBox::*)(int)>(ui->graphType, &QComboBox::currentIndexChanged, this,\n                                      &RizinGraphWidget::typeChanged);\n    connect(ui->customCommand, &QLineEdit::textEdited, this,\n            [this]() { graphView->setGraphCommand(ui->customCommand->text()); });\n    connect(ui->customCommand, &QLineEdit::returnPressed, this, [this]() {\n        graphView->setGraphCommand(ui->customCommand->text());\n        graphView->refreshView();\n    });\n    ui->customCommand->hide();\n    typeChanged();\n}\n\nRizinGraphWidget::~RizinGraphWidget() {}\n\nvoid RizinGraphWidget::typeChanged()\n{\n    auto currentData = ui->graphType->currentData();\n    if (currentData.isNull()) {\n        ui->customCommand->setVisible(true);\n        graphView->setGraphCommand(ui->customCommand->text());\n        ui->customCommand->setFocus();\n    } else {\n        ui->customCommand->setVisible(false);\n        auto command = QString(\"ag%1\").arg(currentData.toChar());\n        graphView->setGraphCommand(command);\n        graphView->refreshView();\n    }\n}\n\nGenericRizinGraphView::GenericRizinGraphView(RizinGraphWidget *parent, MainWindow *main)\n    : SimpleTextGraphView(parent, main), refreshDeferrer(nullptr, this)\n{\n    refreshDeferrer.registerFor(parent);\n    connect(&refreshDeferrer, &RefreshDeferrer::refreshNow, this,\n            &GenericRizinGraphView::refreshView);\n}\n\nvoid GenericRizinGraphView::setGraphCommand(QString cmd)\n{\n    graphCommand = cmd;\n}\n\nvoid GenericRizinGraphView::refreshView()\n{\n    if (!refreshDeferrer.attemptRefresh(nullptr)) {\n        return;\n    }\n    SimpleTextGraphView::refreshView();\n}\n\nvoid GenericRizinGraphView::loadCurrentGraph()\n{\n    blockContent.clear();\n    blocks.clear();\n\n    if (graphCommand.isEmpty()) {\n        return;\n    }\n\n    CutterJson functionsDoc = Core()->cmdj(QString(\"%1 json\").arg(graphCommand));\n    auto nodes = functionsDoc[\"nodes\"];\n\n    for (CutterJson block : nodes) {\n        uint64_t id = block[\"id\"].toUt64();\n\n        QString content;\n        QString title = block[\"title\"].toString();\n        QString body = block[\"body\"].toString();\n        if (!title.isEmpty() && !body.isEmpty()) {\n            content = title + \"/n\" + body;\n        } else {\n            content = title + body;\n        }\n\n        auto edges = block[\"out_nodes\"];\n        GraphLayout::GraphBlock layoutBlock;\n        layoutBlock.entry = id;\n        for (auto edge : edges) {\n            auto targetId = edge.toUt64();\n            layoutBlock.edges.emplace_back(targetId);\n        }\n\n        addBlock(std::move(layoutBlock), content);\n    }\n\n    cleanupEdges(blocks);\n\n    computeGraphPlacement();\n\n    if (graphCommand != lastShownCommand) {\n        selectedBlock = NO_BLOCK_SELECTED;\n        lastShownCommand = graphCommand;\n        center();\n    }\n}\n"
  },
  {
    "path": "src/widgets/RizinGraphWidget.h",
    "content": "#ifndef RZ_GRAPH_WIDGET_H\n#define RZ_GRAPH_WIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"widgets/SimpleTextGraphView.h\"\n#include \"common/RefreshDeferrer.h\"\n\nclass MainWindow;\n\nnamespace Ui {\nclass RizinGraphWidget;\n}\n\nclass RizinGraphWidget;\n\n/**\n * @brief Generic graph view for rizin graphs.\n * Not all rizin graph commands output the same kind of json. Only those that have following format\n * @code{.json}\n * { \"nodes\": [\n *      {\n *          \"id\": 0,\n *          \"tittle\": \"node_0_tittle\",\n *          \"body\": \"\".\n *          \"out_nodes\": [1, 2, 3]\n *      },\n *      ...\n * ]}\n * @endcode\n * Id don't have to be sequential. Simple text label is displayed containing concatenation of\n * label and body. No rizin builtin graph uses both. Duplicate edges and edges with target id\n * not present in the list of nodes are removed.\n */\nclass GenericRizinGraphView : public SimpleTextGraphView\n{\n    Q_OBJECT\npublic:\n    GenericRizinGraphView(RizinGraphWidget *parent, MainWindow *main);\n    void setGraphCommand(QString cmd);\n    void refreshView() override;\n\nprotected:\n    void loadCurrentGraph() override;\n\nprivate:\n    RefreshDeferrer refreshDeferrer;\n    QString graphCommand;\n    QString lastShownCommand;\n};\n\nclass RizinGraphWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit RizinGraphWidget(MainWindow *main);\n    ~RizinGraphWidget();\n\nprivate:\n    std::unique_ptr<Ui::RizinGraphWidget> ui;\n    GenericRizinGraphView *graphView;\n\n    void typeChanged();\n};\n\n#endif // RZ_GRAPH_WIDGET_H\n"
  },
  {
    "path": "src/widgets/RizinGraphWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RizinGraphWidget</class>\n <widget class=\"QDockWidget\" name=\"RizinGraphWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>439</width>\n    <height>162</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Rizin graphs</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n      <item>\n       <widget class=\"QComboBox\" name=\"graphType\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"editable\">\n         <bool>false</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLineEdit\" name=\"customCommand\">\n        <property name=\"enabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n          <horstretch>1</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"placeholderText\">\n         <string>ag...</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QPushButton\" name=\"refreshButton\">\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"../resources.qrc\">\n          <normaloff>:/img/icons/arrow_right.svg</normaloff>:/img/icons/arrow_right.svg</iconset>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <spacer name=\"horizontalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Horizontal</enum>\n        </property>\n        <property name=\"sizeType\">\n         <enum>QSizePolicy::MinimumExpanding</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>0</width>\n          <height>20</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources>\n  <include location=\"../resources.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/SdbWidget.cpp",
    "content": "#include \"SdbWidget.h\"\n#include \"ui_SdbWidget.h\"\n\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n\n#include <QDebug>\n#include <QTreeWidget>\n\nSdbWidget::SdbWidget(MainWindow *main) : CutterDockWidget(main), ui(new Ui::SdbWidget)\n{\n    ui->setupUi(this);\n\n    path.clear();\n\n    connect(Core(), &CutterCore::refreshAll, this, [this]() { reload(); });\n    reload();\n}\n\nvoid SdbWidget::reload(QString _path)\n{\n    path = _path;\n\n    ui->lineEdit->setText(path);\n    /* insert root sdb keyvalue pairs */\n\n    ui->treeWidget->clear();\n    QList<QString> keys;\n    /* key-values */\n    keys = Core()->sdbListKeys(path);\n    for (const QString &key : keys) {\n        QTreeWidgetItem *tempItem = new QTreeWidgetItem();\n        tempItem->setText(0, key);\n        tempItem->setText(1, Core()->sdbGet(path, key));\n        tempItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled\n                           | Qt::ItemIsDragEnabled | Qt::ItemIsEditable);\n        ui->treeWidget->insertTopLevelItem(0, tempItem);\n    }\n    qhelpers::adjustColumns(ui->treeWidget, 0);\n    /* namespaces */\n    keys = Core()->sdbList(path);\n    if (!path.isEmpty()) {\n        keys.append(\"..\");\n    }\n    for (const QString &key : keys) {\n        QTreeWidgetItem *tempItem = new QTreeWidgetItem();\n        tempItem->setText(0, key + \"/\");\n        tempItem->setText(1, \"\");\n        ui->treeWidget->insertTopLevelItem(0, tempItem);\n    }\n    qhelpers::adjustColumns(ui->treeWidget, 0);\n}\n\nvoid SdbWidget::on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)\n{\n    if (column < 0)\n        return;\n\n    QString newpath;\n\n    if (column == 0) {\n        if (item->text(0) == \"../\") {\n            int idx = path.lastIndexOf(QLatin1Char('/'));\n            if (idx != -1) {\n                newpath = path.mid(0, idx);\n            } else {\n                newpath.clear();\n            }\n            reload(newpath);\n\n        } else if (item->text(0).indexOf(QLatin1Char('/')) != -1) {\n            if (!path.isEmpty()) {\n                newpath = path + \"/\" + item->text(0).remove(QLatin1Char('/'));\n            } else {\n                newpath = path + item->text(0).remove(QLatin1Char('/'));\n            }\n            // enter directory\n            reload(newpath);\n        }\n    }\n}\n\nSdbWidget::~SdbWidget() = default;\n\nvoid SdbWidget::on_lockButton_clicked()\n{\n    if (ui->lockButton->isChecked()) {\n        this->setAllowedAreas(Qt::NoDockWidgetArea);\n        ui->lockButton->setIcon(QIcon(\":/lock\"));\n    } else {\n        this->setAllowedAreas(Qt::AllDockWidgetAreas);\n        ui->lockButton->setIcon(QIcon(\":/unlock\"));\n    }\n}\n\nvoid SdbWidget::on_treeWidget_itemChanged(QTreeWidgetItem *item, int column)\n{\n    Core()->sdbSet(path, item->text(0), item->text(column));\n}\n"
  },
  {
    "path": "src/widgets/SdbWidget.h",
    "content": "#ifndef SDBWIDGET_H\n#define SDBWIDGET_H\n\n#include <memory>\n\n#include \"CutterDockWidget.h\"\n\nclass MainWindow;\nclass QTreeWidgetItem;\n\nnamespace Ui {\nclass SdbWidget;\n}\n\nclass SdbWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)\n#    define Q_DISABLE_COPY(SdbWidget)                                                              \\\n        SdbWidget(const SdbWidget &s) = delete;                                                    \\\n        SdbWidget &operator=(const SdbWidget &s) = delete;\n\n#    define Q_DISABLE_MOVE(SdbWidget)                                                              \\\n        SdbWidget(SdbWidget &&s) = delete;                                                         \\\n        SdbWidget &operator=(SdbWidget &&s) = delete;\n\n#    define Q_DISABLE_COPY_MOVE(SdbWidget)                                                         \\\n        Q_DISABLE_COPY(SdbWidget)                                                                  \\\n        Q_DISABLE_MOVE(SdbWidget)\n#endif\n\n    Q_DISABLE_COPY_MOVE(SdbWidget)\n\npublic:\n    explicit SdbWidget(MainWindow *main);\n    ~SdbWidget() override;\n\nprivate slots:\n    void on_treeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);\n    void on_lockButton_clicked();\n    void on_treeWidget_itemChanged(QTreeWidgetItem *item, int column);\n\n    void reload(QString _path = QString());\n\nprivate:\n    std::unique_ptr<Ui::SdbWidget> ui;\n    QString path;\n};\n\n#endif // SDBWIDGET_H\n"
  },
  {
    "path": "src/widgets/SdbWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SdbWidget</class>\n <widget class=\"QDockWidget\" name=\"SdbWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>575</width>\n    <height>350</height>\n   </rect>\n  </property>\n  <property name=\"floating\">\n   <bool>false</bool>\n  </property>\n  <property name=\"windowTitle\">\n   <string>SDB Browser</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>5</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>5</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>5</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>5</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n      <property name=\"spacing\">\n       <number>5</number>\n      </property>\n      <item>\n       <widget class=\"QLineEdit\" name=\"lineEdit\">\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"frame\">\n         <bool>false</bool>\n        </property>\n        <property name=\"placeholderText\">\n         <string notr=\"true\"/>\n        </property>\n        <property name=\"clearButtonEnabled\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QToolButton\" name=\"searchButton\">\n        <property name=\"text\">\n         <string notr=\"true\"/>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"../resources.qrc\">\n          <normaloff>:/img/icons/arrow_right.svg</normaloff>:/img/icons/arrow_right.svg</iconset>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"Line\" name=\"line\">\n        <property name=\"orientation\">\n         <enum>Qt::Vertical</enum>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QToolButton\" name=\"lockButton\">\n        <property name=\"text\">\n         <string notr=\"true\"/>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"../resources.qrc\">\n          <normaloff>:/img/icons/unlock_white.svg</normaloff>:/img/icons/unlock_white.svg</iconset>\n        </property>\n        <property name=\"checkable\">\n         <bool>true</bool>\n        </property>\n        <property name=\"checked\">\n         <bool>false</bool>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </item>\n    <item>\n     <widget class=\"QTreeWidget\" name=\"treeWidget\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">QTreeWidget::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"frameShadow\">\n       <enum>QFrame::Plain</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>false</bool>\n      </property>\n      <column>\n       <property name=\"text\">\n        <string>Key</string>\n       </property>\n      </column>\n      <column>\n       <property name=\"text\">\n        <string>Value</string>\n       </property>\n      </column>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <resources>\n  <include location=\"../resources.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/SearchBarWidget.cpp",
    "content": "#include \"SearchBarWidget.h\"\n#include \"ui_SearchBarWidget.h\"\n#include \"CutterSearchable.h\"\n#include \"shortcuts/ShortcutManager.h\"\n#include \"Configuration.h\"\n\n#include <QStyle>\n#include <QStyleOption>\n#include <QPainter>\n#include <QGraphicsDropShadowEffect>\n#include <QTimer>\n#include <QBitArray>\n#include <QSizeGrip>\n#include <QMenu>\n#include <QKeyEvent>\n\nSearchBarSizeGrip::SearchBarSizeGrip(QWidget *parent) : QSizeGrip(parent)\n{\n    this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);\n    this->setFixedWidth(5);\n}\n\nvoid SearchBarSizeGrip::moveEvent(QMoveEvent *moveEvent)\n{\n    QSizeGrip::moveEvent(moveEvent);\n    setCursor(Qt::SizeHorCursor);\n}\n\nvoid SearchBarSizeGrip::paintEvent(QPaintEvent *event)\n{\n    Q_UNUSED(event);\n}\n\nSearchBarWidget::SearchBarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::SearchBarWidget)\n{\n    ui->setupUi(this);\n\n    auto *shadow = new QGraphicsDropShadowEffect(this);\n    shadow->setBlurRadius(15);\n    shadow->setColor(QColor(0, 0, 0, 120));\n    shadow->setOffset(0, 2);\n    this->setGraphicsEffect(shadow);\n\n    // buttons\n    ui->menuButton->setAutoRaise(true);\n    ui->menuButton->setPopupMode(QToolButton::InstantPopup);\n    ui->menuButton->setStyleSheet(\"QToolButton { padding: 5px; }\"\n                                  \"QToolButton::menu-indicator { width: 0px; image: none; }\");\n    ui->findNextButton->setAutoRaise(true);\n    ui->findPrevButton->setAutoRaise(true);\n    ui->findLastButton->setAutoRaise(true);\n    ui->hideButton->setAutoRaise(true);\n\n    ui->menuButton->setIcon(QIcon(\":/img/icons/cog_light.svg\"));\n    ui->findNextButton->setIcon(QIcon(\":/img/icons/arrow_right.svg\"));\n    ui->findPrevButton->setIcon(QIcon(\":/img/icons/arrow_left.svg\"));\n    ui->findLastButton->setIcon(QIcon(\":/img/icons/down_light.svg\"));\n    ui->hideButton->setIcon(QIcon(\":/img/icons/delete_light.svg\"));\n\n    // removing this makes the buttons too close together in light theme\n    const QString toolButtonPadding = \"QToolButton { padding: 5px; }\";\n    ui->findNextButton->setStyleSheet(toolButtonPadding);\n    ui->findPrevButton->setStyleSheet(toolButtonPadding);\n    ui->findLastButton->setStyleSheet(toolButtonPadding);\n\n    connect(ui->findNextButton, &QToolButton::clicked, this, &SearchBarWidget::findNextTriggered);\n    connect(ui->findPrevButton, &QToolButton::clicked, this, &SearchBarWidget::findPrevTriggered);\n    connect(ui->findLastButton, &QToolButton::clicked, this, &SearchBarWidget::findLastTriggered);\n    connect(ui->hideButton, &QToolButton::clicked, this, &SearchBarWidget::hideSearchBar);\n\n    // shortcuts\n    QShortcut *findNextShortcut = Shortcuts()->makeQShortcut(\"Search.findNext\", ui->searchEdit);\n    QShortcut *findPrevShortcut = Shortcuts()->makeQShortcut(\"Search.findPrev\", ui->searchEdit);\n    QShortcut *findLastShortcut = Shortcuts()->makeQShortcut(\"Search.findLast\", ui->searchEdit);\n    QShortcut *hideShortcut = Shortcuts()->makeQShortcut(\"Search.hide\", this);\n    QShortcut *optionsShortcut = Shortcuts()->makeQShortcut(\"Search.options\", this);\n    hideShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n    connect(findNextShortcut, &QShortcut::activated, this, &SearchBarWidget::findNextTriggered);\n    connect(findPrevShortcut, &QShortcut::activated, this, &SearchBarWidget::findPrevTriggered);\n    connect(findLastShortcut, &QShortcut::activated, this, &SearchBarWidget::findLastTriggered);\n    connect(hideShortcut, &QShortcut::activated, ui->hideButton, &QToolButton::click);\n    connect(optionsShortcut, &QShortcut::activated, ui->menuButton, &QToolButton::click);\n\n    // menu\n    QMenu *optionsMenu = new QMenu(this);\n    ui->menuButton->setMenu(optionsMenu);\n\n    auto emitSearchChanged = [this] { emit searchChanged(text(), options()); };\n\n    m_caseSensitiveAction = optionsMenu->addAction(tr(\"&Case Sensitive\"));\n    m_caseSensitiveAction->setCheckable(true);\n    connect(m_caseSensitiveAction, &QAction::triggered, this, emitSearchChanged);\n\n    m_wholeWordsAction = optionsMenu->addAction(tr(\"Match &Whole Words\"));\n    m_wholeWordsAction->setCheckable(true);\n    connect(m_wholeWordsAction, &QAction::triggered, this, emitSearchChanged);\n\n    m_regExpAction = optionsMenu->addAction(tr(\"Match &Regular Expression\"));\n    m_regExpAction->setCheckable(true);\n    connect(m_regExpAction, &QAction::triggered, this, emitSearchChanged);\n\n    m_highlightMatchesAction = optionsMenu->addAction(tr(\"&Highlight All Matches\"));\n    m_highlightMatchesAction->setCheckable(true);\n    m_highlightMatchesAction->setChecked(true);\n    connect(m_highlightMatchesAction, &QAction::triggered, this, emitSearchChanged);\n\n    // debounce timer\n    QTimer *debounceTimer = new QTimer(this);\n    debounceTimer->setSingleShot(true);\n    debounceTimer->setInterval(200);\n    connect(debounceTimer, &QTimer::timeout, this,\n            [this] { emit searchChanged(text(), options()); });\n    connect(ui->searchEdit, &QLineEdit::textChanged, debounceTimer,\n            static_cast<void (QTimer::*)()>(&QTimer::start));\n\n    // size grip for resizing\n    this->setWindowFlags(Qt::SubWindow);\n    SearchBarSizeGrip *sg = new SearchBarSizeGrip(this);\n    ui->horizontalLayout->insertWidget(0, sg);\n\n    setFocusProxy(ui->searchEdit);\n    this->setMinimumWidth(this->width() + 65); // account for spacing and margins\n\n    // fixes the outline not showing around the widget where the label is present\n    ui->searchLabel->setStyleSheet(\"QLabel { background: transparent; }\");\n}\n\nSearchBarWidget::~SearchBarWidget() {}\n\nvoid SearchBarWidget::setCurrentIndex(int index)\n{\n    m_index = index;\n    updateLabel();\n}\n\nvoid SearchBarWidget::setTotalCount(int count)\n{\n    m_count = count;\n    updateLabel();\n}\n\nvoid SearchBarWidget::setRange(int index, int count)\n{\n    m_index = index;\n    m_count = count;\n    updateLabel();\n}\n\nvoid SearchBarWidget::clear()\n{\n    m_index = 0;\n    m_count = 0;\n    updateLabel();\n}\n\nvoid SearchBarWidget::updateLabel()\n{\n    // indexes are 0-based\n    int index = std::min(m_index + 1, m_count);\n    ui->searchLabel->setText(tr(\"%1 of %2\").arg(index).arg(m_count));\n}\n\nvoid SearchBarWidget::showSearchBar()\n{\n    this->show();\n    this->raise();\n    this->setFocus();\n    emit showTriggered();\n}\n\nvoid SearchBarWidget::hideSearchBar()\n{\n    this->hide();\n    emit hideTriggered();\n}\n\nvoid SearchBarWidget::selectText()\n{\n    ui->searchEdit->selectAll();\n}\n\nint SearchBarWidget::totalCount() const\n{\n    return m_count;\n}\n\nint SearchBarWidget::currentIndex() const\n{\n    return m_index;\n}\n\nQString SearchBarWidget::text() const\n{\n    return ui->searchEdit->text();\n}\n\nint SearchBarWidget::options() const\n{\n    int options = 0;\n    options = m_caseSensitiveAction->isChecked() ? (options | CaseSensitive) : options;\n    options = m_wholeWordsAction->isChecked() ? (options | WholeWords) : options;\n    options = m_regExpAction->isChecked() ? (options | RegExp) : options;\n    options = m_highlightMatchesAction->isChecked() ? (options | HighlightMatches) : options;\n    return options;\n}\n\nvoid SearchBarWidget::paintEvent(QPaintEvent *event)\n{\n    // fill the background\n    QStyleOption opt;\n    opt.initFrom(this);\n    QPainter p(this);\n    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);\n\n    QWidget::paintEvent(event);\n}\n"
  },
  {
    "path": "src/widgets/SearchBarWidget.h",
    "content": "#ifndef SEARCHBARWIDGET_H\n#define SEARCHBARWIDGET_H\n\n#include <QWidget>\n#include <QSizeGrip>\n#include <memory>\n\nnamespace Ui {\nclass SearchBarWidget;\n}\n\n/**\n * @brief Small grip to let users resize the search bar for the left\n */\nclass SearchBarSizeGrip : public QSizeGrip\n{\npublic:\n    explicit SearchBarSizeGrip(QWidget *parent = nullptr);\n\nprotected:\n    /**\n     * @brief Changes the cursor icon to indicate search bar is expandable in width\n     */\n    void moveEvent(QMoveEvent *moveEvent) override;\n    void paintEvent(QPaintEvent *event) override;\n};\n\n/**\n * @brief The main search bar widget for finding text\n */\nclass SearchBarWidget : public QWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit SearchBarWidget(QWidget *parent = nullptr);\n    ~SearchBarWidget();\n\n    void setTotalCount(int count);\n    void setCurrentIndex(int index);\n    void setRange(int index, int count);\n\n    int totalCount() const;\n    int currentIndex() const;\n    QString text() const;\n    int options() const;\n\n    void updateLabel();\n    void clear();\n\npublic slots:\n    void showSearchBar();\n    void hideSearchBar();\n    void selectText();\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n\nsignals:\n    void searchChanged(const QString &text, int options);\n    void findNextTriggered();\n    void findPrevTriggered();\n    void findLastTriggered();\n    void hideTriggered();\n    void showTriggered();\n\nprivate:\n    std::unique_ptr<Ui::SearchBarWidget> ui;\n    int m_index = 0;\n    int m_count = 0;\n\n    QAction *m_caseSensitiveAction;\n    QAction *m_wholeWordsAction;\n    QAction *m_regExpAction;\n    QAction *m_highlightMatchesAction;\n};\n\n#endif // SEARCHBARWIDGET_H\n"
  },
  {
    "path": "src/widgets/SearchBarWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SearchBarWidget</class>\n <widget class=\"QWidget\" name=\"SearchBarWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>417</width>\n    <height>50</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>50</height>\n   </size>\n  </property>\n  <property name=\"maximumSize\">\n   <size>\n    <width>16777215</width>\n    <height>50</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <property name=\"autoFillBackground\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\" stretch=\"0,1,0,0,0,0,0\">\n   <property name=\"spacing\">\n    <number>2</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>5</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QToolButton\" name=\"menuButton\">\n     <property name=\"toolTip\">\n      <string>Show Search Options</string>\n     </property>\n     <property name=\"text\">\n      <string>...</string>\n     </property>\n     <property name=\"icon\">\n      <iconset theme=\"QIcon::ThemeIcon::DocumentProperties\"/>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLineEdit\" name=\"searchEdit\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>200</width>\n       <height>0</height>\n      </size>\n     </property>\n     <property name=\"placeholderText\">\n      <string>Search</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QToolButton\" name=\"findPrevButton\">\n     <property name=\"toolTip\">\n      <string>Find Previous Match</string>\n     </property>\n     <property name=\"text\">\n      <string>...</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QToolButton\" name=\"findNextButton\">\n     <property name=\"toolTip\">\n      <string>Find Next Match</string>\n     </property>\n     <property name=\"text\">\n      <string>...</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QToolButton\" name=\"findLastButton\">\n     <property name=\"toolTip\">\n      <string>Find Last Match</string>\n     </property>\n     <property name=\"text\">\n      <string>...</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"searchLabel\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>80</width>\n       <height>0</height>\n      </size>\n     </property>\n     <property name=\"layoutDirection\">\n      <enum>Qt::LayoutDirection::LeftToRight</enum>\n     </property>\n     <property name=\"text\">\n      <string>0 of 0</string>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignmentFlag::AlignCenter</set>\n     </property>\n     <property name=\"margin\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QToolButton\" name=\"hideButton\">\n     <property name=\"toolTip\">\n      <string>Hide Search Bar</string>\n     </property>\n     <property name=\"text\">\n      <string>...</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/SearchWidget.cpp",
    "content": "#include \"SearchWidget.h\"\n#include \"ui_SearchWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"DisassemblyPreview.h\"\n\n#include <QDockWidget>\n#include <QTreeWidget>\n#include <QComboBox>\n#include <QShortcut>\n\nnamespace {\n\nstatic const int kMaxTooltipWidth = 500;\nstatic const int kMaxTooltipDisasmPreviewLines = 10;\nstatic const int kMaxTooltipHexdumpBytes = 64;\n\n}\n\nstatic const QVector<std::pair<QString, const char *>> searchBoundaries {\n    { \"io.maps\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"All maps\") },\n    { \"io.map\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Current map\") },\n    { \"raw\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Whole file\") },\n    { \"block\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Current block\") },\n    { \"bin.section\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Current mapped section\") },\n    { \"bin.sections\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"All mapped sections\") },\n    { \"bin.segment\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Current mapped segment\") },\n    { \"bin.segments\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"All mapped segments\") },\n    { \"code\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"All exec sections\") },\n    { \"io.sky\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"All io.skyline\") },\n    { \"analysis.fcn\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Current function\") },\n    { \"analysis.bb\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Current basic block\") },\n};\n\nstatic const QVector<std::pair<QString, const char *>> searchBoundariesDebug {\n    { \"dbg.maps\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"All memory maps\") },\n    { \"dbg.map\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Memory map\") },\n    { \"block\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Current block\") },\n    { \"dbg.program\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"All exec sections\") },\n    { \"dbg.stack\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Stack\") },\n    { \"dbg.heap\", QT_TRANSLATE_NOOP(\"SearchWidget\", \"Heap\") }\n};\n\nstruct SearchKindInfo\n{\n    SearchKind kind;\n    const char *name;\n    const char *textHint;\n    bool noInput;\n};\n\nstatic const SearchKindInfo searchKinds[] = {\n    { SearchKind::AsmCode, QT_TRANSLATE_NOOP(\"SearchWidget\", \"asm code\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"jmp rax\"), false },\n    { SearchKind::String, QT_TRANSLATE_NOOP(\"SearchWidget\", \"string (literal)\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"foobar\"), false },\n    { SearchKind::StringCaseInsensitive,\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"string (case insensitive)\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"fOobaR\"), false },\n    { SearchKind::StringRegexExtended, QT_TRANSLATE_NOOP(\"SearchWidget\", \"string (extended regex)\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"(foo){,4}[Bb]ar\"), false },\n    { SearchKind::HexString, QT_TRANSLATE_NOOP(\"SearchWidget\", \"hex string\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"ab01..23...1234ef\"), false },\n    { SearchKind::ROPGadgets, QT_TRANSLATE_NOOP(\"SearchWidget\", \"ROP gadgets\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"pop,,pop\"), false },\n    { SearchKind::ROPGadgetsRegex, QT_TRANSLATE_NOOP(\"SearchWidget\", \"ROP gadgets (regex)\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"mov e[abc]x\"), false },\n    { SearchKind::Value32BE, QT_TRANSLATE_NOOP(\"SearchWidget\", \"32bit big endian value\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"0xdeadbeef (big endian)\"), false },\n    { SearchKind::Value32LE, QT_TRANSLATE_NOOP(\"SearchWidget\", \"32bit little endian value\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"0xdeadbeef (little endian)\"), false },\n    { SearchKind::Value64BE, QT_TRANSLATE_NOOP(\"SearchWidget\", \"64bit big endian value\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"0xfedcba9876543210 (big endian)\"), false },\n    { SearchKind::Value64BE, QT_TRANSLATE_NOOP(\"SearchWidget\", \"64bit little endian value\"),\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"0xfedcba9876543210 (little endian)\"), false },\n    { SearchKind::CryptographicMaterial,\n      QT_TRANSLATE_NOOP(\"SearchWidget\", \"Cryptographic material\"), nullptr, true },\n    { SearchKind::MagicSignature, QT_TRANSLATE_NOOP(\"SearchWidget\", \"Magic signature\"), nullptr,\n      true },\n};\n\nstatic const SearchKindInfo &searchKindInfo(SearchKind kind)\n{\n    auto res = std::find_if(std::begin(searchKinds), std::end(searchKinds),\n                            [kind](const SearchKindInfo &info) { return info.kind == kind; });\n    if (res != std::end(searchKinds)) {\n        return *res;\n    }\n    return searchKinds[1];\n}\n\nSearchModel::SearchModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint SearchModel::rowCount(const QModelIndex &) const\n{\n    return search.count();\n}\n\nint SearchModel::columnCount(const QModelIndex &) const\n{\n    return Columns::COUNT;\n}\n\nQVariant SearchModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= search.count())\n        return QVariant();\n\n    const SearchDescription &exp = search.at(index.row());\n\n    switch (role) {\n    case Qt::FontRole: {\n        switch (index.column()) {\n        case CODE:\n            return QFont(\"Inconsolata\");\n        case DATA:\n            return QFont(\"Inconsolata\");\n        default:\n            return QVariant();\n        }\n    }\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case OFFSET:\n            return RzAddressString(exp.offset);\n        case SIZE:\n            return RzSizeString(exp.size);\n        case CODE:\n            return exp.code;\n        case DATA:\n            return exp.data;\n        case COMMENT: {\n            return exp.detail;\n        }\n        default:\n            return QVariant();\n        }\n    case Qt::ToolTipRole: {\n\n        QString previewContent = QString();\n        // if result is CODE, show disassembly\n        if (!exp.code.isEmpty()) {\n            previewContent =\n                    Core()->getDisassemblyPreview(exp.offset, kMaxTooltipDisasmPreviewLines)\n                            .join(\"<br>\");\n            // if result is DATA or Disassembly is N/A\n        } else if (!exp.data.isEmpty() || previewContent.isEmpty()) {\n            previewContent = Core()->getHexdumpPreview(exp.offset, kMaxTooltipHexdumpBytes);\n        }\n\n        const QFont &fnt = Config()->getBaseFont();\n        QFontMetrics fm { fnt };\n\n        QString toolTipContent =\n                QString(\"<html><div style=\\\"font-family: %1; font-size: %2pt; white-space: \"\n                        \"nowrap;\\\">\")\n                        .arg(fnt.family())\n                        .arg(qMax(6, fnt.pointSize() - 1)); // slightly decrease font size, to keep\n                                                            // more text in the same box\n\n        toolTipContent +=\n                tr(\"<div style=\\\"margin-bottom: 10px;\\\"><strong>Preview</strong>:<br>%1</div>\")\n                        .arg(previewContent);\n\n        toolTipContent += \"</div></html>\";\n        return toolTipContent;\n    }\n    case SearchDescriptionRole:\n        return QVariant::fromValue(exp);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant SearchModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case SIZE:\n            return tr(\"Size\");\n        case OFFSET:\n            return tr(\"Offset\");\n        case CODE:\n            return tr(\"Code\");\n        case DATA:\n            return tr(\"Data\");\n        case COMMENT:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA SearchModel::address(const QModelIndex &index) const\n{\n    const SearchDescription &exp = search.at(index.row());\n    return exp.offset;\n}\n\nSearchSortFilterProxyModel::SearchSortFilterProxyModel(SearchModel *source_model, QObject *parent)\n    : AddressableFilterProxyModel(source_model, parent)\n{\n}\n\nbool SearchSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    SearchDescription search =\n            index.data(SearchModel::SearchDescriptionRole).value<SearchDescription>();\n    return qhelpers::filterStringContains(search.code, this);\n}\n\nbool SearchSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    SearchDescription left_search =\n            left.data(SearchModel::SearchDescriptionRole).value<SearchDescription>();\n    SearchDescription right_search =\n            right.data(SearchModel::SearchDescriptionRole).value<SearchDescription>();\n\n    switch (left.column()) {\n    case SearchModel::SIZE:\n        return left_search.size < right_search.size;\n    case SearchModel::OFFSET:\n        return left_search.offset < right_search.offset;\n    case SearchModel::CODE:\n        return left_search.code < right_search.code;\n    case SearchModel::DATA:\n        return left_search.data < right_search.data;\n    case SearchModel::COMMENT:\n        return left_search.detail < right_search.detail;\n    default:\n        break;\n    }\n\n    return left_search.offset < right_search.offset;\n}\n\nSearchWidget::SearchWidget(MainWindow *main) : CutterDockWidget(main), ui(new Ui::SearchWidget)\n{\n    ui->setupUi(this);\n    setStyleSheet(QString(\"QToolTip { max-width: %1px; opacity: 230; }\").arg(kMaxTooltipWidth));\n\n    updateSearchBoundaries();\n\n    search_model = new SearchModel(this);\n    search_proxy_model = new SearchSortFilterProxyModel(search_model, this);\n    ui->searchTreeView->setModel(search_proxy_model);\n    ui->searchTreeView->setMainWindow(main);\n    ui->searchTreeView->sortByColumn(SearchModel::OFFSET, Qt::AscendingOrder);\n\n    setScrollMode();\n\n    connect(Core(), &CutterCore::toggleDebugView, this, &SearchWidget::updateSearchBoundaries);\n    connect(Core(), &CutterCore::refreshAll, this, &SearchWidget::refreshSearchspaces);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(search_model, SearchModel::COMMENT); });\n\n    connect(ui->filterLineEdit, &QLineEdit::returnPressed, this, &SearchWidget::runSearch);\n    connect(ui->searchButton, &QAbstractButton::clicked, this, &SearchWidget::runSearch);\n\n    connect(ui->searchspaceCombo,\n            static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,\n            [this](int index) { updatePlaceholderText(index); });\n\n    updateColors();\n    connect(Config(), &Configuration::colorsUpdated, this, &SearchWidget::updateColors);\n}\n\nSearchWidget::~SearchWidget() {}\n\nvoid SearchWidget::updateSearchBoundaries()\n{\n    QVector<std::pair<QString, const char *>> boundaries;\n\n    if (Core()->currentlyDebugging && !Core()->currentlyEmulating) {\n        boundaries = searchBoundariesDebug;\n    } else {\n        boundaries = searchBoundaries;\n    }\n\n    ui->searchInCombo->setCurrentIndex(ui->searchInCombo->findData(boundaries[0].first));\n\n    ui->searchInCombo->blockSignals(true);\n    ui->searchInCombo->clear();\n    for (auto item : boundaries) {\n        ui->searchInCombo->addItem(tr(item.second), item.first);\n    }\n    ui->searchInCombo->blockSignals(false);\n\n    ui->filterLineEdit->clear();\n}\n\nvoid SearchWidget::searchChanged()\n{\n    refreshSearchspaces();\n}\n\nvoid SearchWidget::refreshSearchspaces()\n{\n    int cur_idx = ui->searchspaceCombo->currentIndex();\n    if (cur_idx < 0)\n        cur_idx = 0;\n\n    ui->searchspaceCombo->clear();\n    for (auto &kind : searchKinds) {\n        ui->searchspaceCombo->addItem(tr(kind.name), static_cast<int>(kind.kind));\n    }\n\n    if (cur_idx > 0)\n        ui->searchspaceCombo->setCurrentIndex(cur_idx);\n\n    refreshSearch();\n}\n\nvoid SearchWidget::runSearch()\n{\n    disableSearch();\n    refreshSearch();\n    checkSearchResultEmpty();\n    enableSearch();\n}\n\nvoid SearchWidget::refreshSearch()\n{\n    QString searchFor = ui->filterLineEdit->text();\n    auto searchSpace = static_cast<SearchKind>(ui->searchspaceCombo->currentData().toInt());\n    QString searchIn = ui->searchInCombo->currentData().toString();\n\n    search_model->beginResetModel();\n    search_model->search = Core()->getAllSearch(searchFor, searchSpace, searchIn);\n    search_model->endResetModel();\n\n    qhelpers::adjustColumns(ui->searchTreeView, 3, 0);\n}\n\n// No Results Found information message when search returns empty\n// Called by &QShortcut::activated and &QAbstractButton::clicked signals\nvoid SearchWidget::checkSearchResultEmpty()\n{\n    if (!search_model->search.isEmpty())\n        return;\n\n    QString searchFor = ui->filterLineEdit->text();\n    auto searchSpace = static_cast<SearchKind>(ui->searchspaceCombo->currentData().toInt());\n    if (searchFor.isEmpty() && !searchKindInfo(searchSpace).noInput) {\n        return;\n    }\n    QString noResultsMessage = \"<b>\";\n    noResultsMessage.append(tr(\"No results found for:\"));\n    noResultsMessage.append(\"</b><br>\");\n    if (searchFor.isEmpty()) {\n        noResultsMessage.append(ui->searchspaceCombo->currentText().toHtmlEscaped());\n    } else {\n        noResultsMessage.append(ui->filterLineEdit->text().toHtmlEscaped());\n    }\n\n    QMessageBox::information(this, tr(\"No Results Found\"), noResultsMessage);\n}\n\nvoid SearchWidget::setScrollMode()\n{\n    qhelpers::setVerticalScrollMode(ui->searchTreeView);\n}\n\nvoid SearchWidget::updatePlaceholderText(int)\n{\n    // ensure we grab the correct kind.\n    auto kind = static_cast<SearchKind>(ui->searchspaceCombo->currentData().toInt());\n    auto info = searchKindInfo(kind);\n    if (info.textHint) {\n        ui->filterLineEdit->setPlaceholderText(tr(info.textHint));\n    } else {\n        ui->filterLineEdit->setPlaceholderText(\"\");\n    }\n    ui->filterLineEdit->setDisabled(info.noInput);\n    if (info.noInput) {\n        ui->filterLineEdit->clear();\n    }\n}\n\nvoid SearchWidget::disableSearch()\n{\n    ui->searchButton->setEnabled(false);\n    ui->searchButton->setText(tr(\"Searching...\"));\n    qApp->processEvents();\n}\n\nvoid SearchWidget::enableSearch()\n{\n    ui->searchButton->setEnabled(true);\n    ui->searchButton->setText(tr(\"Search\"));\n}\n\nvoid SearchWidget::updateColors()\n{\n    ui->searchTreeView->setStyleSheet(DisassemblyPreview::getToolTipStyleSheet());\n}\n"
  },
  {
    "path": "src/widgets/SearchWidget.h",
    "content": "#ifndef SEARCHWIDGET_H\n#define SEARCHWIDGET_H\n\n#include <memory>\n\n#include <QAbstractItemModel>\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"AddressableItemList.h\"\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass SearchWidget;\n\nclass SearchModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend SearchWidget;\n\nprivate:\n    QList<SearchDescription> search;\n\npublic:\n    enum Columns { OFFSET = 0, SIZE, CODE, DATA, COMMENT, COUNT };\n    static const int SearchDescriptionRole = Qt::UserRole;\n\n    SearchModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n};\n\nclass SearchSortFilterProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    SearchSortFilterProxyModel(SearchModel *source_model, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nnamespace Ui {\nclass SearchWidget;\n}\n\nclass SearchWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit SearchWidget(MainWindow *main);\n    ~SearchWidget();\n\nprivate slots:\n    void searchChanged();\n    void updateSearchBoundaries();\n    void refreshSearchspaces();\n    void runSearch();\n    void updateColors();\n\nprivate:\n    std::unique_ptr<Ui::SearchWidget> ui;\n\n    SearchModel *search_model;\n    SearchSortFilterProxyModel *search_proxy_model;\n\n    void refreshSearch();\n    void checkSearchResultEmpty();\n    void enableSearch();\n    void disableSearch();\n    void setScrollMode();\n    void updatePlaceholderText(int index);\n};\n\n#endif // SEARCHWIDGET_H\n"
  },
  {
    "path": "src/widgets/SearchWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SearchWidget</class>\n <widget class=\"QDockWidget\" name=\"SearchWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>548</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Search</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"AddressableItemList&lt;&gt;\" name=\"searchTreeView\">\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"dragEnabled\">\n       <bool>true</bool>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n      <property name=\"animated\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_17\">\n      <property name=\"spacing\">\n       <number>10</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLineEdit\" name=\"filterLineEdit\">\n        <property name=\"text\">\n         <string/>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QPushButton\" name=\"searchButton\">\n        <property name=\"text\">\n         <string>Search</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"searchspaceLabel\">\n        <property name=\"text\">\n         <string>Search for:</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QComboBox\" name=\"searchspaceCombo\"/>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"searchInLabel\">\n        <property name=\"text\">\n         <string>Search in:</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QComboBox\" name=\"searchInCombo\">\n        <property name=\"maximumSize\">\n         <size>\n          <width>16777215</width>\n          <height>300</height>\n         </size>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>AddressableItemList&lt;&gt;</class>\n   <extends>QTreeView</extends>\n   <header>widgets/AddressableItemList.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/SearchableDockWidget.cpp",
    "content": "#include \"SearchableDockWidget.h\"\n#include \"SearchBarWidget.h\"\n\n#include <QScrollBar>\n#include <QAbstractScrollArea>\n#include <QTimer>\n\nnamespace {\nconstexpr int hPadding = 7;\nconstexpr int vPadding = 4;\n};\n\nSearchableDockWidget::SearchableDockWidget(MainWindow *parent)\n    : CutterDockWidget(parent), m_searchBar(new SearchBarWidget(this))\n{\n    CutterSearchableHelper::setupConnections(this, m_searchBar);\n}\n\nvoid SearchableDockWidget::resizeEvent(QResizeEvent *event)\n{\n    CutterDockWidget::resizeEvent(event);\n    updateSearchBarPosition();\n}\n\nvoid SearchableDockWidget::updateSearchBarPosition()\n{\n    CutterSearchableHelper::positionSearchBar(this, m_searchBar, searchableArea(), searchHPadding(),\n                                              searchVPadding());\n}\n\nint SearchableDockWidget::searchHPadding() const\n{\n    return hPadding;\n}\n\nint SearchableDockWidget::searchVPadding() const\n{\n    return vPadding;\n}\n"
  },
  {
    "path": "src/widgets/SearchableDockWidget.h",
    "content": "#ifndef SEARCHABLEDOCKWIDGET_H\n#define SEARCHABLEDOCKWIDGET_H\n\n#include \"CutterDockWidget.h\"\n#include \"CutterSearchable.h\"\n\nclass SearchBarWidget;\n\n/**\n * @brief A dock widget that includes a search bar\n */\nclass CUTTER_EXPORT SearchableDockWidget : public CutterDockWidget, public CutterSearchable\n{\n    Q_OBJECT\n\npublic:\n    explicit SearchableDockWidget(MainWindow *parent);\n\n    void updateSearchBarPosition();\n\nprotected:\n    SearchBarWidget *m_searchBar;\n\n    void resizeEvent(QResizeEvent *event) override;\n    int searchHPadding() const override;\n    int searchVPadding() const override;\n\nprivate:\n};\n\n#endif // SEARCHABLEDOCKWIDGET_H\n"
  },
  {
    "path": "src/widgets/SearchableTextEdit.cpp",
    "content": "#include \"SearchableTextEdit.h\"\n#include \"CutterSearchable.h\"\n#include \"Configuration.h\"\n\n#include <QRegularExpression>\n\nSearchableTextEdit::SearchableTextEdit(QWidget *parent) : QPlainTextEdit(parent) {}\n\nQPair<int, int> SearchableTextEdit::search(const QString &string, int options)\n{\n    m_searchResults.clear();\n    m_currentIndex = -1;\n    m_highlightMatches = options & SearchOption::HighlightMatches;\n\n    if (string.isEmpty()) {\n        clearSearch();\n        return { 0, 0 };\n    }\n\n    QTextDocument::FindFlags flags = {};\n    flags = options & SearchOption::CaseSensitive ? (flags | QTextDocument::FindCaseSensitively)\n                                                  : flags;\n    flags = options & SearchOption::WholeWords ? (flags | QTextDocument::FindWholeWords) : flags;\n\n    // we will search relative to this cursor\n    QTextCursor originalCursor = this->textCursor();\n\n    // start from the top\n    QTextDocument *doc = this->document();\n    QTextCursor cursor(doc);\n\n    if (options & SearchOption::RegExp) {\n        const QRegularExpression regex(string);\n        while (!cursor.isNull() && !cursor.atEnd()) {\n            cursor = doc->find(regex, cursor, flags);\n\n            if (cursor.hasSelection()) {\n                handleMatch(cursor, originalCursor);\n            } else {\n                cursor.movePosition(QTextCursor::NextCharacter); // avoid infinite loop\n            }\n        }\n    } else {\n        while (!cursor.isNull() && !cursor.atEnd()) {\n            cursor = doc->find(string, cursor, flags);\n            handleMatch(cursor, originalCursor);\n        }\n    }\n\n    // wrap around\n    if (m_currentIndex < 0 && !m_searchResults.isEmpty()) {\n        m_currentIndex = 0;\n    }\n\n    scrollToCurrentIndex();\n    highlightMatches();\n\n    return { m_currentIndex, m_searchResults.size() };\n}\n\nvoid SearchableTextEdit::handleMatch(const QTextCursor &currentCursor,\n                                     const QTextCursor &originalCursor)\n{\n    if (!currentCursor.isNull()) {\n\n        if (m_currentIndex < 0 && currentCursor.selectionEnd() >= originalCursor.selectionStart()) {\n            m_currentIndex = m_searchResults.size();\n        }\n\n        int len = currentCursor.selectionEnd() - currentCursor.selectionStart();\n        m_searchResults.append(SearchResult { currentCursor.selectionStart(), len });\n    }\n}\n\nvoid SearchableTextEdit::clearSearch()\n{\n    this->setExtraSelections({});\n    m_searchResults.clear();\n}\n\nint SearchableTextEdit::findNext()\n{\n    if (m_searchResults.isEmpty()) {\n        return 0;\n    }\n\n    m_currentIndex = (m_currentIndex + 1) % m_searchResults.size();\n\n    scrollToCurrentIndex();\n    highlightMatches();\n\n    return m_currentIndex;\n}\n\nint SearchableTextEdit::findPrev()\n{\n    if (m_searchResults.isEmpty()) {\n        return 0;\n    }\n\n    int count = m_searchResults.size();\n    m_currentIndex = (m_currentIndex - 1 + count) % count;\n    scrollToCurrentIndex();\n    highlightMatches();\n\n    return m_currentIndex;\n}\n\nint SearchableTextEdit::findLast()\n{\n    if (m_searchResults.isEmpty()) {\n        return 0;\n    }\n\n    m_currentIndex = m_searchResults.size() - 1;\n\n    scrollToCurrentIndex();\n    highlightMatches();\n\n    return m_currentIndex;\n}\n\nvoid SearchableTextEdit::resizeEvent(QResizeEvent *event)\n{\n    highlightMatches();\n    QPlainTextEdit::resizeEvent(event);\n}\n\nvoid SearchableTextEdit::highlightMatches()\n{\n    if (m_currentIndex < 0 || m_currentIndex >= m_searchResults.size()) {\n        this->setExtraSelections({});\n        return;\n    }\n\n    QTextCursor cursor(this->document());\n\n    if (!m_highlightMatches) {\n        mapCursorToResult(cursor, m_searchResults[m_currentIndex]);\n        QTextEdit::ExtraSelection selection;\n        selection.format.setBackground(ConfigColor(\"searchCurrent\"));\n        selection.cursor = cursor;\n        this->setExtraSelections({ selection });\n        return;\n    }\n\n    QPoint startPoint = QPoint(0, 0);\n    int startPos = this->cursorForPosition(startPoint).position();\n\n    QPoint endPoint = QPoint(this->geometry().width(), this->geometry().height());\n    auto endCursor = this->cursorForPosition(endPoint);\n    int endPos = endCursor.position();\n    endCursor.movePosition(QTextCursor::End);\n    int maxPos = endCursor.position();\n\n    QList<QTextEdit::ExtraSelection> selections;\n    for (int i = 0; i < m_searchResults.size(); ++i) {\n        const auto res = m_searchResults[i];\n        mapCursorToResult(cursor, m_searchResults[i]);\n        int sPos = std::max(startPos - res.length, 0);\n        int ePos = std::min(endPos + res.length, maxPos);\n        if (i == m_currentIndex) {\n            QTextEdit::ExtraSelection selection;\n            selection.format.setBackground(ConfigColor(\"searchCurrent\"));\n            selection.cursor = cursor;\n            selections.append(selection);\n        } else if (cursor.selectionStart() >= sPos && cursor.selectionEnd() <= ePos) {\n            // only highlight visible matches\n            QTextEdit::ExtraSelection selection;\n            selection.format.setBackground(ConfigColor(\"searchHighlight\"));\n            selection.cursor = cursor;\n            selections.append(selection);\n        }\n    }\n    this->setExtraSelections(selections);\n}\n\nvoid SearchableTextEdit::scrollToCurrentIndex()\n{\n    if (m_currentIndex < 0 || m_currentIndex >= m_searchResults.size()) {\n        return;\n    }\n\n    QTextCursor scrollCursor(this->document());\n    mapCursorToResult(scrollCursor, m_searchResults[m_currentIndex]);\n    scrollCursor.setPosition(scrollCursor.selectionStart());\n    scrollCursor.clearSelection();\n    this->setTextCursor(scrollCursor);\n}\n"
  },
  {
    "path": "src/widgets/SearchableTextEdit.h",
    "content": "#ifndef SEARCHABLETEXTEDIT_H\n#define SEARCHABLETEXTEDIT_H\n\n#include <QPlainTextEdit>\n\n/**\n * @brief A text editor that adds search functionality\n * Use this instead of QPlainTextEdit in widgets that have a search bar\n */\nclass SearchableTextEdit : public QPlainTextEdit\n{\n    Q_OBJECT\n\npublic:\n    explicit SearchableTextEdit(QWidget *parent = nullptr);\n\n    /**\n     * @brief Search for a given string or regex in the document, selects the\n     * first match on or after the cursor\n     * @param string String or regex to search\n     * @param flags Find flags\n     * @param isRegex True if regex, false otherwise\n     * @return <index, count> where index is currently selected index and count is total number of\n     * matches\n     */\n    QPair<int, int> search(const QString &string, int options);\n\n    /** returns new index */\n    int findNext();\n    int findPrev();\n    int findLast();\n\n    void clearSearch();\n\nprotected:\n    void resizeEvent(QResizeEvent *event) override;\n\npublic slots:\n    void highlightMatches();\n\nprivate:\n    struct SearchResult\n    {\n        int position;\n        int length;\n    };\n\n    int m_currentIndex;\n    QList<SearchResult> m_searchResults;\n    bool m_highlightMatches;\n\n    void handleMatch(const QTextCursor &currentCursor, const QTextCursor &originalCursor);\n    void scrollToCurrentIndex();\n\n    inline void mapCursorToResult(QTextCursor &cursor, const SearchResult result)\n    {\n        cursor.setPosition(result.position);\n        cursor.setPosition(result.position + result.length, QTextCursor::KeepAnchor);\n    }\n};\n\n#endif // SEARCHABLETEXTEDIT_H\n"
  },
  {
    "path": "src/widgets/SectionsWidget.cpp",
    "content": "#include \"SectionsWidget.h\"\n#include \"QuickFilterView.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"common/Configuration.h\"\n#include \"ui_ListDockWidget.h\"\n\n#include <QGraphicsSceneMouseEvent>\n#include <QGraphicsTextItem>\n#include <QGraphicsView>\n#include <QHBoxLayout>\n#include <QVBoxLayout>\n#include <QShortcut>\n#include <QToolTip>\n\nSectionsModel::SectionsModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint SectionsModel::rowCount(const QModelIndex &) const\n{\n    return sections.count();\n}\n\nint SectionsModel::columnCount(const QModelIndex &) const\n{\n    return SectionsModel::ColumnCount;\n}\n\nQVariant SectionsModel::data(const QModelIndex &index, int role) const\n{\n    // TODO: create unique colors, e. g. use HSV color space and rotate in H for 360/size\n    static const QList<QColor> colors = {\n        QColor(\"#1ABC9C\"), // TURQUOISE\n        QColor(\"#2ECC71\"), // EMERALD\n        QColor(\"#3498DB\"), // PETER RIVER\n        QColor(\"#9B59B6\"), // AMETHYST\n        QColor(\"#34495E\"), // WET ASPHALT\n        QColor(\"#F1C40F\"), // SUN FLOWER\n        QColor(\"#E67E22\"), // CARROT\n        QColor(\"#E74C3C\"), // ALIZARIN\n        QColor(\"#ECF0F1\"), // CLOUDS\n        QColor(\"#BDC3C7\"), // SILVER\n        QColor(\"#95A5A6\") // COBCRETE\n    };\n\n    if (index.row() >= sections.count()) {\n        return QVariant();\n    }\n\n    const SectionDescription &section = sections.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case SectionsModel::NameColumn:\n            return section.name;\n        case SectionsModel::SizeColumn:\n            return RzSizeString(section.size);\n        case SectionsModel::AddressColumn:\n            return RzAddressString(section.vaddr);\n        case SectionsModel::EndAddressColumn:\n            return RzAddressString(section.vaddr + section.vsize);\n        case SectionsModel::VirtualSizeColumn:\n            return RzSizeString(section.vsize);\n        case SectionsModel::PermissionsColumn:\n            return section.perm;\n        case SectionsModel::EntropyColumn:\n            return section.entropy;\n        case SectionsModel::CommentColumn:\n            return Core()->getCommentAt(section.vaddr);\n        default:\n            return QVariant();\n        }\n    case Qt::DecorationRole:\n        if (index.column() == 0)\n            return colors[index.row() % colors.size()];\n        return QVariant();\n    case SectionsModel::SectionDescriptionRole:\n        return QVariant::fromValue(section);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant SectionsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case SectionsModel::NameColumn:\n            return tr(\"Name\");\n        case SectionsModel::SizeColumn:\n            return tr(\"Size\");\n        case SectionsModel::AddressColumn:\n            return tr(\"Address\");\n        case SectionsModel::EndAddressColumn:\n            return tr(\"End Address\");\n        case SectionsModel::VirtualSizeColumn:\n            return tr(\"Virtual Size\");\n        case SectionsModel::PermissionsColumn:\n            return tr(\"Permissions\");\n        case SectionsModel::EntropyColumn:\n            return tr(\"Entropy\");\n        case SectionsModel::CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA SectionsModel::address(const QModelIndex &index) const\n{\n    const SectionDescription &section = sections.at(index.row());\n    return section.vaddr;\n}\n\nQString SectionsModel::name(const QModelIndex &index) const\n{\n    const SectionDescription &section = sections.at(index.row());\n    return section.name;\n}\n\nSectionsProxyModel::SectionsProxyModel(SectionsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    auto leftSection = left.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();\n    auto rightSection =\n            right.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();\n\n    switch (left.column()) {\n    default:\n    case SectionsModel::NameColumn:\n        return leftSection.name < rightSection.name;\n    case SectionsModel::SizeColumn:\n        return leftSection.size < rightSection.size;\n    case SectionsModel::AddressColumn:\n    case SectionsModel::EndAddressColumn:\n        if (leftSection.vaddr != rightSection.vaddr) {\n            return leftSection.vaddr < rightSection.vaddr;\n        }\n        return leftSection.vsize < rightSection.vsize;\n    case SectionsModel::VirtualSizeColumn:\n        return leftSection.vsize < rightSection.vsize;\n    case SectionsModel::PermissionsColumn:\n        return leftSection.perm < rightSection.perm;\n    case SectionsModel::EntropyColumn:\n        return leftSection.entropy < rightSection.entropy;\n    case SectionsModel::CommentColumn:\n        return Core()->getCommentAt(leftSection.vaddr) < Core()->getCommentAt(rightSection.vaddr);\n    }\n}\n\nSectionsWidget::SectionsWidget(MainWindow *main) : ListDockWidget(main)\n{\n    setObjectName(\"SectionsWidget\");\n    setWindowTitle(tr(\"Sections\"));\n    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);\n\n    sectionsRefreshDeferrer = createRefreshDeferrer([this]() { refreshSections(); });\n    dockRefreshDeferrer = createRefreshDeferrer([this]() { refreshDocks(); });\n\n    initSectionsTable();\n    initQuickFilter();\n    initAddrMapDocks();\n    initConnects();\n}\n\nSectionsWidget::~SectionsWidget() = default;\n\nvoid SectionsWidget::initSectionsTable()\n{\n    sectionsModel = new SectionsModel(this);\n    proxyModel = new SectionsProxyModel(sectionsModel, this);\n    setModels(proxyModel);\n\n    ui->treeView->sortByColumn(SectionsModel::AddressColumn, Qt::AscendingOrder);\n}\n\nvoid SectionsWidget::initQuickFilter()\n{\n    ui->quickFilterView->closeFilter();\n}\n\nvoid SectionsWidget::initAddrMapDocks()\n{\n    QVBoxLayout *layout = ui->verticalLayout;\n    showCount(false);\n\n    rawAddrDock = new RawAddrDock(sectionsModel, this);\n    virtualAddrDock = new VirtualAddrDock(sectionsModel, this);\n    addrDockWidget = new QWidget();\n    QHBoxLayout *addrDockLayout = new QHBoxLayout();\n    addrDockLayout->addWidget(rawAddrDock);\n    addrDockLayout->addWidget(virtualAddrDock);\n    addrDockWidget->setLayout(addrDockLayout);\n    layout->addWidget(addrDockWidget);\n\n    QPixmap map(\":/img/icons/previous.svg\");\n    QTransform transform;\n    transform = transform.rotate(90);\n    map = map.transformed(transform);\n    QIcon icon;\n    icon.addPixmap(map);\n\n    toggleButton = new QToolButton;\n    toggleButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n    toggleButton->setFixedHeight(30);\n    toggleButton->setIcon(icon);\n    toggleButton->setIconSize(QSize(16, 12));\n    toggleButton->setAutoRaise(true);\n    toggleButton->setArrowType(Qt::NoArrow);\n    toggleButton->hide();\n    layout->addWidget(toggleButton);\n}\n\nvoid SectionsWidget::initConnects()\n{\n    connect(Core(), &CutterCore::refreshAll, this, &SectionsWidget::refreshSections);\n    connect(Core(), &CutterCore::codeRebased, this, &SectionsWidget::refreshSections);\n    connect(this, &QDockWidget::visibilityChanged, this, [=](bool visibility) {\n        if (visibility) {\n            refreshSections();\n        }\n    });\n    connect(Core(), &CutterCore::seekChanged, this, &SectionsWidget::refreshDocks);\n    connect(Config(), &Configuration::colorsUpdated, this, &SectionsWidget::refreshSections);\n    connect(toggleButton, &QToolButton::clicked, this, [=] {\n        toggleButton->hide();\n        addrDockWidget->show();\n        virtualAddrDock->show();\n    });\n    connect(virtualAddrDock, &QDockWidget::visibilityChanged, this, [=](bool visibility) {\n        if (!visibility) {\n            updateToggle();\n        }\n    });\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(sectionsModel, SectionsModel::CommentColumn); });\n}\n\nvoid SectionsWidget::refreshSections()\n{\n    if (!sectionsRefreshDeferrer->attemptRefresh(nullptr) || Core()->isDebugTaskInProgress()) {\n        return;\n    }\n    sectionsModel->beginResetModel();\n    sectionsModel->sections = Core()->getAllSections();\n    sectionsModel->endResetModel();\n    qhelpers::adjustColumns(ui->treeView, SectionsModel::ColumnCount, 0);\n    refreshDocks();\n}\n\nvoid SectionsWidget::refreshDocks()\n{\n    if (!dockRefreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n    rawAddrDock->updateDock();\n    virtualAddrDock->updateDock();\n    drawIndicatorOnAddrDocks();\n}\n\nvoid SectionsWidget::drawIndicatorOnAddrDocks()\n{\n    RVA offset = Core()->getOffset();\n    for (int i = 0; i != virtualAddrDock->proxyModel->rowCount(); i++) {\n        QModelIndex idx = virtualAddrDock->proxyModel->index(i, 0);\n        RVA vaddr =\n                idx.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>().vaddr;\n        int vsize =\n                idx.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>().vsize;\n        RVA end = vaddr + vsize;\n        if (offset < end) {\n            QString name = idx.data(SectionsModel::SectionDescriptionRole)\n                                   .value<SectionDescription>()\n                                   .name;\n            float ratio = 0;\n            if (vsize > 0 && offset > vaddr) {\n                ratio = (float)(offset - vaddr) / (float)vsize;\n            }\n            rawAddrDock->drawIndicator(name, ratio);\n            virtualAddrDock->drawIndicator(name, ratio);\n            return;\n        }\n    }\n}\n\nvoid SectionsWidget::resizeEvent(QResizeEvent *event)\n{\n    CutterDockWidget::resizeEvent(event);\n    refreshDocks();\n}\n\nvoid SectionsWidget::updateToggle()\n{\n    if (!virtualAddrDock->isVisible()) {\n        addrDockWidget->hide();\n        toggleButton->show();\n    }\n}\n\nAbstractAddrDock::AbstractAddrDock(SectionsModel *model, QWidget *parent)\n    : QDockWidget(parent),\n      addrDockScene(new AddrDockScene(this)),\n      graphicsView(new QGraphicsView(this))\n{\n    graphicsView->setScene(addrDockScene);\n    setWidget(graphicsView);\n    setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);\n    proxyModel = new SectionsProxyModel(model, this);\n\n    setWidget(graphicsView);\n\n    indicatorHeight = 5;\n    indicatorParamPosY = 20;\n    heightThreshold = 30;\n    heightDivisor = 1000;\n    rectOffset = 100;\n    rectWidthMin = 80;\n    rectWidthMax = 400;\n    indicatorColor = ConfigColor(\"gui.navbar.seek\");\n    textColor = ConfigColor(\"gui.dataoffset\");\n}\n\nAbstractAddrDock::~AbstractAddrDock() {}\n\nvoid AbstractAddrDock::updateDock()\n{\n    addrDockScene->clear();\n\n    const QBrush bg = QBrush(ConfigColor(\"gui.background\"));\n    addrDockScene->setBackgroundBrush(bg);\n\n    textColor = ConfigColor(\"gui.dataoffset\");\n\n    int y = 0;\n    int validMinSize = getValidMinSize();\n    int rectWidth = getRectWidth();\n    proxyModel->sort(SectionsModel::AddressColumn, Qt::AscendingOrder);\n    for (int i = 0; i < proxyModel->rowCount(); ++i) {\n        QModelIndex idx = proxyModel->index(i, 0);\n        auto desc = idx.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();\n\n        QString name = desc.name;\n\n        addrDockScene->seekAddrMap[name] = desc.vaddr;\n        addrDockScene->seekAddrSizeMap[name] = desc.vsize;\n\n        RVA addr = getAddressOfSection(desc);\n        RVA size = getSizeOfSection(desc);\n        addrDockScene->nameAddrMap[name] = addr;\n        addrDockScene->nameAddrSizeMap[name] = size;\n\n        int drawSize = getAdjustedSize(size, validMinSize);\n\n        QGraphicsRectItem *rect = new QGraphicsRectItem(rectOffset, y, rectWidth, drawSize);\n        rect->setBrush(QBrush(idx.data(Qt::DecorationRole).value<QColor>()));\n        addrDockScene->addItem(rect);\n\n        addTextItem(textColor, QPoint(0, y), RzAddressString(addr));\n        addTextItem(textColor, QPoint(rectOffset, y), RzSizeString(size));\n        addTextItem(textColor, QPoint(rectOffset + rectWidth, y), name);\n\n        addrDockScene->namePosYMap[name] = y;\n        addrDockScene->nameHeightMap[name] = drawSize;\n\n        y += drawSize;\n    }\n\n    graphicsView->setSceneRect(addrDockScene->itemsBoundingRect());\n}\n\nvoid AbstractAddrDock::addTextItem(QColor color, QPoint pos, QString string)\n{\n    QGraphicsTextItem *text = new QGraphicsTextItem;\n    text->setDefaultTextColor(color);\n    text->setPos(pos);\n    text->setPlainText(string);\n    addrDockScene->addItem(text);\n}\n\nint AbstractAddrDock::getAdjustedSize(int size, int validMinSize)\n{\n    if (size == 0) {\n        return size;\n    }\n    if (size == validMinSize) {\n        return heightThreshold;\n    }\n    float r = (float)size / (float)validMinSize;\n    r /= heightDivisor;\n    r += 1;\n    return heightThreshold * r;\n}\n\nint AbstractAddrDock::getRectWidth()\n{\n    return qBound(rectWidthMin, width() - 300, rectWidthMax);\n}\n\nint AbstractAddrDock::getIndicatorWidth()\n{\n    return getRectWidth() + 200;\n}\n\nint AbstractAddrDock::getValidMinSize()\n{\n    proxyModel->sort(SectionsModel::SizeColumn, Qt::AscendingOrder);\n    for (int i = 0; i < proxyModel->rowCount(); i++) {\n        QModelIndex idx = proxyModel->index(i, 0);\n        int size = getSizeOfSection(\n                idx.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>());\n        if (size > 0) {\n            return size;\n        }\n    }\n    return 0;\n}\n\nvoid AbstractAddrDock::drawIndicator(QString name, float ratio)\n{\n    RVA offset = Core()->getOffset();\n    float padding = addrDockScene->nameHeightMap[name] * ratio;\n    int y = addrDockScene->namePosYMap[name] + (int)padding;\n    QColor color = indicatorColor;\n    QGraphicsRectItem *indicator =\n            new QGraphicsRectItem(QRectF(0, y, getIndicatorWidth(), indicatorHeight));\n    indicator->setBrush(QBrush(color));\n    addrDockScene->addItem(indicator);\n\n    if (!addrDockScene->disableCenterOn) {\n        graphicsView->centerOn(indicator);\n    }\n\n    addTextItem(color, QPoint(rectOffset + getRectWidth(), y - indicatorParamPosY), name);\n    addTextItem(color, QPoint(0, y - indicatorParamPosY), QString(\"0x%1\").arg(offset, 0, 16));\n}\n\nAddrDockScene::AddrDockScene(QWidget *parent) : QGraphicsScene(parent)\n{\n    disableCenterOn = false;\n}\n\nAddrDockScene::~AddrDockScene() {}\n\nvoid AddrDockScene::mousePressEvent(QGraphicsSceneMouseEvent *event)\n{\n    RVA addr = getAddrFromPos((int)event->scenePos().y(), false);\n    if (addr != RVA_INVALID) {\n        QToolTip::showText(event->screenPos(), RzAddressString(addr));\n        if (event->buttons() & Qt::LeftButton) {\n            RVA seekAddr = getAddrFromPos((int)event->scenePos().y(), true);\n            disableCenterOn = true;\n            Core()->seekAndShow(seekAddr);\n            disableCenterOn = false;\n            return;\n        }\n    } else {\n        QToolTip::hideText();\n    }\n}\n\nvoid AddrDockScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)\n{\n    mousePressEvent(event);\n}\n\nRVA AddrDockScene::getAddrFromPos(int posY, bool seek)\n{\n    QHash<QString, int>::const_iterator it;\n    QHash<QString, RVA> addrMap = seek ? seekAddrMap : nameAddrMap;\n    QHash<QString, RVA> addrSizeMap = seek ? seekAddrSizeMap : nameAddrSizeMap;\n    for (it = namePosYMap.constBegin(); it != namePosYMap.constEnd(); ++it) {\n        QString name = it.key();\n        int y = it.value();\n        int h = nameHeightMap[name];\n        if (posY >= y && y + h >= posY) {\n            if (h == 0) {\n                return addrMap[name];\n            }\n            return addrMap[name] + (float)addrSizeMap[name] * ((float)(posY - y) / (float)h);\n        }\n    }\n    return RVA_INVALID;\n}\n\nRawAddrDock::RawAddrDock(SectionsModel *model, QWidget *parent) : AbstractAddrDock(model, parent)\n{\n    setWindowTitle(tr(\"Raw\"));\n    connect(this, &QDockWidget::featuresChanged, this,\n            [=]() { setFeatures(QDockWidget::NoDockWidgetFeatures); });\n}\n\nVirtualAddrDock::VirtualAddrDock(SectionsModel *model, QWidget *parent)\n    : AbstractAddrDock(model, parent)\n{\n    setWindowTitle(tr(\"Virtual\"));\n    connect(this, &QDockWidget::featuresChanged, this,\n            [=]() { setFeatures(QDockWidget::DockWidgetClosable); });\n}\n\nvoid RawAddrDock::updateDock()\n{\n    AbstractAddrDock::updateDock();\n    setFeatures(QDockWidget::DockWidgetClosable);\n}\n\nvoid VirtualAddrDock::updateDock()\n{\n    AbstractAddrDock::updateDock();\n    setFeatures(QDockWidget::NoDockWidgetFeatures);\n}\n"
  },
  {
    "path": "src/widgets/SectionsWidget.h",
    "content": "#ifndef SECTIONSWIDGET_H\n#define SECTIONSWIDGET_H\n\n#include <memory>\n#include <map>\n\n#include <QtWidgets/QToolButton>\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n#include <QGraphicsScene>\n#include <QLabel>\n#include <QHash>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass QAbstractItemView;\nclass SectionsWidget;\nclass AbstractAddrDock;\nclass AddrDockScene;\nclass QGraphicsSceneMouseEvent;\nclass RawAddrDock;\nclass VirtualAddrDock;\nclass QuickFilterView;\nclass QGraphicsView;\nclass QGraphicsRectItem;\n\nclass SectionsModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend SectionsWidget;\n\nprivate:\n    QList<SectionDescription> sections;\n\npublic:\n    enum Column {\n        NameColumn = 0,\n        SizeColumn,\n        AddressColumn,\n        EndAddressColumn,\n        VirtualSizeColumn,\n        PermissionsColumn,\n        EntropyColumn,\n        CommentColumn,\n        ColumnCount\n    };\n    enum Role { SectionDescriptionRole = Qt::UserRole };\n\n    SectionsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n};\n\nclass SectionsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    SectionsProxyModel(SectionsModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass SectionsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit SectionsWidget(MainWindow *main);\n    ~SectionsWidget();\n\nprivate slots:\n    void refreshSections();\n    void refreshDocks();\n\nprotected:\n    void resizeEvent(QResizeEvent *event) override;\n\nprivate:\n    SectionsModel *sectionsModel;\n    SectionsProxyModel *proxyModel;\n\n    QWidget *addrDockWidget;\n    RawAddrDock *rawAddrDock;\n    VirtualAddrDock *virtualAddrDock;\n    QToolButton *toggleButton;\n\n    /**\n     * RefreshDeferrer for loading the section data\n     */\n    RefreshDeferrer *sectionsRefreshDeferrer;\n\n    /**\n     * RefreshDeferrer for updating the visualization docks\n     */\n    RefreshDeferrer *dockRefreshDeferrer;\n\n    void initSectionsTable();\n    void initQuickFilter();\n    void initConnects();\n    void initAddrMapDocks();\n    void drawIndicatorOnAddrDocks();\n    void updateToggle();\n};\n\nclass AbstractAddrDock : public QDockWidget\n{\n    Q_OBJECT\n\n    friend SectionsWidget;\n\npublic:\n    explicit AbstractAddrDock(SectionsModel *model, QWidget *parent = nullptr);\n    ~AbstractAddrDock();\n\n    virtual void updateDock();\n\nprotected:\n    int indicatorHeight;\n    int indicatorParamPosY;\n    float heightThreshold;\n    float heightDivisor;\n    int rectOffset;\n    int rectWidthMin;\n    int rectWidthMax;\n    QColor indicatorColor;\n    QColor textColor;\n    AddrDockScene *addrDockScene;\n    QGraphicsView *graphicsView;\n    SectionsProxyModel *proxyModel;\n\n    void addTextItem(QColor color, QPoint pos, QString string);\n    int getAdjustedSize(int size, int validMinSize);\n    int getRectWidth();\n    int getIndicatorWidth();\n    int getValidMinSize();\n\n    virtual RVA getSizeOfSection(const SectionDescription &section) = 0;\n    virtual RVA getAddressOfSection(const SectionDescription &section) = 0;\n\nprivate:\n    void drawIndicator(QString name, float ratio);\n};\n\nclass AddrDockScene : public QGraphicsScene\n{\n    Q_OBJECT\n\npublic:\n    explicit AddrDockScene(QWidget *parent = nullptr);\n    ~AddrDockScene();\n\n    bool disableCenterOn;\n\n    QHash<QString, RVA> nameAddrMap;\n    QHash<QString, RVA> nameAddrSizeMap;\n    QHash<QString, RVA> seekAddrMap;\n    QHash<QString, RVA> seekAddrSizeMap;\n    QHash<QString, int> namePosYMap;\n    QHash<QString, int> nameHeightMap;\n\nprotected:\n    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;\n    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;\n\nprivate:\n    RVA getAddrFromPos(int posY, bool seek);\n};\n\nclass RawAddrDock : public AbstractAddrDock\n{\n    Q_OBJECT\n\npublic:\n    explicit RawAddrDock(SectionsModel *model, QWidget *parent = nullptr);\n    ~RawAddrDock() = default;\n\n    void updateDock() override;\n\nprotected:\n    RVA getSizeOfSection(const SectionDescription &section) override { return section.size; };\n    RVA getAddressOfSection(const SectionDescription &section) override { return section.paddr; };\n};\n\nclass VirtualAddrDock : public AbstractAddrDock\n{\n    Q_OBJECT\n\npublic:\n    explicit VirtualAddrDock(SectionsModel *model, QWidget *parent = nullptr);\n    ~VirtualAddrDock() = default;\n\n    void updateDock() override;\n\nprotected:\n    RVA getSizeOfSection(const SectionDescription &section) override { return section.vsize; };\n    RVA getAddressOfSection(const SectionDescription &section) override { return section.vaddr; };\n};\n\n#endif // SECTIONSWIDGET_H\n"
  },
  {
    "path": "src/widgets/SegmentsWidget.cpp",
    "content": "#include \"SegmentsWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"ui_ListDockWidget.h\"\n\n#include <QVBoxLayout>\n#include <QShortcut>\n\nSegmentsModel::SegmentsModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint SegmentsModel::rowCount(const QModelIndex &) const\n{\n    return segments.count();\n}\n\nint SegmentsModel::columnCount(const QModelIndex &) const\n{\n    return SegmentsModel::ColumnCount;\n}\n\nQVariant SegmentsModel::data(const QModelIndex &index, int role) const\n{\n    // TODO: create unique colors, e. g. use HSV color space and rotate in H for 360/size\n    static const QList<QColor> colors = {\n        QColor(\"#1ABC9C\"), // TURQUOISE\n        QColor(\"#2ECC71\"), // EMERALD\n        QColor(\"#3498DB\"), // PETER RIVER\n        QColor(\"#9B59B6\"), // AMETHYST\n        QColor(\"#34495E\"), // WET ASPHALT\n        QColor(\"#F1C40F\"), // SUN FLOWER\n        QColor(\"#E67E22\"), // CARROT\n        QColor(\"#E74C3C\"), // ALIZARIN\n        QColor(\"#ECF0F1\"), // CLOUDS\n        QColor(\"#BDC3C7\"), // SILVER\n        QColor(\"#95A5A6\") // COBCRETE\n    };\n\n    if (index.row() >= segments.count())\n        return QVariant();\n\n    const SegmentDescription &segment = segments.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case SegmentsModel::NameColumn:\n            return segment.name;\n        case SegmentsModel::SizeColumn:\n            return QString::number(segment.size);\n        case SegmentsModel::AddressColumn:\n            return RzAddressString(segment.vaddr);\n        case SegmentsModel::EndAddressColumn:\n            return RzAddressString(segment.vaddr + segment.size);\n        case SegmentsModel::PermColumn:\n            return segment.perm;\n        case SegmentsModel::CommentColumn:\n            return Core()->getCommentAt(segment.vaddr);\n        default:\n            return QVariant();\n        }\n    case Qt::DecorationRole:\n        if (index.column() == 0)\n            return colors[index.row() % colors.size()];\n        return QVariant();\n    case SegmentsModel::SegmentDescriptionRole:\n        return QVariant::fromValue(segment);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant SegmentsModel::headerData(int segment, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (segment) {\n        case SegmentsModel::NameColumn:\n            return tr(\"Name\");\n        case SegmentsModel::SizeColumn:\n            return tr(\"Size\");\n        case SegmentsModel::AddressColumn:\n            return tr(\"Address\");\n        case SegmentsModel::EndAddressColumn:\n            return tr(\"End Address\");\n        case SegmentsModel::PermColumn:\n            return tr(\"Permissions\");\n        case SegmentsModel::CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA SegmentsModel::address(const QModelIndex &index) const\n{\n    const SegmentDescription &segment = segments.at(index.row());\n    return segment.vaddr;\n}\n\nQString SegmentsModel::name(const QModelIndex &index) const\n{\n    const SegmentDescription &segment = segments.at(index.row());\n    return segment.name;\n}\n\nSegmentsProxyModel::SegmentsProxyModel(SegmentsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool SegmentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    auto leftSegment = left.data(SegmentsModel::SegmentDescriptionRole).value<SegmentDescription>();\n    auto rightSegment =\n            right.data(SegmentsModel::SegmentDescriptionRole).value<SegmentDescription>();\n    switch (left.column()) {\n    case SegmentsModel::NameColumn:\n        return leftSegment.name < rightSegment.name;\n    case SegmentsModel::SizeColumn:\n        return leftSegment.size < rightSegment.size;\n    case SegmentsModel::AddressColumn:\n    case SegmentsModel::EndAddressColumn:\n        return leftSegment.vaddr < rightSegment.vaddr;\n    case SegmentsModel::CommentColumn:\n        return Core()->getCommentAt(leftSegment.vaddr) < Core()->getCommentAt(rightSegment.vaddr);\n    default:\n        break;\n    }\n    return false;\n}\n\nSegmentsWidget::SegmentsWidget(MainWindow *main) : ListDockWidget(main)\n{\n    setObjectName(\"SegmentsWidget\");\n    setWindowTitle(tr(\"Segments\"));\n\n    segmentsModel = new SegmentsModel(this);\n    auto proxyModel = new SegmentsProxyModel(segmentsModel, this);\n    setModels(proxyModel);\n\n    ui->treeView->sortByColumn(SegmentsModel::NameColumn, Qt::AscendingOrder);\n\n    ui->quickFilterView->closeFilter();\n    showCount(false);\n\n    connect(Core(), &CutterCore::refreshAll, this, &SegmentsWidget::refreshSegments);\n    connect(Core(), &CutterCore::codeRebased, this, &SegmentsWidget::refreshSegments);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(segmentsModel, SegmentsModel::CommentColumn); });\n}\n\nSegmentsWidget::~SegmentsWidget() {}\n\nvoid SegmentsWidget::refreshSegments()\n{\n    segmentsModel->beginResetModel();\n    segmentsModel->segments = Core()->getAllSegments();\n    segmentsModel->endResetModel();\n\n    qhelpers::adjustColumns(ui->treeView, SegmentsModel::ColumnCount, 0);\n}\n"
  },
  {
    "path": "src/widgets/SegmentsWidget.h",
    "content": "#ifndef SEGMENTSWIDGET_H\n#define SEGMENTSWIDGET_H\n\n#include <memory>\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass QAbstractItemView;\nclass SegmentsWidget;\n\nclass SegmentsModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend SegmentsWidget;\n\nprivate:\n    QList<SegmentDescription> segments;\n\npublic:\n    enum Column {\n        NameColumn = 0,\n        SizeColumn,\n        AddressColumn,\n        EndAddressColumn,\n        PermColumn,\n        CommentColumn,\n        ColumnCount\n    };\n    enum Role { SegmentDescriptionRole = Qt::UserRole };\n\n    SegmentsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int segment, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n};\n\nclass SegmentsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    SegmentsProxyModel(SegmentsModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass SegmentsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit SegmentsWidget(MainWindow *main);\n    ~SegmentsWidget();\n\nprivate slots:\n    void refreshSegments();\n\nprivate:\n    SegmentsModel *segmentsModel;\n};\n\n#endif // SEGMENTSWIDGET_H\n"
  },
  {
    "path": "src/widgets/SimpleTextGraphView.cpp",
    "content": "\n#include \"SimpleTextGraphView.h\"\n#include \"core/Cutter.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Configuration.h\"\n#include \"common/SyntaxHighlighter.h\"\n#include \"common/Helpers.h\"\n\n#include <QPainter>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QMouseEvent>\n#include <QPropertyAnimation>\n#include <QShortcut>\n#include <QToolTip>\n#include <QTextEdit>\n#include <QVBoxLayout>\n#include <QStandardPaths>\n#include <QClipboard>\n#include <QApplication>\n#include <QAction>\n\n#include <cmath>\n\nSimpleTextGraphView::SimpleTextGraphView(QWidget *parent, MainWindow *mainWindow)\n    : CutterGraphView(parent),\n      contextMenu(new QMenu(this)),\n      addressableItemContextMenu(this, mainWindow),\n      copyAction(tr(\"Copy\"), this)\n{\n    copyAction.setShortcut(QKeySequence::StandardKey::Copy);\n    copyAction.setShortcutContext(Qt::WidgetShortcut);\n    connect(&copyAction, &QAction::triggered, this, &SimpleTextGraphView::copyBlockText);\n\n    contextMenu->addAction(&copyAction);\n    contextMenu->addAction(&actionExportGraph);\n    contextMenu->addMenu(layoutMenu);\n\n    addressableItemContextMenu.insertAction(addressableItemContextMenu.actions().first(),\n                                            &copyAction);\n    addressableItemContextMenu.addSeparator();\n    addressableItemContextMenu.addAction(&actionExportGraph);\n    addressableItemContextMenu.addMenu(layoutMenu);\n\n    addActions(addressableItemContextMenu.actions());\n    addAction(&copyAction);\n    enableAddresses(haveAddresses);\n}\n\nSimpleTextGraphView::~SimpleTextGraphView()\n{\n    for (QShortcut *shortcut : shortcuts) {\n        delete shortcut;\n    }\n}\n\nvoid SimpleTextGraphView::refreshView()\n{\n    initFont();\n    setLayoutConfig(getLayoutConfig());\n    saveCurrentBlock();\n    loadCurrentGraph();\n    if (blocks.find(selectedBlock) == blocks.end()) {\n        selectedBlock = NO_BLOCK_SELECTED;\n    }\n    restoreCurrentBlock();\n    emit viewRefreshed();\n}\n\nvoid SimpleTextGraphView::selectBlockWithId(ut64 blockId)\n{\n    if (!enableBlockSelection) {\n        return;\n    }\n    auto contentIt = blockContent.find(blockId);\n    if (contentIt != blockContent.end()) {\n        selectedBlock = blockId;\n        if (haveAddresses) {\n            addressableItemContextMenu.setTarget(contentIt->second.address, contentIt->second.text);\n        }\n        viewport()->update();\n    } else {\n        selectedBlock = NO_BLOCK_SELECTED;\n    }\n}\n\nvoid SimpleTextGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive)\n{\n    QRectF blockRect(block.x, block.y, block.width, block.height);\n\n    p.setPen(Qt::black);\n    p.setBrush(Qt::gray);\n    p.setFont(Config()->getFont());\n    p.drawRect(blockRect);\n\n    // Render node\n    auto &content = blockContent[block.entry];\n\n    p.setPen(QColor(0, 0, 0, 0));\n    p.setBrush(QColor(0, 0, 0, 100));\n    p.setPen(QPen(graphNodeColor, 1));\n\n    bool blockSelected = interactive && (block.entry == selectedBlock);\n    if (blockSelected) {\n        p.setBrush(disassemblySelectedBackgroundColor);\n    } else {\n        p.setBrush(disassemblyBackgroundColor);\n    }\n    // Draw basic block background\n    p.drawRect(blockRect);\n\n    // Stop rendering text when it's too small\n    auto transform = p.combinedTransform();\n    QRect screenChar = transform.mapRect(QRect(0, 0, ACharWidth, charHeight));\n\n    if (screenChar.width() < Config()->getGraphMinFontSize()) {\n        return;\n    }\n\n    p.setPen(palette().color(QPalette::WindowText));\n    // Render node text\n    QFontMetrics fm = QFontMetrics(p.font());\n    auto x = block.x + padding / 2;\n    int y = block.y + padding / 2 + fm.ascent();\n    p.drawText(QPoint(x, y), content.text);\n}\n\nGraphView::EdgeConfiguration SimpleTextGraphView::edgeConfiguration(GraphView::GraphBlock &from,\n                                                                    GraphView::GraphBlock *to,\n                                                                    bool interactive)\n{\n    EdgeConfiguration ec;\n    ec.color = jmpColor;\n    ec.start_arrow = false;\n    ec.end_arrow = true;\n    if (interactive && (selectedBlock == from.entry || selectedBlock == to->entry)) {\n        ec.width_scale = 2.0;\n    }\n    return ec;\n}\n\nvoid SimpleTextGraphView::setBlockSelectionEnabled(bool value)\n{\n    enableBlockSelection = value;\n    if (!value) {\n        selectedBlock = NO_BLOCK_SELECTED;\n    }\n}\n\nvoid SimpleTextGraphView::addBlock(GraphLayout::GraphBlock block, const QString &text, RVA address)\n{\n    auto &content = blockContent[block.entry];\n    content.text = text;\n    content.address = address;\n\n    int height = 1;\n    int width = mFontMetrics->width(text);\n    block.width = static_cast<int>(width + padding);\n    block.height = (height * charHeight) + padding;\n    GraphView::addBlock(std::move(block));\n}\n\nvoid SimpleTextGraphView::enableAddresses(bool enabled)\n{\n    haveAddresses = enabled;\n    if (!enabled) {\n        addressableItemContextMenu.clearTarget();\n        // Clearing addreassable item context menu disables all the actions inside it including\n        // extra ones added by SimpleTextGraphView. Re-enable them because they are in the regular\n        // context menu as well and shouldn't be disabled.\n        for (auto action : contextMenu->actions()) {\n            action->setEnabled(true);\n        }\n    };\n}\n\nvoid SimpleTextGraphView::copyBlockText()\n{\n    auto blockIt = blockContent.find(selectedBlock);\n    if (blockIt != blockContent.end()) {\n        QClipboard *clipboard = QApplication::clipboard();\n        clipboard->setText(blockIt->second.text);\n    }\n}\n\nvoid SimpleTextGraphView::contextMenuEvent(QContextMenuEvent *event)\n{\n    GraphView::contextMenuEvent(event);\n    if (!event->isAccepted() && event->reason() != QContextMenuEvent::Mouse && enableBlockSelection\n        && selectedBlock != NO_BLOCK_SELECTED) {\n        auto blockIt = blocks.find(selectedBlock);\n        if (blockIt != blocks.end()) {\n            blockContextMenuRequested(blockIt->second, event, {});\n        }\n    }\n    if (!event->isAccepted()) {\n        contextMenu->exec(event->globalPos());\n        event->accept();\n    }\n}\n\nvoid SimpleTextGraphView::blockContextMenuRequested(GraphView::GraphBlock &block,\n                                                    QContextMenuEvent *event, QPoint /*pos*/)\n{\n    if (haveAddresses) {\n        const auto &content = blockContent[block.entry];\n        addressableItemContextMenu.setTarget(content.address, content.text);\n        QPoint pos = event->globalPos();\n\n        if (event->reason() != QContextMenuEvent::Mouse) {\n            QPoint blockPosition(block.x + block.width / 2, block.y + block.height / 2);\n            blockPosition = logicalToViewCoordinates(blockPosition);\n            if (viewport()->rect().contains(blockPosition)) {\n                pos = mapToGlobal(blockPosition);\n            }\n        }\n        addressableItemContextMenu.exec(pos);\n        event->accept();\n    }\n}\n\nvoid SimpleTextGraphView::blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event,\n                                         QPoint /*pos*/)\n{\n    if (haveAddresses) {\n        QToolTip::showText(event->globalPos(), RzAddressString(blockContent[block.entry].address));\n    }\n}\n\nvoid SimpleTextGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event,\n                                       QPoint /*pos*/)\n{\n    if ((event->button() == Qt::LeftButton || event->button() == Qt::RightButton)\n        && enableBlockSelection) {\n        selectBlockWithId(block.entry);\n    }\n}\n\nvoid SimpleTextGraphView::restoreCurrentBlock()\n{\n    if (enableBlockSelection) {\n        auto blockIt = blocks.find(selectedBlock);\n        if (blockIt != blocks.end()) {\n            showBlock(blockIt->second, true);\n        }\n    }\n}\n\nvoid SimpleTextGraphView::paintEvent(QPaintEvent *event)\n{\n    // SimpleTextGraphView is always dirty\n    setCacheDirty();\n    GraphView::paintEvent(event);\n}\n"
  },
  {
    "path": "src/widgets/SimpleTextGraphView.h",
    "content": "#ifndef SIMPLE_TEXT_GRAPHVIEW_H\n#define SIMPLE_TEXT_GRAPHVIEW_H\n\n// Based on the DisassemblerGraphView from x64dbg\n\n#include <QWidget>\n#include <QPainter>\n#include <QShortcut>\n#include <QLabel>\n\n#include \"widgets/CutterGraphView.h\"\n#include \"menus/AddressableItemContextMenu.h\"\n#include \"common/RichTextPainter.h\"\n#include \"common/CutterSeekable.h\"\n\n/**\n * @brief Graphview with nodes containing simple plaintext labels.\n */\nclass SimpleTextGraphView : public CutterGraphView\n{\n    Q_OBJECT\npublic:\n    SimpleTextGraphView(QWidget *parent, MainWindow *mainWindow);\n    ~SimpleTextGraphView() override;\n    virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override;\n    virtual GraphView::EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from,\n                                                           GraphView::GraphBlock *to,\n                                                           bool interactive) override;\n\n    /**\n     * @brief Enable or disable block selection.\n     * Selecting a block highlights it and allows copying the label. Enabled by default.\n     * @param value\n     */\n    void setBlockSelectionEnabled(bool value);\npublic slots:\n    void refreshView() override;\n    /**\n     * @brief Select a given block. Requires block selection to be enabled.\n     */\n    void selectBlockWithId(ut64 blockId);\n\nprotected:\n    void paintEvent(QPaintEvent *event) override;\n    void contextMenuEvent(QContextMenuEvent *event) override;\n    void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,\n                                   QPoint pos) override;\n    void blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event, QPoint pos) override;\n    void blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, QPoint pos) override;\n\n    void restoreCurrentBlock() override;\n\n    /**\n     * @brief Load the graph to be displayed.\n     * Needs to cleanup the old graph and use addBlock() to create new nodes.\n     */\n    virtual void loadCurrentGraph() = 0;\n    virtual void addBlock(GraphLayout::GraphBlock block, const QString &content,\n                          RVA address = RVA_INVALID);\n    /**\n     * @brief Enable or disable address interactions for nodes.\n     * If enabled node addresses need to be specified when calling addBlock(). Adds address related\n     * items to the node context menu. By default disabled.\n     * @param enabled\n     */\n    void enableAddresses(bool enabled);\n\n    struct BlockContent\n    {\n        QString text;\n        RVA address;\n    };\n    std::unordered_map<ut64, BlockContent> blockContent;\n\n    QList<QShortcut *> shortcuts;\n    QMenu *contextMenu;\n    AddressableItemContextMenu addressableItemContextMenu;\n    QAction copyAction;\n\n    static const ut64 NO_BLOCK_SELECTED = RVA_INVALID;\n    ut64 selectedBlock = NO_BLOCK_SELECTED;\n    bool enableBlockSelection = true;\n    bool haveAddresses = false;\n\nprivate:\n    void copyBlockText();\n};\n\n#endif // SIMPLE_TEXT_GRAPHVIEW_H\n"
  },
  {
    "path": "src/widgets/StackWidget.cpp",
    "content": "#include \"StackWidget.h\"\n#include \"ui_StackWidget.h\"\n#include \"common/JsonModel.h\"\n#include \"common/Helpers.h\"\n#include \"dialogs/EditInstructionDialog.h\"\n\n#include \"core/MainWindow.h\"\n#include \"QHeaderView\"\n#include \"QMenu\"\n\nStackWidget::StackWidget(MainWindow *main)\n    : CutterDockWidget(main),\n      ui(new Ui::StackWidget),\n      menuText(this),\n      addressableItemContextMenu(this, main)\n{\n    ui->setupUi(this);\n\n    // Setup stack model\n    viewStack->setFont(Config()->getFont());\n    viewStack->setModel(modelStack);\n    viewStack->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);\n    viewStack->verticalHeader()->hide();\n    viewStack->setShowGrid(false);\n    viewStack->setSortingEnabled(true);\n    viewStack->setAutoScroll(false);\n    viewStack->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);\n    ui->verticalLayout->addWidget(viewStack);\n    viewStack->setEditTriggers(\n            viewStack->editTriggers()\n            & ~(QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed));\n\n    editAction = new QAction(tr(\"Edit stack value...\"), this);\n    viewStack->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { updateContents(); });\n\n    connect(Core(), &CutterCore::refreshAll, this, &StackWidget::updateContents);\n    connect(Core(), &CutterCore::registersChanged, this, &StackWidget::updateContents);\n    connect(Core(), &CutterCore::stackChanged, this, &StackWidget::updateContents);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(modelStack, StackModel::CommentColumn); });\n    connect(Config(), &Configuration::fontsUpdated, this, &StackWidget::fontsUpdatedSlot);\n    connect(viewStack, &QAbstractItemView::doubleClicked, this, &StackWidget::onDoubleClicked);\n    connect(viewStack, &QWidget::customContextMenuRequested, this,\n            &StackWidget::customMenuRequested);\n    connect(editAction, &QAction::triggered, this, &StackWidget::editStack);\n    connect(viewStack->selectionModel(), &QItemSelectionModel::currentChanged, this,\n            &StackWidget::onCurrentChanged);\n\n    addressableItemContextMenu.addAction(editAction);\n    addActions(addressableItemContextMenu.actions());\n\n    menuText.setSeparator(true);\n    qhelpers::prependQAction(&menuText, &addressableItemContextMenu);\n}\n\nStackWidget::~StackWidget() = default;\n\nvoid StackWidget::updateContents()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr) || Core()->isDebugTaskInProgress()) {\n        return;\n    }\n\n    setStackGrid();\n}\n\nvoid StackWidget::setStackGrid()\n{\n    modelStack->reload();\n    viewStack->resizeColumnsToContents();\n}\n\nvoid StackWidget::fontsUpdatedSlot()\n{\n    viewStack->setFont(Config()->getFont());\n}\n\nvoid StackWidget::onDoubleClicked(const QModelIndex &index)\n{\n    if (!index.isValid())\n        return;\n    // Check if we are clicking on the offset or value columns and seek if it is the case\n    int column = index.column();\n    if (column <= StackModel::ValueColumn) {\n        QString item = index.data().toString();\n        Core()->seek(item);\n        if (column == StackModel::OffsetColumn) {\n            mainWindow->showMemoryWidget(MemoryWidgetType::Hexdump);\n        } else {\n            Core()->showMemoryWidget();\n        }\n    }\n}\n\nvoid StackWidget::customMenuRequested(QPoint pos)\n{\n    addressableItemContextMenu.exec(viewStack->viewport()->mapToGlobal(pos));\n}\n\nvoid StackWidget::editStack()\n{\n    bool ok;\n    int row = viewStack->selectionModel()->currentIndex().row();\n    auto model = viewStack->model();\n    QString offset = model->index(row, StackModel::OffsetColumn).data().toString();\n    EditInstructionDialog e(EDIT_NONE, this);\n    e.setWindowTitle(tr(\"Edit stack at %1\").arg(offset));\n\n    QString oldBytes = model->index(row, StackModel::ValueColumn).data().toString();\n    e.setInstruction(oldBytes);\n\n    if (e.exec()) {\n        QString bytes = e.getInstruction();\n        if (bytes != oldBytes) {\n            Core()->editBytesEndian(offset.toULongLong(&ok, 16), bytes);\n        }\n    }\n}\n\nvoid StackWidget::onCurrentChanged(const QModelIndex &current, const QModelIndex &previous)\n{\n    Q_UNUSED(current)\n    Q_UNUSED(previous)\n    auto currentIndex = viewStack->selectionModel()->currentIndex();\n    QString offsetString;\n    if (currentIndex.column() != StackModel::DescriptionColumn) {\n        offsetString = currentIndex.data().toString();\n    } else {\n        offsetString =\n                currentIndex.sibling(currentIndex.row(), StackModel::ValueColumn).data().toString();\n    }\n\n    RVA offset = Core()->math(offsetString);\n    addressableItemContextMenu.setTarget(offset);\n    if (currentIndex.column() == StackModel::OffsetColumn) {\n        menuText.setText(tr(\"Stack position\"));\n    } else {\n        menuText.setText(tr(\"Pointed memory\"));\n    }\n}\n\nStackModel::StackModel(QObject *parent) : QAbstractTableModel(parent) {}\n\nvoid StackModel::reload()\n{\n    QList<AddrRefs> stackItems = Core()->getStack();\n\n    beginResetModel();\n    values.clear();\n    for (const AddrRefs &stackItem : stackItems) {\n        Item item;\n\n        item.offset = stackItem.addr;\n        item.value = RzAddressString(stackItem.value);\n        if (!stackItem.ref.isNull()) {\n            item.refDesc = Core()->formatRefDesc(stackItem.ref);\n        }\n\n        values.push_back(item);\n    }\n    endResetModel();\n}\n\nint StackModel::rowCount(const QModelIndex &) const\n{\n    return this->values.size();\n}\n\nint StackModel::columnCount(const QModelIndex &) const\n{\n    return ColumnCount;\n}\n\nQVariant StackModel::data(const QModelIndex &index, int role) const\n{\n    if (!index.isValid() || index.row() >= values.count())\n        return QVariant();\n\n    const auto &item = values.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case OffsetColumn:\n            return RzAddressString(item.offset);\n        case ValueColumn:\n            return item.value;\n        case DescriptionColumn:\n            return item.refDesc.ref;\n        case CommentColumn:\n            return Core()->getCommentAt(item.offset);\n        default:\n            return QVariant();\n        }\n    case Qt::ForegroundRole:\n        switch (index.column()) {\n        case DescriptionColumn:\n            return item.refDesc.refColor;\n        default:\n            return QVariant();\n        }\n    case StackDescriptionRole:\n        return QVariant::fromValue(item);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant StackModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n    Q_UNUSED(orientation);\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case OffsetColumn:\n            return tr(\"Offset\");\n        case ValueColumn:\n            return tr(\"Value\");\n        case DescriptionColumn:\n            return tr(\"Reference\");\n        case CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nbool StackModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n    if (role != Qt::EditRole || index.column() != ValueColumn) {\n        return false;\n    }\n\n    auto currentData = data(index, StackDescriptionRole);\n    if (!currentData.canConvert<StackModel::Item>()) {\n        return false;\n    }\n    auto currentItem = currentData.value<StackModel::Item>();\n\n    Core()->editBytesEndian(currentItem.offset, value.toString());\n    return true;\n}\n\nQt::ItemFlags StackModel::flags(const QModelIndex &index) const\n{\n    switch (index.column()) {\n    case ValueColumn:\n        return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;\n    default:\n        return QAbstractTableModel::flags(index);\n    }\n}\n"
  },
  {
    "path": "src/widgets/StackWidget.h",
    "content": "#pragma once\n\n#include <QJsonObject>\n#include <memory>\n#include <QStandardItem>\n#include <QTableView>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"menus/AddressableItemContextMenu.h\"\n\nclass MainWindow;\n\nnamespace Ui {\nclass StackWidget;\n}\n\nclass StackModel : public QAbstractTableModel\n{\n    Q_OBJECT\npublic:\n    struct Item\n    {\n        RVA offset;\n        QString value;\n        RefDescription refDesc;\n    };\n\n    enum Column { OffsetColumn = 0, ValueColumn, DescriptionColumn, CommentColumn, ColumnCount };\n    enum Role { StackDescriptionRole = Qt::UserRole };\n\n    StackModel(QObject *parent = nullptr);\n\n    void reload();\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    bool setData(const QModelIndex &index, const QVariant &value, int role) override;\n    Qt::ItemFlags flags(const QModelIndex &index) const override;\n\nprivate:\n    QVector<Item> values;\n};\nQ_DECLARE_METATYPE(StackModel::Item)\n\nclass StackWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit StackWidget(MainWindow *main);\n    ~StackWidget();\n\nprivate slots:\n    void updateContents();\n    void setStackGrid();\n    void fontsUpdatedSlot();\n    void onDoubleClicked(const QModelIndex &index);\n    void customMenuRequested(QPoint pos);\n    void editStack();\n    void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);\n\nprivate:\n    std::unique_ptr<Ui::StackWidget> ui;\n    QTableView *viewStack = new QTableView(this);\n    StackModel *modelStack = new StackModel(this);\n    QAction *editAction;\n    QAction menuText;\n    RefreshDeferrer *refreshDeferrer;\n    AddressableItemContextMenu addressableItemContextMenu;\n};\n"
  },
  {
    "path": "src/widgets/StackWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>StackWidget</class>\n <widget class=\"QDockWidget\" name=\"StackWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>463</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Stack</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n      <property name=\"spacing\">\n       <number>10</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n     </layout>\n  </widget>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/StringsWidget.cpp",
    "content": "#include \"StringsWidget.h\"\n#include \"ui_StringsWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QClipboard>\n#include <QMenu>\n#include <QModelIndex>\n#include <QShortcut>\n\nStringsModel::StringsModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint StringsModel::rowCount(const QModelIndex &) const\n{\n    return strings.count();\n}\n\nint StringsModel::columnCount(const QModelIndex &) const\n{\n    return StringsModel::ColumnCount;\n}\n\nQVariant StringsModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= strings.count())\n        return QVariant();\n\n    const StringDescription &str = strings.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case StringsModel::OffsetColumn:\n            return RzAddressString(str.vaddr);\n        case StringsModel::StringColumn:\n            return str.string;\n        case StringsModel::TypeColumn:\n            return str.type.toUpper();\n        case StringsModel::LengthColumn:\n            return QString::number(str.length);\n        case StringsModel::SizeColumn:\n            return QString::number(str.size);\n        case StringsModel::SectionColumn:\n            return str.section;\n        case StringsModel::CommentColumn:\n            return Core()->getCommentAt(str.vaddr);\n        default:\n            return QVariant();\n        }\n    case StringDescriptionRole:\n        return QVariant::fromValue(str);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant StringsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case StringsModel::OffsetColumn:\n            return tr(\"Address\");\n        case StringsModel::StringColumn:\n            return tr(\"String\");\n        case StringsModel::TypeColumn:\n            return tr(\"Type\");\n        case StringsModel::LengthColumn:\n            return tr(\"Length\");\n        case StringsModel::SizeColumn:\n            return tr(\"Size\");\n        case StringsModel::SectionColumn:\n            return tr(\"Section\");\n        case StringsModel::CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA StringsModel::address(const QModelIndex &index) const\n{\n    const StringDescription &str = strings.at(index.row());\n    return str.vaddr;\n}\n\nconst StringDescription *StringsModel::description(const QModelIndex &index) const\n{\n    return &strings.at(index.row());\n}\n\nStringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nvoid StringsProxyModel::setSelectedSection(QString section)\n{\n    selectedSection = section;\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    invalidateFilter();\n#else\n    invalidateRowsFilter();\n#endif\n}\n\nbool StringsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    StringDescription str =\n            index.data(StringsModel::StringDescriptionRole).value<StringDescription>();\n    if (selectedSection.isEmpty()) {\n        return qhelpers::filterStringContains(str.string, this);\n    } else {\n        return selectedSection == str.section && qhelpers::filterStringContains(str.string, this);\n    }\n}\n\nbool StringsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    auto model = static_cast<StringsModel *>(sourceModel());\n    auto leftStr = model->description(left);\n    auto rightStr = model->description(right);\n\n    switch (left.column()) {\n    case StringsModel::OffsetColumn:\n        return leftStr->vaddr < rightStr->vaddr;\n    case StringsModel::StringColumn: // sort by string\n        return leftStr->string < rightStr->string;\n    case StringsModel::TypeColumn: // sort by type\n        return leftStr->type < rightStr->type;\n    case StringsModel::SizeColumn: // sort by size\n        return leftStr->size < rightStr->size;\n    case StringsModel::LengthColumn: // sort by length\n        return leftStr->length < rightStr->length;\n    case StringsModel::SectionColumn:\n        return leftStr->section < rightStr->section;\n    case StringsModel::CommentColumn:\n        return Core()->getCommentAt(leftStr->vaddr) < Core()->getCommentAt(rightStr->vaddr);\n    default:\n        break;\n    }\n\n    // fallback\n    return leftStr->vaddr < rightStr->vaddr;\n}\n\nStringsWidget::StringsWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::StringsWidget), tree(new CutterTreeWidget(this))\n{\n    ui->setupUi(this);\n    ui->quickFilterView->setLabelText(tr(\"Section:\"));\n\n    // Add Status Bar footer\n    tree->addStatusBar(ui->verticalLayout);\n\n    qhelpers::setVerticalScrollMode(ui->stringsTreeView);\n\n    // Shift-F12 to toggle strings window\n    QShortcut *toggle_shortcut = Shortcuts()->makeQShortcut(\"Strings.toggle\", main);\n    connect(toggle_shortcut, &QShortcut::activated, this, [=]() { toggleDockWidget(true); });\n\n    connect(ui->actionCopy_String, &QAction::triggered, this, &StringsWidget::on_actionCopy);\n\n    ui->actionFilter->setShortcut(QKeySequence::Find);\n\n    ui->stringsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    model = new StringsModel(this);\n    proxyModel = new StringsProxyModel(model, this);\n    ui->stringsTreeView->setMainWindow(main);\n    ui->stringsTreeView->setModel(proxyModel);\n    ui->stringsTreeView->sortByColumn(-1, Qt::AscendingOrder);\n\n    //\n    auto menu = ui->stringsTreeView->getItemContextMenu();\n    menu->addAction(ui->actionCopy_String);\n\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, proxyModel,\n            &QSortFilterProxyModel::setFilterWildcard);\n\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, this,\n            [this] { tree->showItemsNumber(proxyModel->rowCount()); });\n\n    QShortcut *searchShortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,\n            &ComboQuickFilterView::showFilter);\n    searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    QShortcut *clearShortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n    connect(clearShortcut, &QShortcut::activated, this, [this]() {\n        ui->quickFilterView->clearFilter();\n        ui->stringsTreeView->setFocus();\n    });\n    clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    connect(Core(), &CutterCore::refreshAll, this, &StringsWidget::refreshStrings);\n    connect(Core(), &CutterCore::codeRebased, this, &StringsWidget::refreshStrings);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(model, StringsModel::CommentColumn); });\n\n    connect(ui->quickFilterView->comboBox(), &QComboBox::currentTextChanged, this, [this]() {\n        proxyModel->setSelectedSection(ui->quickFilterView->comboBox()->currentData().toString());\n        tree->showItemsNumber(proxyModel->rowCount());\n    });\n\n    auto header = ui->stringsTreeView->header();\n    header->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);\n    header->setSectionResizeMode(StringsModel::StringColumn, QHeaderView::ResizeMode::Stretch);\n    header->setStretchLastSection(false);\n    header->setResizeContentsPrecision(256);\n}\n\nStringsWidget::~StringsWidget() {}\n\nvoid StringsWidget::refreshStrings()\n{\n    if (task) {\n        task->wait();\n    }\n\n    task = QSharedPointer<StringsTask>(new StringsTask());\n    connect(task.data(), &StringsTask::stringSearchFinished, this,\n            &StringsWidget::stringSearchFinished);\n    Core()->getAsyncTaskManager()->start(task);\n\n    refreshSectionCombo();\n}\n\nvoid StringsWidget::refreshSectionCombo()\n{\n    QComboBox *combo = ui->quickFilterView->comboBox();\n\n    combo->clear();\n    combo->addItem(tr(\"(all)\"));\n\n    for (const QString &section : Core()->getSectionList()) {\n        combo->addItem(section, section);\n    }\n\n    proxyModel->setSelectedSection(QString());\n}\n\nvoid StringsWidget::stringSearchFinished(const QList<StringDescription> &strings)\n{\n    model->beginResetModel();\n    model->strings = strings;\n    model->endResetModel();\n\n    tree->showItemsNumber(proxyModel->rowCount());\n\n    task.clear();\n}\n\nvoid StringsWidget::on_actionCopy()\n{\n    QModelIndex current_item = ui->stringsTreeView->currentIndex();\n    int row = current_item.row();\n\n    QModelIndex index;\n\n    index = ui->stringsTreeView->model()->index(row, 1);\n\n    QClipboard *clipboard = QApplication::clipboard();\n    clipboard->setText(index.data().toString());\n}\n"
  },
  {
    "path": "src/widgets/StringsWidget.h",
    "content": "#ifndef STRINGSWIDGET_H\n#define STRINGSWIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"common/StringsTask.h\"\n#include \"CutterTreeWidget.h\"\n#include \"AddressableItemModel.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass StringsWidget;\n\nnamespace Ui {\nclass StringsWidget;\n}\n\nclass StringsModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend StringsWidget;\n\nprivate:\n    QList<StringDescription> strings;\n\npublic:\n    enum Column {\n        OffsetColumn = 0,\n        StringColumn,\n        TypeColumn,\n        LengthColumn,\n        SizeColumn,\n        SectionColumn,\n        CommentColumn,\n        ColumnCount\n    };\n    static const int StringDescriptionRole = Qt::UserRole;\n\n    StringsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    const StringDescription *description(const QModelIndex &index) const;\n};\n\nclass StringsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    StringsProxyModel(StringsModel *sourceModel, QObject *parent = nullptr);\n    void setSelectedSection(QString section);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n\n    QString selectedSection;\n};\n\nclass StringsWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit StringsWidget(MainWindow *main);\n    ~StringsWidget();\n\nprivate slots:\n    void refreshStrings();\n    void stringSearchFinished(const QList<StringDescription> &strings);\n    void refreshSectionCombo();\n\n    void on_actionCopy();\n\nprivate:\n    std::unique_ptr<Ui::StringsWidget> ui;\n\n    QSharedPointer<StringsTask> task;\n\n    StringsModel *model;\n    StringsProxyModel *proxyModel;\n    CutterTreeWidget *tree;\n};\n\n#endif // STRINGSWIDGET_H\n"
  },
  {
    "path": "src/widgets/StringsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>StringsWidget</class>\n <widget class=\"QDockWidget\" name=\"StringsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Strings</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"AddressableItemList&lt;&gt;\" name=\"stringsTreeView\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"styleSheet\">\n       <string notr=\"true\">QTreeWidget::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"ComboQuickFilterView\" name=\"quickFilterView\" native=\"true\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <action name=\"actionCopy_String\">\n   <property name=\"text\">\n    <string>Copy String</string>\n   </property>\n  </action>\n  <action name=\"actionFilter\">\n   <property name=\"text\">\n    <string>Filter</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>AddressableItemList&lt;&gt;</class>\n   <extends>QTreeView</extends>\n   <header>widgets/AddressableItemList.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ComboQuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/ComboQuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/SymbolsWidget.cpp",
    "content": "#include \"SymbolsWidget.h\"\n#include \"ui_ListDockWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n\n#include <QShortcut>\n\nSymbolsModel::SymbolsModel(QObject *parent) : AddressableItemModel<QAbstractListModel>(parent) {}\n\nint SymbolsModel::rowCount(const QModelIndex &) const\n{\n    return symbols.count();\n}\n\nint SymbolsModel::columnCount(const QModelIndex &) const\n{\n    return SymbolsModel::ColumnCount;\n}\n\nQVariant SymbolsModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= symbols.count()) {\n        return QVariant();\n    }\n\n    const SymbolDescription &symbol = symbols.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case SymbolsModel::AddressColumn:\n            return RzAddressString(symbol.vaddr);\n        case SymbolsModel::TypeColumn:\n            return QString(\"%1 %2\").arg(symbol.bind, symbol.type).trimmed();\n        case SymbolsModel::NameColumn:\n            return symbol.name;\n        case SymbolsModel::CommentColumn:\n            return Core()->getCommentAt(symbol.vaddr);\n        default:\n            return QVariant();\n        }\n    case SymbolsModel::SymbolDescriptionRole:\n        return QVariant::fromValue(symbol);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant SymbolsModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case SymbolsModel::AddressColumn:\n            return tr(\"Address\");\n        case SymbolsModel::TypeColumn:\n            return tr(\"Type\");\n        case SymbolsModel::NameColumn:\n            return tr(\"Name\");\n        case SymbolsModel::CommentColumn:\n            return tr(\"Comment\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nRVA SymbolsModel::address(const QModelIndex &index) const\n{\n    const SymbolDescription &symbol = symbols.at(index.row());\n    return symbol.vaddr;\n}\n\nQString SymbolsModel::name(const QModelIndex &index) const\n{\n    const SymbolDescription &symbol = symbols.at(index.row());\n    return symbol.name;\n}\n\nSymbolsProxyModel::SymbolsProxyModel(SymbolsModel *sourceModel, QObject *parent)\n    : AddressableFilterProxyModel(sourceModel, parent)\n{\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n}\n\nbool SymbolsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    auto symbol = index.data(SymbolsModel::SymbolDescriptionRole).value<SymbolDescription>();\n\n    return qhelpers::filterStringContains(symbol.name, this);\n}\n\nbool SymbolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    auto leftSymbol = left.data(SymbolsModel::SymbolDescriptionRole).value<SymbolDescription>();\n    auto rightSymbol = right.data(SymbolsModel::SymbolDescriptionRole).value<SymbolDescription>();\n\n    switch (left.column()) {\n    case SymbolsModel::AddressColumn:\n        return leftSymbol.vaddr < rightSymbol.vaddr;\n    case SymbolsModel::TypeColumn:\n        return leftSymbol.type < rightSymbol.type;\n    case SymbolsModel::NameColumn:\n        return leftSymbol.name < rightSymbol.name;\n    case SymbolsModel::CommentColumn:\n        return Core()->getCommentAt(leftSymbol.vaddr) < Core()->getCommentAt(rightSymbol.vaddr);\n    default:\n        break;\n    }\n\n    return false;\n}\n\nSymbolsWidget::SymbolsWidget(MainWindow *main) : ListDockWidget(main)\n{\n    setWindowTitle(tr(\"Symbols\"));\n    setObjectName(\"SymbolsWidget\");\n\n    symbolsModel = new SymbolsModel(this);\n    symbolsProxyModel = new SymbolsProxyModel(symbolsModel, this);\n    setModels(symbolsProxyModel);\n    ui->treeView->sortByColumn(SymbolsModel::AddressColumn, Qt::AscendingOrder);\n\n    connect(Core(), &CutterCore::codeRebased, this, &SymbolsWidget::refreshSymbols);\n    connect(Core(), &CutterCore::refreshAll, this, &SymbolsWidget::refreshSymbols);\n    connect(Core(), &CutterCore::commentsChanged, this,\n            [this]() { qhelpers::emitColumnChanged(symbolsModel, SymbolsModel::CommentColumn); });\n}\n\nSymbolsWidget::~SymbolsWidget() {}\n\nvoid SymbolsWidget::refreshSymbols()\n{\n    symbolsModel->beginResetModel();\n    symbolsModel->symbols = Core()->getAllSymbols();\n    symbolsModel->endResetModel();\n\n    qhelpers::adjustColumns(ui->treeView, SymbolsModel::ColumnCount, 0);\n}\n"
  },
  {
    "path": "src/widgets/SymbolsWidget.h",
    "content": "#ifndef SYMBOLSWIDGET_H\n#define SYMBOLSWIDGET_H\n\n#include <memory>\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"widgets/ListDockWidget.h\"\n\nclass MainWindow;\nclass QTreeWidgetItem;\nclass SymbolsWidget;\n\nclass SymbolsModel : public AddressableItemModel<QAbstractListModel>\n{\n    Q_OBJECT\n\n    friend SymbolsWidget;\n\nprivate:\n    QList<SymbolDescription> symbols;\n\npublic:\n    enum Column { AddressColumn = 0, TypeColumn, NameColumn, CommentColumn, ColumnCount };\n    enum Role { SymbolDescriptionRole = Qt::UserRole };\n\n    SymbolsModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    RVA address(const QModelIndex &index) const override;\n    QString name(const QModelIndex &index) const override;\n};\n\nclass SymbolsProxyModel : public AddressableFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    SymbolsProxyModel(SymbolsModel *sourceModel, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n};\n\nclass SymbolsWidget : public ListDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit SymbolsWidget(MainWindow *main);\n    ~SymbolsWidget();\n\nprivate slots:\n    void refreshSymbols();\n\nprivate:\n    SymbolsModel *symbolsModel;\n    SymbolsProxyModel *symbolsProxyModel;\n};\n\n#endif // SYMBOLSWIDGET_H\n"
  },
  {
    "path": "src/widgets/ThreadsWidget.cpp",
    "content": "#include <QShortcut>\n#include \"ThreadsWidget.h\"\n#include \"CutterCommon.h\"\n#include \"Helpers.h\"\n#include \"ui_ThreadsWidget.h\"\n#include \"QuickFilterView.h\"\n#include <rz_debug.h>\n\n#include \"core/MainWindow.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\nThreadModel::ThreadModel(QObject *parent) : QAbstractListModel(parent) {}\n\nvoid ThreadModel::setList(QList<ThreadDescription> data)\n{\n    beginResetModel();\n    this->threads = data;\n    endResetModel();\n}\n\nint ThreadModel::rowCount(const QModelIndex &) const\n{\n    return threads.count();\n}\n\nint ThreadModel::columnCount(const QModelIndex &) const\n{\n    return ThreadModel::ColumnIndex::COLUMN_COUNT;\n}\n\nQVariant ThreadModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= threads.count())\n        return QVariant();\n\n    const ThreadDescription &thread = threads.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case ColumnIndex::COLUMN_PID:\n            return thread.current ? QString(\"*%0\").arg(thread.pid) : QString(\"%0\").arg(thread.pid);\n        case ColumnIndex::COLUMN_STATUS:\n            return translateStatus(thread.status);\n        case ColumnIndex::COLUMN_PATH:\n            return thread.path;\n        case ColumnIndex::COLUMN_PC:\n            return RzAddressString(thread.pc);\n        case ColumnIndex::COLUMN_TLS:\n            return RzAddressString(thread.tls);\n        default:\n            return QVariant();\n        }\n    case Qt::FontRole: {\n        if (thread.current) {\n            QFont font;\n            font.setBold(true);\n            return font;\n        }\n        return QVariant();\n    }\n    case Qt::EditRole:\n        switch (index.column()) {\n        case ColumnIndex::COLUMN_PID:\n            return thread.pid;\n        case ColumnIndex::COLUMN_PC:\n            return thread.pc;\n        case ColumnIndex::COLUMN_TLS:\n            return thread.tls;\n        default:\n            return data(index, Qt::DisplayRole);\n        }\n        break;\n    default:\n        return QVariant();\n    }\n}\n\nQVariant ThreadModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case ColumnIndex::COLUMN_PID:\n            return tr(\"TID\");\n        case ColumnIndex::COLUMN_STATUS:\n            return tr(\"Status\");\n        case ColumnIndex::COLUMN_PATH:\n            return tr(\"Path\");\n        case ColumnIndex::COLUMN_PC:\n            return tr(\"PC\");\n        case ColumnIndex::COLUMN_TLS:\n            return tr(\"TLS\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nThreadsWidget::ThreadsWidget(MainWindow *main)\n    : CutterDockWidget(main),\n      ui(new Ui::ThreadsWidget),\n      menuText(this),\n      addressableItemContextMenu(this, main)\n{\n    ui->setupUi(this);\n\n    // Setup threads model\n    modelThreads = new ThreadModel(this);\n\n    ui->viewThreads->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);\n    ui->viewThreads->verticalHeader()->setVisible(false);\n    fontsUpdatedSlot();\n\n    modelFilter = new QSortFilterProxyModel(this);\n    modelFilter->setSourceModel(modelThreads);\n\n    modelFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);\n    modelFilter->setSortCaseSensitivity(Qt::CaseInsensitive);\n    modelFilter->setFilterKeyColumn(-1);\n    modelFilter->setFilterRole(Qt::DisplayRole);\n    modelFilter->setSortRole(Qt::EditRole);\n\n    ui->viewThreads->setModel(modelFilter);\n\n    // CTRL+F switches to the filter view and opens it in case it's hidden\n    QShortcut *searchShortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,\n            &QuickFilterView::showFilter);\n    searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    // ESC switches back to the thread table and clears the buffer\n    QShortcut *clearShortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n    connect(clearShortcut, &QShortcut::activated, this, [this]() {\n        ui->quickFilterView->clearFilter();\n        ui->viewThreads->setFocus();\n    });\n    clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { updateContents(); });\n\n    menuText.setSeparator(true);\n    qhelpers::prependQAction(&menuText, &addressableItemContextMenu);\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, modelFilter,\n            &QSortFilterProxyModel::setFilterWildcard);\n    connect(Core(), &CutterCore::refreshAll, this, &ThreadsWidget::updateContents);\n    connect(Core(), &CutterCore::registersChanged, this, &ThreadsWidget::updateContents);\n    connect(Core(), &CutterCore::debugTaskStateChanged, this, &ThreadsWidget::updateContents);\n    // Seek doesn't necessarily change when switching threads/processes\n    connect(Core(), &CutterCore::switchedThread, this, &ThreadsWidget::updateContents);\n    connect(Core(), &CutterCore::switchedProcess, this, &ThreadsWidget::updateContents);\n    connect(Config(), &Configuration::fontsUpdated, this, &ThreadsWidget::fontsUpdatedSlot);\n    connect(ui->viewThreads, &QTableView::activated, this, &ThreadsWidget::onActivated);\n    connect(ui->viewThreads, &QWidget::customContextMenuRequested, this,\n            &ThreadsWidget::tableCustomContextMenu);\n    connect(ui->viewThreads->selectionModel(), &QItemSelectionModel::currentChanged, this,\n            &ThreadsWidget::onCurrentChanged);\n    ui->viewThreads->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);\n}\n\nThreadsWidget::~ThreadsWidget() {}\n\nvoid ThreadsWidget::updateContents()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    if (!Core()->currentlyDebugging) {\n        // Remove rows from the previous debugging session\n        modelThreads->setList(QList<ThreadDescription>());\n        return;\n    }\n\n    if (Core()->isDebugTaskInProgress()) {\n        ui->viewThreads->setDisabled(true);\n    } else {\n        setThreadsGrid();\n        ui->viewThreads->setDisabled(false);\n    }\n}\n\nQString ThreadModel::translateStatus(const char status) const\n{\n    switch (status) {\n    case RZ_DBG_PROC_STOP:\n        return \"Stopped\";\n    case RZ_DBG_PROC_RUN:\n        return \"Running\";\n    case RZ_DBG_PROC_SLEEP:\n        return \"Sleeping\";\n    case RZ_DBG_PROC_ZOMBIE:\n        return \"Zombie\";\n    case RZ_DBG_PROC_DEAD:\n        return \"Dead\";\n    case RZ_DBG_PROC_RAISED:\n        return \"Raised event\";\n    default:\n        return \"Unknown status\";\n    }\n}\n\nvoid ThreadsWidget::setThreadsGrid()\n{\n    modelThreads->setList(Core()->getProcessThreads());\n    ui->viewThreads->resizeColumnsToContents();\n}\n\nvoid ThreadsWidget::fontsUpdatedSlot()\n{\n    ui->viewThreads->setFont(Config()->getFont());\n}\n\nvoid ThreadsWidget::onActivated(const QModelIndex &index)\n{\n    if (!index.isValid())\n        return;\n\n    int tid = modelFilter->data(index.sibling(index.row(), ThreadModel::COLUMN_PID), Qt::EditRole)\n                      .toInt();\n\n    // Verify that the selected tid is still in the threads list since dpt= will\n    // attach to any given id. If it isn't found simply update the UI.\n    for (const auto &value : Core()->getProcessThreads()) {\n        if (tid == value.pid) {\n            Core()->setCurrentDebugThread(tid);\n            break;\n        }\n    }\n\n    updateContents();\n}\n\nvoid ThreadsWidget::onCurrentChanged(const QModelIndex &current, const QModelIndex &previous)\n{\n    Q_UNUSED(previous)\n\n    RVA offset = 0;\n    if (current.column() == ThreadModel::ColumnIndex::COLUMN_TLS) {\n        offset = current.data(Qt::EditRole).toULongLong();\n        menuText.setText(tr(\"TLS (%0)\").arg(RzAddressString(offset)));\n    } else {\n        offset = current.sibling(current.row(), ThreadModel::ColumnIndex::COLUMN_PC)\n                         .data(Qt::EditRole)\n                         .toULongLong();\n        menuText.setText(tr(\"PC (%0)\").arg(RzAddressString(offset)));\n    }\n\n    addressableItemContextMenu.setTarget(offset);\n}\n\nvoid ThreadsWidget::tableCustomContextMenu(const QPoint &pos)\n{\n    addressableItemContextMenu.exec(this->ui->viewThreads->viewport()->mapToGlobal(pos));\n}\n"
  },
  {
    "path": "src/widgets/ThreadsWidget.h",
    "content": "#pragma once\n\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"AddressableItemContextMenu.h\"\n#include \"CutterDescriptions.h\"\n#include \"CutterDockWidget.h\"\n\nclass MainWindow;\n\nnamespace Ui {\nclass ThreadsWidget;\n}\n\nclass ThreadModel : public QAbstractListModel\n{\n    Q_OBJECT\n\n    friend class ThreadsWidget;\n\nprivate:\n    QList<ThreadDescription> threads;\n\npublic:\n    enum ColumnIndex {\n        COLUMN_PID = 0,\n        COLUMN_STATUS,\n        COLUMN_PATH,\n        COLUMN_PC,\n        COLUMN_TLS,\n\n        COLUMN_COUNT,\n    };\n\n    ThreadModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    void setList(QList<ThreadDescription> data);\n\n    const ThreadDescription *description(const QModelIndex &index) const;\n    QString translateStatus(const char status) const;\n};\n\nclass ThreadsWidget : public CutterDockWidget\n{\n    Q_OBJECT\npublic:\n    explicit ThreadsWidget(MainWindow *main);\n    ~ThreadsWidget();\n\nprivate slots:\n    void updateContents();\n    void setThreadsGrid();\n    void fontsUpdatedSlot();\n    void onActivated(const QModelIndex &index);\n    void tableCustomContextMenu(const QPoint &pos);\n    void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);\n\nprivate:\n    std::unique_ptr<Ui::ThreadsWidget> ui;\n    ThreadModel *modelThreads;\n    QSortFilterProxyModel *modelFilter;\n    RefreshDeferrer *refreshDeferrer;\n    QAction menuText;\n    AddressableItemContextMenu addressableItemContextMenu;\n};\n"
  },
  {
    "path": "src/widgets/ThreadsWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ThreadsWidget</class>\n <widget class=\"QDockWidget\" name=\"ThreadsWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>463</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Threads</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n      <property name=\"spacing\">\n       <number>10</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QTableView\" name=\"viewThreads\">\n        <property name=\"sortingEnabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"cornerButtonEnabled\">\n         <bool>false</bool>\n        </property>\n        <property name=\"selectionMode\">\n         <enum>QAbstractItemView::SingleSelection</enum>\n        </property>\n        <property name=\"editTriggers\">\n         <enum>QAbstractItemView::NoEditTriggers</enum>\n        </property>\n        <property name=\"horizontalScrollMode\">\n         <enum>QAbstractItemView::ScrollPerPixel</enum>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QuickFilterView\" name=\"quickFilterView\" native=\"true\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n       </widget>\n      </item>\n     </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>QuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/QuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/TypesWidget.cpp",
    "content": "#include \"TypesWidget.h\"\n#include \"ui_TypesWidget.h\"\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"dialogs/TypesInteractionDialog.h\"\n#include \"dialogs/TypesVariablesDialog.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include <QMenu>\n#include <QFileDialog>\n#include <QShortcut>\n#include <QIcon>\n\nTypesModel::TypesModel(QObject *parent) : QAbstractListModel(parent) {}\n\nQVariant TypesModel::toolTipValue(const QModelIndex &index) const\n{\n    TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n\n    if (t.category == \"Primitive\") {\n        return QVariant();\n    }\n\n    return Core()->getTypeAsC(t.type).trimmed();\n}\n\nint TypesModel::rowCount(const QModelIndex &) const\n{\n    return types.count();\n}\n\nint TypesModel::columnCount(const QModelIndex &) const\n{\n    return Columns::COUNT;\n}\n\nQVariant TypesModel::data(const QModelIndex &index, int role) const\n{\n    if (index.row() >= types.count())\n        return QVariant();\n\n    const TypeDescription &exp = types.at(index.row());\n\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (index.column()) {\n        case TYPE:\n            return exp.type;\n        case SIZE:\n            return exp.size ? exp.size : QVariant();\n        case FORMAT:\n            return exp.format;\n        case CATEGORY:\n            return exp.category;\n        default:\n            return QVariant();\n        }\n    case Qt::ToolTipRole:\n        return toolTipValue(index);\n    case TypeDescriptionRole:\n        return QVariant::fromValue(exp);\n    default:\n        return QVariant();\n    }\n}\n\nQVariant TypesModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case TYPE:\n            return tr(\"Type / Name\");\n        case SIZE:\n            return tr(\"Size\");\n        case FORMAT:\n            return tr(\"Format\");\n        case CATEGORY:\n            return tr(\"Category\");\n        default:\n            return QVariant();\n        }\n    default:\n        return QVariant();\n    }\n}\n\nbool TypesModel::removeRows(int row, int count, const QModelIndex &parent)\n{\n    RzCoreLocked core(Core());\n    rz_type_db_del(core->analysis->typedb, types.at(row).type.toUtf8().constData());\n    beginRemoveRows(parent, row, row + count - 1);\n    while (count--) {\n        types.removeAt(row);\n    }\n    endRemoveRows();\n    return true;\n}\n\nTypesSortFilterProxyModel::TypesSortFilterProxyModel(TypesModel *source_model, QObject *parent)\n    : QSortFilterProxyModel(parent)\n{\n    setSourceModel(source_model);\n}\n\nvoid TypesSortFilterProxyModel::setCategory(QString category)\n{\n    selectedCategory = category;\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    invalidateFilter();\n#else\n    invalidateRowsFilter();\n#endif\n}\n\nbool TypesSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const\n{\n    QModelIndex index = sourceModel()->index(row, 0, parent);\n    TypeDescription exp = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n    if (!selectedCategory.isEmpty() && selectedCategory != exp.category) {\n        return false;\n    }\n    return qhelpers::filterStringContains(exp.type, this);\n}\n\nbool TypesSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const\n{\n    TypeDescription left_exp = left.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n    TypeDescription right_exp =\n            right.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n\n    switch (left.column()) {\n    case TypesModel::TYPE:\n        return left_exp.type < right_exp.type;\n    case TypesModel::SIZE:\n        return left_exp.size < right_exp.size;\n    case TypesModel::FORMAT:\n        return left_exp.format < right_exp.format;\n    case TypesModel::CATEGORY:\n        return left_exp.category < right_exp.category;\n    default:\n        break;\n    }\n\n    return left_exp.size < right_exp.size;\n}\n\nTypesWidget::TypesWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::TypesWidget), tree(new CutterTreeWidget(this))\n{\n    ui->setupUi(this);\n    ui->quickFilterView->setLabelText(tr(\"Category\"));\n\n    // Add status bar which displays the count\n    tree->addStatusBar(ui->verticalLayout);\n\n    // Set single select mode\n    ui->typesTreeView->setSelectionMode(QAbstractItemView::SingleSelection);\n\n    // Setup up the model and the proxy model\n    types_model = new TypesModel(this);\n    types_proxy_model = new TypesSortFilterProxyModel(types_model, this);\n    ui->typesTreeView->setModel(types_proxy_model);\n    ui->typesTreeView->sortByColumn(TypesModel::TYPE, Qt::AscendingOrder);\n\n    setScrollMode();\n\n    // Setup custom context menu\n    connect(ui->typesTreeView, &QWidget::customContextMenuRequested, this,\n            &TypesWidget::showTypesContextMenu);\n\n    ui->typesTreeView->setContextMenuPolicy(Qt::CustomContextMenu);\n\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, types_proxy_model,\n            &QSortFilterProxyModel::setFilterWildcard);\n\n    connect(ui->quickFilterView, &ComboQuickFilterView::filterTextChanged, this,\n            [this] { tree->showItemsNumber(types_proxy_model->rowCount()); });\n\n    QShortcut *searchShortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(searchShortcut, &QShortcut::activated, ui->quickFilterView,\n            &ComboQuickFilterView::showFilter);\n    searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    QShortcut *clearShortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n    connect(clearShortcut, &QShortcut::activated, ui->quickFilterView,\n            &ComboQuickFilterView::clearFilter);\n    clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    connect(Core(), &CutterCore::refreshAll, this, &TypesWidget::refreshTypes);\n\n    connect(ui->quickFilterView->comboBox(), &QComboBox::currentTextChanged, this, [this]() {\n        types_proxy_model->setCategory(ui->quickFilterView->comboBox()->currentData().toString());\n        tree->showItemsNumber(types_proxy_model->rowCount());\n    });\n\n    actionViewType = new QAction(tr(\"View Type\"), this);\n    actionEditType = new QAction(tr(\"Edit Type\"), this);\n    actionShowVariables = new QAction(tr(\"Show Variables and Globals of this Type\"), this);\n\n    connect(actionViewType, &QAction::triggered, [this]() { viewType(true); });\n    connect(actionEditType, &QAction::triggered, [this]() { viewType(false); });\n    connect(ui->typesTreeView, &QTreeView::doubleClicked, this,\n            &TypesWidget::typeItemDoubleClicked);\n\n    connect(actionShowVariables, &QAction::triggered, [this]() { showVariables(); });\n}\n\nTypesWidget::~TypesWidget() {}\n\nvoid TypesWidget::refreshTypes()\n{\n    types_model->beginResetModel();\n    types_model->types = Core()->getAllTypes();\n    types_model->endResetModel();\n\n    QStringList categories;\n    for (const TypeDescription &exp : types_model->types) {\n        categories << exp.category;\n    }\n    categories.removeDuplicates();\n    refreshCategoryCombo(categories);\n\n    qhelpers::adjustColumns(ui->typesTreeView, 4, 0);\n}\n\nvoid TypesWidget::refreshCategoryCombo(const QStringList &categories)\n{\n    QComboBox *combo = ui->quickFilterView->comboBox();\n\n    combo->clear();\n    combo->addItem(tr(\"(All)\"));\n\n    for (const QString &category : categories) {\n        combo->addItem(category, category);\n    }\n\n    types_proxy_model->setCategory(QString());\n}\n\nvoid TypesWidget::setScrollMode()\n{\n    qhelpers::setVerticalScrollMode(ui->typesTreeView);\n}\n\nvoid TypesWidget::showTypesContextMenu(const QPoint &pt)\n{\n    QModelIndex index = ui->typesTreeView->indexAt(pt);\n\n    QMenu menu(ui->typesTreeView);\n    menu.addAction(ui->actionLoad_New_Types);\n\n    if (index.isValid()) {\n        TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n        if (t.category != \"Primitive\") {\n            // Add \"Link To Address\" option\n            menu.addAction(actionViewType);\n            menu.addAction(actionEditType);\n            menu.addAction(actionShowVariables);\n        }\n    }\n\n    menu.addAction(ui->actionExport_Types);\n\n    if (index.isValid()) {\n        TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n        if (t.category != \"Typedef\") {\n            menu.addSeparator();\n            menu.addAction(ui->actionDelete_Type);\n        }\n    }\n\n    menu.exec(ui->typesTreeView->mapToGlobal(pt));\n}\n\nvoid TypesWidget::on_actionExport_Types_triggered()\n{\n    auto core = Core()->lock();\n    char *str = rz_core_types_as_c_all(core, true);\n    if (!str) {\n        return;\n    }\n    QString filename =\n            QFileDialog::getSaveFileName(this, tr(\"Save File\"), Config()->getRecentFolder());\n    if (filename.isEmpty()) {\n        return;\n    }\n    Config()->setRecentFolder(QFileInfo(filename).absolutePath());\n    QFile file(filename);\n    if (!file.open(QIODevice::WriteOnly)) {\n        QMessageBox::critical(this, tr(\"Error\"), file.errorString());\n        on_actionExport_Types_triggered();\n        return;\n    }\n    QTextStream fileOut(&file);\n    fileOut << str;\n    free(str);\n    file.close();\n}\n\nvoid TypesWidget::on_actionLoad_New_Types_triggered()\n{\n    QModelIndex index = ui->typesTreeView->currentIndex();\n    if (!index.isValid()) {\n        return;\n    }\n\n    TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n\n    TypesInteractionDialog dialog(this);\n    connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);\n    dialog.setWindowTitle(tr(\"Load New Types\"));\n    dialog.exec();\n}\n\nvoid TypesWidget::viewType(bool readOnly)\n{\n\n    QModelIndex index = ui->typesTreeView->currentIndex();\n\n    if (!index.isValid()) {\n        return;\n    }\n\n    TypesInteractionDialog dialog(this, readOnly);\n    TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n    if (!readOnly) {\n        dialog.setWindowTitle(tr(\"Edit Type: \") + t.type);\n        connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);\n    } else {\n        dialog.setWindowTitle(tr(\"View Type: \") + t.type + tr(\" (Read Only)\"));\n    }\n    dialog.fillTextArea(Core()->getTypeAsC(t.type));\n    dialog.setTypeName(t.type);\n    dialog.exec();\n}\n\nvoid TypesWidget::on_actionDelete_Type_triggered()\n{\n    QModelIndex proxyIndex = ui->typesTreeView->currentIndex();\n    if (!proxyIndex.isValid()) {\n        return;\n    }\n    QModelIndex index = types_proxy_model->mapToSource(proxyIndex);\n    if (!index.isValid()) {\n        return;\n    }\n\n    TypeDescription exp = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n    QMessageBox::StandardButton reply = QMessageBox::question(\n            this, tr(\"Cutter\"), tr(\"Are you sure you want to delete \\\"%1\\\"?\").arg(exp.type));\n    if (reply == QMessageBox::Yes) {\n        types_model->removeRow(index.row());\n    }\n}\n\nvoid TypesWidget::typeItemDoubleClicked(const QModelIndex &index)\n{\n    if (!index.isValid()) {\n        return;\n    }\n\n    TypesInteractionDialog dialog(this, true);\n    TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n    if (t.category == \"Primitive\") {\n        return;\n    }\n    dialog.fillTextArea(Core()->getTypeAsC(t.type));\n    dialog.setWindowTitle(tr(\"View Type: \") + t.type + tr(\" (Read Only)\"));\n    dialog.setTypeName(t.type);\n    dialog.exec();\n}\n\nvoid TypesWidget::showVariables()\n{\n    QModelIndex index = ui->typesTreeView->currentIndex();\n\n    if (!index.isValid()) {\n        return;\n    }\n    TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();\n\n    TypesVariablesDialog tvd(this, t.type);\n    tvd.exec();\n}\n\nvoid TypesWidget::selectTypeByName(const QString &typeName)\n{\n    if (typeName.isEmpty()) {\n        return;\n    }\n\n    QModelIndexList results = types_proxy_model->match(\n            types_proxy_model->index(0, 0), Qt::DisplayRole, typeName, 1, Qt::MatchExactly);\n\n    // if results are empty, remove the filter and try again\n    // avoids removing the filter unnecessarily\n    bool isTextFilterEmpty;\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n    isTextFilterEmpty = types_proxy_model->filterRegExp().pattern().isEmpty();\n#else\n    isTextFilterEmpty = types_proxy_model->filterRegularExpression().pattern().isEmpty();\n#endif\n    if (results.isEmpty()\n        && (!isTextFilterEmpty || ui->quickFilterView->comboBox()->currentIndex() != 0)) {\n\n        ui->quickFilterView->clearFilter();\n        ui->quickFilterView->comboBox()->setCurrentIndex(0); // select (All)\n        types_proxy_model->setFilterFixedString(\"\");\n        results = types_proxy_model->match(types_proxy_model->index(0, 0), Qt::DisplayRole,\n                                           typeName, 1, Qt::MatchExactly);\n    }\n\n    if (results.isEmpty()) {\n        return;\n    }\n\n    QModelIndex index = results.first();\n    ui->typesTreeView->setCurrentIndex(index);\n    ui->typesTreeView->selectionModel()->select(\n            index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);\n    ui->typesTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter);\n    ui->typesTreeView->setFocus();\n}\n"
  },
  {
    "path": "src/widgets/TypesWidget.h",
    "content": "#ifndef TYPESWIDGET_H\n#define TYPESWIDGET_H\n\n#include <memory>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeWidget.h\"\n\n#include <QAbstractListModel>\n#include <QSortFilterProxyModel>\n\nclass MainWindow;\nclass QTreeWidget;\nclass TypesWidget;\n\nnamespace Ui {\nclass TypesWidget;\n}\n\nclass MainWindow;\nclass QTreeWidgetItem;\n\nclass TypesModel : public QAbstractListModel\n{\n    Q_OBJECT\n\n    friend TypesWidget;\n\nprivate:\n    QList<TypeDescription> types;\n\n    /**\n     * @brief Returns a description of the type for the given index\n     */\n    QVariant toolTipValue(const QModelIndex &index) const;\n\npublic:\n    enum Columns { TYPE = 0, SIZE, CATEGORY, FORMAT, COUNT };\n    static const int TypeDescriptionRole = Qt::UserRole;\n\n    TypesModel(QObject *parent = nullptr);\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const override;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\n    QVariant data(const QModelIndex &index, int role) const override;\n    QVariant headerData(int section, Qt::Orientation orientation,\n                        int role = Qt::DisplayRole) const override;\n\n    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;\n};\n\nclass TypesSortFilterProxyModel : public QSortFilterProxyModel\n{\n    Q_OBJECT\n\npublic:\n    TypesSortFilterProxyModel(TypesModel *source_model, QObject *parent = nullptr);\n    void setCategory(QString category);\n\nprotected:\n    bool filterAcceptsRow(int row, const QModelIndex &parent) const override;\n    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;\n\n    QString selectedCategory;\n};\n\nclass TypesWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit TypesWidget(MainWindow *main);\n    ~TypesWidget();\n\n    /**\n     * @brief Highlight and scroll to a specific type in the list\n     * Clears the filter if the type is not visible in the filtered list\n     * @param typeName Name of the type to select\n     */\n    void selectTypeByName(const QString &typeName);\n\nprivate slots:\n    void refreshTypes();\n\n    /**\n     * @brief Show custom context menu\n     * @param pt Position of the place where the right mouse button was clicked\n     */\n    void showTypesContextMenu(const QPoint &pt);\n\n    /**\n     * @brief Executed on clicking the Export Types option in the context menu\n     * It shows the user a file dialog box to select a file where the types\n     * will be exported.\n     */\n    void on_actionExport_Types_triggered();\n\n    /**\n     * @brief Executed on clicking the Load New types option in the context menu\n     * It will open the TypesInteractionDialog where the user can either enter the\n     * types manually, or can select a file from where the types will be loaded\n     */\n    void on_actionLoad_New_Types_triggered();\n\n    /**\n     * @brief Executed on clicking either the Edit Type or View Type options in the context menu\n     * It will open the TypesInteractionDialog filled with the selected type. Depends on Edit or\n     * View mode the text view would be read-only or not.\n     */\n    void viewType(bool readOnly = true);\n\n    /**\n     * @brief Executed on clicking the Delete Type option in the context menu\n     * Upon confirmation from the user, it will delete the selected type.\n     */\n    void on_actionDelete_Type_triggered();\n\n    /**\n     * @brief triggers when the user double-clicks an item. This will open\n     * a dialog that shows the Type's content\n     */\n    void typeItemDoubleClicked(const QModelIndex &index);\n\n    /**\n     * @brief Opens a dialog that displays all global and local variables associated\n     * with the selected type\n     */\n    void showVariables();\n\nprivate:\n    std::unique_ptr<Ui::TypesWidget> ui;\n\n    TypesModel *types_model;\n    TypesSortFilterProxyModel *types_proxy_model;\n    CutterTreeWidget *tree;\n    QAction *actionViewType;\n    QAction *actionEditType;\n    QAction *actionShowVariables;\n\n    void setScrollMode();\n\n    /**\n     * @brief Sets the contents of the ComboBox to the supplied contents\n     * @param categories The list of categories which has to be added to the ComboBox\n     */\n    void refreshCategoryCombo(const QStringList &categories);\n};\n\n#endif // TYPESWIDGET_H\n"
  },
  {
    "path": "src/widgets/TypesWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>TypesWidget</class>\n <widget class=\"QDockWidget\" name=\"TypesWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Types</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"CutterTreeView\" name=\"typesTreeView\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"styleSheet\">\n       <string notr=\"true\">CutterTreeView::item\n{\n    padding-top: 1px;\n    padding-bottom: 1px;\n}</string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"ComboQuickFilterView\" name=\"quickFilterView\" native=\"true\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <action name=\"actionExport_Types\">\n   <property name=\"text\">\n    <string>Export Types</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Export Types</string>\n   </property>\n  </action>\n  <action name=\"actionLoad_New_Types\">\n   <property name=\"text\">\n    <string>Load New Types</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Load New Types</string>\n   </property>\n  </action>\n  <action name=\"actionDelete_Type\">\n   <property name=\"text\">\n    <string>Delete Type</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Delete Type</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>CutterTreeView</class>\n   <extends>QTreeView</extends>\n   <header>widgets/CutterTreeView.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ComboQuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/ComboQuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/VTablesWidget.cpp",
    "content": "#include <QShortcut>\n#include <QModelIndex>\n\n#include \"core/MainWindow.h\"\n#include \"common/Helpers.h\"\n#include \"shortcuts/ShortcutManager.h\"\n\n#include \"VTablesWidget.h\"\n#include \"ui_VTablesWidget.h\"\n\nVTableModel::VTableModel(QObject *parent) : QAbstractItemModel(parent) {}\n\nQModelIndex VTableModel::index(int row, int column, const QModelIndex &parent) const\n{\n    return createIndex(row, column, (quintptr)parent.isValid() ? parent.row() : -1);\n}\n\nQModelIndex VTableModel::parent(const QModelIndex &index) const\n{\n    return index.isValid() && index.internalId() != (quintptr)-1\n            ? this->index(index.internalId(), index.column())\n            : QModelIndex();\n}\n\nint VTableModel::rowCount(const QModelIndex &parent) const\n{\n    return parent.isValid()\n            ? (parent.parent().isValid() ? 0 : vtables.at(parent.row()).methods.count())\n            : vtables.count();\n}\n\nint VTableModel::columnCount(const QModelIndex &) const\n{\n    return Columns::COUNT;\n}\n\nQVariant VTableModel::data(const QModelIndex &index, int role) const\n{\n    QModelIndex parent = index.parent();\n    if (parent.isValid()) {\n        const BinClassMethodDescription &res = vtables.at(parent.row()).methods.at(index.row());\n        switch (role) {\n        case Qt::DisplayRole:\n            switch (index.column()) {\n            case NAME:\n                return res.name.isEmpty() ? tr(\"No Name found\") : res.name;\n            case ADDRESS:\n                return RzAddressString(res.addr);\n            }\n            break;\n        case VTableDescriptionRole:\n            return QVariant::fromValue(res);\n        default:\n            break;\n        }\n    } else\n        switch (role) {\n        case Qt::DisplayRole:\n            switch (index.column()) {\n            case NAME:\n                return tr(\"VTable\") + \" \" + QString::number(index.row() + 1);\n            case ADDRESS:\n                return RzAddressString(vtables.at(index.row()).addr);\n            }\n            break;\n        case VTableDescriptionRole: {\n            const VTableDescription &res = vtables.at(index.row());\n            return QVariant::fromValue(res);\n        }\n        default:\n            break;\n        }\n    return QVariant();\n}\n\nQVariant VTableModel::headerData(int section, Qt::Orientation, int role) const\n{\n    switch (role) {\n    case Qt::DisplayRole:\n        switch (section) {\n        case NAME:\n            return tr(\"Name\");\n        case ADDRESS:\n            return tr(\"Address\");\n        default:\n            break;\n        }\n        break;\n    default:\n        break;\n    }\n    return QVariant();\n}\n\nVTableSortFilterProxyModel::VTableSortFilterProxyModel(VTableModel *model, QObject *parent)\n    : QSortFilterProxyModel(parent)\n{\n    setSourceModel(model);\n    setFilterCaseSensitivity(Qt::CaseInsensitive);\n    setSortCaseSensitivity(Qt::CaseInsensitive);\n    setFilterKeyColumn(VTableModel::NAME);\n#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)\n    setRecursiveFilteringEnabled(true);\n#endif\n}\n\nbool VTableSortFilterProxyModel::filterAcceptsRow(int source_row,\n                                                  const QModelIndex &source_parent) const\n{\n    if (QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent))\n        return true;\n    if (source_parent.isValid())\n        return QSortFilterProxyModel::filterAcceptsRow(source_parent.row(), QModelIndex());\n#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)\n    else {\n        QAbstractItemModel *const model = sourceModel();\n        const QModelIndex source = model->index(source_row, 0, QModelIndex());\n        const int rows = model->rowCount(source);\n        for (int i = 0; i < rows; ++i)\n            if (QSortFilterProxyModel::filterAcceptsRow(i, source))\n                return true;\n    }\n#endif\n    return false;\n}\n\nVTablesWidget::VTablesWidget(MainWindow *main)\n    : CutterDockWidget(main), ui(new Ui::VTablesWidget), tree(new CutterTreeWidget(this))\n{\n    ui->setupUi(this);\n\n    // Add Status Bar footer\n    tree->addStatusBar(ui->verticalLayout);\n\n    model = new VTableModel(this);\n    proxy = new VTableSortFilterProxyModel(model, this);\n\n    ui->vTableTreeView->setModel(proxy);\n    ui->vTableTreeView->sortByColumn(VTableModel::ADDRESS, Qt::AscendingOrder);\n\n    // Esc to clear the filter entry\n    QShortcut *clear_shortcut = Shortcuts()->makeQShortcut(\"General.clearFilter\", this);\n    connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView,\n            &QuickFilterView::clearFilter);\n    clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    // Ctrl-F to show/hide the filter entry\n    QShortcut *search_shortcut = Shortcuts()->makeQShortcut(\"General.showFilter\", this);\n    connect(search_shortcut, &QShortcut::activated, ui->quickFilterView,\n            &QuickFilterView::showFilter);\n    search_shortcut->setContext(Qt::WidgetWithChildrenShortcut);\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, proxy,\n            &QSortFilterProxyModel::setFilterWildcard);\n    connect(ui->quickFilterView, &QuickFilterView::filterClosed, ui->vTableTreeView,\n            [this]() { ui->vTableTreeView->setFocus(); });\n\n    connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this,\n            [this] { tree->showItemsNumber(proxy->rowCount()); });\n\n    connect(Core(), &CutterCore::codeRebased, this, &VTablesWidget::refreshVTables);\n    connect(Core(), &CutterCore::refreshAll, this, &VTablesWidget::refreshVTables);\n\n    refreshDeferrer = createRefreshDeferrer([this]() { refreshVTables(); });\n}\n\nVTablesWidget::~VTablesWidget() {}\n\nvoid VTablesWidget::refreshVTables()\n{\n    if (!refreshDeferrer->attemptRefresh(nullptr)) {\n        return;\n    }\n\n    model->beginResetModel();\n    model->vtables = Core()->getAllVTables();\n    model->endResetModel();\n\n    qhelpers::adjustColumns(ui->vTableTreeView, 3, 0);\n\n    ui->vTableTreeView->setColumnWidth(0, 200);\n\n    tree->showItemsNumber(proxy->rowCount());\n}\n\nvoid VTablesWidget::on_vTableTreeView_doubleClicked(const QModelIndex &index)\n{\n    if (!index.isValid()) {\n        return;\n    }\n\n    QModelIndex parent = index.parent();\n    if (parent.isValid()) {\n        Core()->seekAndShow(index.data(VTableModel::VTableDescriptionRole)\n                                    .value<BinClassMethodDescription>()\n                                    .addr);\n    } else {\n        Core()->seekAndShow(\n                index.data(VTableModel::VTableDescriptionRole).value<VTableDescription>().addr);\n    }\n}\n"
  },
  {
    "path": "src/widgets/VTablesWidget.h",
    "content": "#ifndef VTABLESWIDGET_H\n#define VTABLESWIDGET_H\n\n#include <memory>\n\n#include <QTreeView>\n#include <QSortFilterProxyModel>\n\n#include \"core/Cutter.h\"\n#include \"CutterDockWidget.h\"\n#include \"CutterTreeWidget.h\"\n\nnamespace Ui {\nclass VTablesWidget;\n}\n\nclass MainWindow;\nclass VTablesWidget;\n\nclass VTableModel : public QAbstractItemModel\n{\n    Q_OBJECT\n\n    friend VTablesWidget;\n\nprivate:\n    QList<VTableDescription> vtables;\n\npublic:\n    enum Columns { NAME = 0, ADDRESS, COUNT };\n    static const int VTableDescriptionRole = Qt::UserRole;\n\n    VTableModel(QObject *parent = nullptr);\n\n    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;\n    QModelIndex parent(const QModelIndex &index) const;\n\n    int rowCount(const QModelIndex &parent = QModelIndex()) const;\n    int columnCount(const QModelIndex &parent = QModelIndex()) const;\n\n    QVariant data(const QModelIndex &index, int role) const;\n    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\nclass VTableSortFilterProxyModel : public QSortFilterProxyModel\n{\npublic:\n    VTableSortFilterProxyModel(VTableModel *model, QObject *parent = nullptr);\n\nprotected:\n    bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;\n};\n\nclass VTablesWidget : public CutterDockWidget\n{\n    Q_OBJECT\n\npublic:\n    explicit VTablesWidget(MainWindow *main);\n    ~VTablesWidget();\n\nprivate slots:\n    void refreshVTables();\n    void on_vTableTreeView_doubleClicked(const QModelIndex &index);\n\nprivate:\n    std::unique_ptr<Ui::VTablesWidget> ui;\n\n    VTableModel *model;\n    QSortFilterProxyModel *proxy;\n    CutterTreeWidget *tree;\n    RefreshDeferrer *refreshDeferrer;\n};\n\n#endif // VTABLESWIDGET_H\n"
  },
  {
    "path": "src/widgets/VTablesWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>VTablesWidget</class>\n <widget class=\"QDockWidget\" name=\"VTablesWidget\">\n  <property name=\"styleSheet\">\n   <string notr=\"true\"/>\n  </property>\n  <property name=\"windowTitle\">\n   <string>&amp;VTable</string>\n  </property>\n  <widget class=\"QWidget\" name=\"dockWidgetContents\">\n   <property name=\"sizePolicy\">\n    <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n     <horstretch>1</horstretch>\n     <verstretch>0</verstretch>\n    </sizepolicy>\n   </property>\n   <property name=\"minimumSize\">\n    <size>\n     <width>200</width>\n     <height>0</height>\n    </size>\n   </property>\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"CutterTreeView\" name=\"vTableTreeView\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Expanding\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"contextMenuPolicy\">\n       <enum>Qt::CustomContextMenu</enum>\n      </property>\n      <property name=\"styleSheet\">\n       <string notr=\"true\">\nCutterTreeView::item\n{\n   padding-top: 1px;\n   padding-bottom: 1px;\n}\n       </string>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"lineWidth\">\n       <number>0</number>\n      </property>\n      <property name=\"sizeAdjustPolicy\">\n       <enum>QAbstractScrollArea::AdjustToContents</enum>\n      </property>\n      <property name=\"indentation\">\n       <number>8</number>\n      </property>\n      <property name=\"sortingEnabled\">\n       <bool>true</bool>\n      </property>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"QuickFilterView\" name=\"quickFilterView\" native=\"true\"/>\n    </item>\n   </layout>\n  </widget>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>CutterTreeView</class>\n   <extends>QTreeView</extends>\n   <header>widgets/CutterTreeView.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>QuickFilterView</class>\n   <extends>QWidget</extends>\n   <header>widgets/QuickFilterView.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/widgets/VisualNavbar.cpp",
    "content": "#include \"VisualNavbar.h\"\n#include \"core/MainWindow.h\"\n#include \"common/TempConfig.h\"\n\n#include <QGraphicsView>\n#include <QComboBox>\n#include <QGraphicsScene>\n#include <QGraphicsRectItem>\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QJsonParseError>\n#include <QToolTip>\n#include <QMouseEvent>\n#include <QLayout>\n\n#include <array>\n#include <cmath>\n\nstatic const int NAVBAR_HEIGHT = 15;\nstatic const int LEGEND_HEIGHT = 25;\nstatic const int TOTAL_HEIGHT = NAVBAR_HEIGHT + LEGEND_HEIGHT;\n\nstatic const int LEGEND_BOX_SIZE = 16;\nstatic const int LEGEND_BOX_Y_OFFSET = 7; // Relative to navbar bottom\nstatic const int LEGEND_TEXT_X_OFFSET = 17;\nstatic const int LEGEND_TEXT_Y_OFFSET = -5;\nstatic const int LEGEND_ITEM_SPACING = 30;\n\nVisualNavbar::VisualNavbar(MainWindow *main, QWidget *parent)\n    : QToolBar(main),\n      graphicsView(new QGraphicsView),\n      seekGraphicsItem(nullptr),\n      PCGraphicsItem(nullptr),\n      legendItem(nullptr),\n      main(main)\n{\n    Q_UNUSED(parent);\n\n    blockTooltip = false;\n\n    setObjectName(\"visualNavbar\");\n    setWindowTitle(tr(\"Visual navigation bar\"));\n    //    setMovable(false);\n    setContentsMargins(0, 0, 0, 0);\n    // If line below is used, with the dark theme the paintEvent is not called\n    // and the result is wrong. Something to do with overwriting the style sheet :/\n    // setStyleSheet(\"QToolBar { border: 0px; border-bottom: 0px; border-top: 0px; border-width:\n    // 0px;}\");\n\n    /*\n    QComboBox *addsCombo = new QComboBox();\n    addsCombo->addItem(\"\");\n    addsCombo->addItem(\"Entry points\");\n    addsCombo->addItem(\"Marks\");\n    */\n    addWidget(this->graphicsView);\n    // addWidget(addsCombo);\n\n    connect(Core(), &CutterCore::seekChanged, this, &VisualNavbar::on_seekChanged);\n    connect(Core(), &CutterCore::registersChanged, this, &VisualNavbar::drawPCCursor);\n    connect(Core(), &CutterCore::refreshAll, this, &VisualNavbar::fetchAndPaintData);\n    connect(Core(), &CutterCore::functionsChanged, this, &VisualNavbar::fetchAndPaintData);\n    connect(Core(), &CutterCore::flagsChanged, this, &VisualNavbar::fetchAndPaintData);\n    connect(Core(), &CutterCore::globalVarsChanged, this, &VisualNavbar::fetchAndPaintData);\n\n    graphicsScene = new QGraphicsScene(this);\n\n    const QBrush bg = QBrush(QColor(74, 74, 74));\n\n    graphicsScene->setBackgroundBrush(bg);\n\n    this->graphicsView->setAlignment(Qt::AlignLeft);\n\n    bool legendEnabled = Config()->getNavBarLegendEnabled();\n    int initialHeight = legendEnabled ? TOTAL_HEIGHT : NAVBAR_HEIGHT;\n    this->graphicsView->setMinimumHeight(initialHeight);\n    this->graphicsView->setMaximumHeight(initialHeight);\n\n    this->graphicsView->setFrameShape(QFrame::NoFrame);\n    this->graphicsView->setRenderHints({});\n    this->graphicsView->setScene(graphicsScene);\n    this->graphicsView->setRenderHints(QPainter::Antialiasing);\n    this->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n    this->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n    // So the graphicsView doesn't intercept mouse events.\n    this->graphicsView->setEnabled(false);\n    this->graphicsView->installEventFilter(this);\n    this->graphicsView->setMouseTracking(true);\n    setMouseTracking(true);\n\n    setContextMenuPolicy(Qt::CustomContextMenu);\n    connect(this, &QWidget::customContextMenuRequested, this, &VisualNavbar::showLegendContextMenu);\n}\n\nunsigned int nextPow2(unsigned int n)\n{\n    unsigned int b = 0;\n    while (n) {\n        n >>= 1;\n        b++;\n    }\n    return (1u << b);\n}\n\nvoid VisualNavbar::paintEvent(QPaintEvent *event)\n{\n    Q_UNUSED(event);\n\n    QPainter painter(this);\n\n    auto w = static_cast<unsigned int>(width());\n    bool fetch = false;\n    if (statsWidth < w) {\n        statsWidth = nextPow2(w);\n        fetch = true;\n    } else if (statsWidth > w * 4) {\n        statsWidth = statsWidth > 0 ? statsWidth / 2 : 0;\n        fetch = true;\n    }\n\n    if (fetch) {\n        fetchAndPaintData();\n    } else if (previousWidth != w) {\n        this->previousWidth = w;\n        updateGraphicsScene();\n    }\n}\n\nvoid VisualNavbar::fetchAndPaintData()\n{\n    fetchStats();\n    updateGraphicsScene();\n}\n\nvoid VisualNavbar::fetchStats()\n{\n    static const ut64 blocksCount = 2048;\n\n    RzCoreLocked core(Core());\n    stats.reset(nullptr);\n    auto list =\n            fromOwned(rz_core_get_boundaries_select(core, \"search.from\", \"search.to\", \"search.in\"));\n    if (!list) {\n        return;\n    }\n    RzListIter *iter;\n    RzIOMap *map;\n    ut64 from = UT64_MAX;\n    ut64 to = 0;\n    CutterRzListForeach (list.get(), iter, RzIOMap, map) {\n        ut64 f = rz_itv_begin(map->itv);\n        ut64 t = rz_itv_end(map->itv);\n        if (f < from) {\n            from = f;\n        }\n        if (t > to) {\n            to = t;\n        }\n    }\n    to--; // rz_core_analysis_get_stats takes inclusive ranges\n    if (to < from) {\n        return;\n    }\n    stats.reset(\n            rz_core_analysis_get_stats(core, from, to, RZ_MAX(1, (to + 1 - from) / blocksCount)));\n}\n\nenum class DataType : int { Signature, Code, Data, String, Import, Symbol, Unexplored, Count };\n\nvoid VisualNavbar::updateGraphicsScene()\n{\n    bool legendVisible = Config()->getNavBarLegendEnabled();\n    graphicsScene->clear();\n    xToAddress.clear();\n    seekGraphicsItem = nullptr;\n    PCGraphicsItem = nullptr;\n    legendItem = nullptr;\n    graphicsScene->setBackgroundBrush(QBrush(Config()->getColor(\"gui.navbar.unexplored\")));\n\n    if (!stats) {\n        return;\n    }\n\n    int w = graphicsView->width();\n\n    RVA totalSize = stats->to - stats->from + 1;\n    RVA beginAddr = stats->from;\n\n    double widthPerByte = (double)w\n            / (double)(totalSize ? totalSize : pow(2.0, 64.0)); // account for overflow on 2^64\n    auto xFromAddr = [widthPerByte, beginAddr](RVA addr) -> double {\n        return (addr - beginAddr) * widthPerByte;\n    };\n\n    std::array<QBrush, static_cast<int>(DataType::Count)> dataTypeBrushes;\n    dataTypeBrushes[static_cast<int>(DataType::Signature)] =\n            QBrush(Config()->getColor(\"gui.navbar.signature\"));\n    dataTypeBrushes[static_cast<int>(DataType::Code)] =\n            QBrush(Config()->getColor(\"gui.navbar.code\"));\n    dataTypeBrushes[static_cast<int>(DataType::Data)] =\n            QBrush(Config()->getColor(\"gui.navbar.data\"));\n    dataTypeBrushes[static_cast<int>(DataType::String)] =\n            QBrush(Config()->getColor(\"gui.navbar.str\"));\n    dataTypeBrushes[static_cast<int>(DataType::Import)] =\n            QBrush(Config()->getColor(\"gui.navbar.import\"));\n    dataTypeBrushes[static_cast<int>(DataType::Symbol)] =\n            QBrush(Config()->getColor(\"gui.navbar.sym\"));\n    dataTypeBrushes[static_cast<int>(DataType::Unexplored)] =\n            QBrush(Config()->getColor(\"gui.navbar.unexplored\"));\n\n    DataType lastDataType = DataType::Unexplored;\n    QGraphicsRectItem *dataItem = nullptr;\n    QRectF dataItemRect(0.0, 0.0, 0.0, (double)NAVBAR_HEIGHT);\n    for (size_t i = 0; i < rz_vector_len(&stats->blocks); i++) {\n        RzCoreAnalysisStatsItem *block =\n                reinterpret_cast<RzCoreAnalysisStatsItem *>(rz_vector_index_ptr(&stats->blocks, i));\n        ut64 from = rz_core_analysis_stats_get_block_from(stats.get(), i);\n        ut64 to = rz_core_analysis_stats_get_block_to(stats.get(), i) + 1;\n        // Keep track of where which memory segment is mapped so we are able to convert from\n        // address to X coordinate and vice versa.\n        XToAddress x2a;\n        x2a.x_start = xFromAddr(from);\n        x2a.x_end = xFromAddr(to);\n        x2a.address_from = from;\n        x2a.address_to = to;\n        xToAddress.append(x2a);\n\n        DataType dataType;\n        if (block->signatures) {\n            dataType = DataType::Signature;\n        } else if (block->imports) {\n            dataType = DataType::Import;\n        } else if (block->functions) {\n            dataType = DataType::Code;\n        } else if (block->strings) {\n            dataType = DataType::String;\n        } else if (block->symbols) {\n            dataType = DataType::Symbol;\n        } else if (block->in_functions) {\n            dataType = DataType::Code;\n        } else if (block->perm & RZ_PERM_RW && !(block->perm & RZ_PERM_X)) {\n            dataType = DataType::Data;\n        } else {\n            lastDataType = DataType::Unexplored;\n            continue;\n        }\n\n        if (dataType == lastDataType) {\n            double r = xFromAddr(to);\n            if (r > dataItemRect.right()) {\n                dataItemRect.setRight(r);\n                dataItem->setRect(dataItemRect);\n            }\n            dataItem->setRect(dataItemRect);\n            continue;\n        }\n\n        dataItemRect.setX(xFromAddr(from));\n        dataItemRect.setRight(xFromAddr(to));\n\n        dataItem = new QGraphicsRectItem(dataItemRect);\n        dataItem->setPen(Qt::NoPen);\n        dataItem->setBrush(dataTypeBrushes[static_cast<int>(dataType)]);\n        graphicsScene->addItem(dataItem);\n\n        lastDataType = dataType;\n    }\n\n    drawSeekCursor();\n\n    legendItem = new QGraphicsItemGroup();\n\n    QColor themeBg = Config()->windowColorIsDark() ? palette().color(QPalette::Window)\n                                                   : Config()->getColor(\"gui.background\");\n\n    QGraphicsRectItem *lBg = new QGraphicsRectItem(0, NAVBAR_HEIGHT, w, LEGEND_HEIGHT);\n    lBg->setBrush(themeBg);\n    lBg->setPen(Qt::NoPen);\n    legendItem->addToGroup(lBg);\n\n    struct LegendPart\n    {\n        QString name;\n        QBrush brush;\n    };\n    QList<LegendPart> parts = {\n        { tr(\"Signatures\"), dataTypeBrushes[static_cast<int>(DataType::Signature)] },\n        { tr(\"Code\"), dataTypeBrushes[static_cast<int>(DataType::Code)] },\n        { tr(\"Data\"), dataTypeBrushes[static_cast<int>(DataType::Data)] },\n        { tr(\"Strings\"), dataTypeBrushes[static_cast<int>(DataType::String)] },\n        { tr(\"Imports\"), dataTypeBrushes[static_cast<int>(DataType::Import)] },\n        { tr(\"Symbols\"), dataTypeBrushes[static_cast<int>(DataType::Symbol)] },\n        { tr(\"Unexplored\"), dataTypeBrushes[static_cast<int>(DataType::Unexplored)] }\n    };\n\n    qreal curX = 10;\n    qreal legY = NAVBAR_HEIGHT + LEGEND_BOX_Y_OFFSET;\n    for (const auto &p : parts) {\n        QGraphicsRectItem *r = new QGraphicsRectItem(curX, legY, LEGEND_BOX_SIZE, LEGEND_BOX_SIZE);\n        r->setBrush(p.brush);\n        r->setPen(QPen(Qt::black, 0.5));\n        legendItem->addToGroup(r);\n\n        QGraphicsTextItem *t = new QGraphicsTextItem(p.name);\n        t->setPos(curX + LEGEND_TEXT_X_OFFSET, legY + LEGEND_TEXT_Y_OFFSET);\n        legendItem->addToGroup(t);\n        curX += t->boundingRect().width() + LEGEND_ITEM_SPACING;\n    }\n\n    graphicsScene->addItem(legendItem);\n    legendItem->setVisible(legendVisible);\n    graphicsScene->setSceneRect(0, 0, w, legendVisible ? TOTAL_HEIGHT : NAVBAR_HEIGHT);\n}\n\nvoid VisualNavbar::drawCursor(RVA addr, QColor color, QGraphicsRectItem *&graphicsItem)\n{\n    double cursor_x = addressToLocalX(addr);\n    if (graphicsItem != nullptr) {\n        graphicsScene->removeItem(graphicsItem);\n        delete graphicsItem;\n        graphicsItem = nullptr;\n    }\n    if (std::isnan(cursor_x)) {\n        return;\n    }\n    // Subtract 1 so the 2px wide cursor is centered\n    graphicsItem = new QGraphicsRectItem(cursor_x - 1, 0, 2, NAVBAR_HEIGHT);\n    graphicsItem->setPen(Qt::NoPen);\n    graphicsItem->setBrush(QBrush(color));\n    graphicsScene->addItem(graphicsItem);\n}\n\nvoid VisualNavbar::drawPCCursor()\n{\n    drawCursor(Core()->getProgramCounterValue(), Config()->getColor(\"gui.navbar.pc\"),\n               PCGraphicsItem);\n}\n\nvoid VisualNavbar::drawSeekCursor()\n{\n    drawCursor(Core()->getOffset(), Config()->getColor(\"gui.navbar.seek\"), seekGraphicsItem);\n}\n\nvoid VisualNavbar::on_seekChanged(RVA addr)\n{\n    Q_UNUSED(addr);\n    // Update cursor\n    this->drawSeekCursor();\n}\n\nbool VisualNavbar::eventFilter(QObject *watched, QEvent *event)\n{\n    switch (event->type()) {\n    case QEvent::MouseButtonPress: {\n        auto *mouseEvent = static_cast<QMouseEvent *>(event);\n        const QPoint scenePos = mouseEvent->pos();\n\n        if (scenePos.y() <= NAVBAR_HEIGHT) {\n            isDraggable = true;\n            handleMouseAction(mouseEvent, scenePos);\n            return true;\n        }\n        isDraggable = false;\n        break;\n    }\n    case QEvent::MouseMove: {\n        auto *mouseEvent = static_cast<QMouseEvent *>(event);\n        const QPoint scenePos = mouseEvent->pos();\n\n        if ((mouseEvent->buttons() & Qt::LeftButton && isDraggable)\n            || scenePos.y() <= NAVBAR_HEIGHT) {\n            handleMouseAction(mouseEvent, scenePos);\n        } else {\n            QToolTip::hideText();\n        }\n        return true;\n    }\n    default:\n        break;\n    }\n\n    return QToolBar::eventFilter(watched, event);\n}\n\nvoid VisualNavbar::handleMouseAction(QMouseEvent *event, const QPoint &scenePos)\n{\n    if (blockTooltip) {\n        return;\n    }\n\n    qreal x = scenePos.x();\n    RVA address = localXToAddress(x);\n    if (address != RVA_INVALID) {\n        auto tooltipPos = qhelpers::mouseEventGlobalPos(event);\n        blockTooltip = true; // on Haiku, the below call sometimes triggers another mouseMoveEvent,\n                             // causing infinite recursion\n        QToolTip::showText(tooltipPos, toolTipForAddress(address), this, this->rect());\n        blockTooltip = false;\n        if (event->buttons() & Qt::LeftButton) {\n            event->accept();\n            Core()->seek(address);\n        }\n    }\n}\n\nRVA VisualNavbar::localXToAddress(double x)\n{\n    for (const XToAddress &x2a : xToAddress) {\n        if ((x2a.x_start <= x) && (x <= x2a.x_end)) {\n            double offset = (x - x2a.x_start) / (x2a.x_end - x2a.x_start);\n            double size = x2a.address_to - x2a.address_from;\n            return x2a.address_from + (offset * size);\n        }\n    }\n    return RVA_INVALID;\n}\n\ndouble VisualNavbar::addressToLocalX(RVA address)\n{\n    for (const XToAddress &x2a : xToAddress) {\n        if ((x2a.address_from <= address) && (address < x2a.address_to)) {\n            double offset = (double)(address - x2a.address_from)\n                    / (double)(x2a.address_to - x2a.address_from);\n            double size = x2a.x_end - x2a.x_start;\n            return x2a.x_start + (offset * size);\n        }\n    }\n    return nan(\"\");\n}\n\nQList<QString> VisualNavbar::sectionsForAddress(RVA address)\n{\n    QList<QString> ret;\n    QList<SectionDescription> sections = Core()->getAllSections();\n    for (const SectionDescription &section : sections) {\n        if (address >= section.vaddr && address < section.vaddr + section.vsize) {\n            ret << section.name;\n        }\n    }\n    return ret;\n}\n\nQString VisualNavbar::toolTipForAddress(RVA address)\n{\n    QString ret = tr(\"Address: %1\").arg(RzAddressString(address));\n\n    // Don't append sections when a debug task is in progress to avoid freezing the interface\n    if (Core()->isDebugTaskInProgress()) {\n        return ret;\n    }\n\n    auto sections = sectionsForAddress(address);\n    if (sections.count()) {\n        ret += \"\\n\" + tr(\"Sections: \\n\");\n        bool first = true;\n        for (const QString &section : sections) {\n            if (!first) {\n                ret.append(QLatin1Char('\\n'));\n            } else {\n                first = false;\n            }\n            ret += \"  \" + section;\n        }\n    }\n    return ret;\n}\n\nvoid VisualNavbar::showLegendContextMenu(const QPoint &pos)\n{\n    QMenu menu(this);\n    QAction *toggleLegend = menu.addAction(tr(\"Show Legend\"));\n    toggleLegend->setCheckable(true);\n    toggleLegend->setChecked(Config()->getNavBarLegendEnabled());\n\n    if (menu.exec(mapToGlobal(pos))) {\n        bool checked = toggleLegend->isChecked();\n        Config()->setNavBarLegendEnabled(checked);\n        int h = checked ? TOTAL_HEIGHT : NAVBAR_HEIGHT;\n        this->graphicsView->setMinimumHeight(h);\n        this->graphicsView->setMaximumHeight(h);\n\n        // Force the layout to realize the height change\n        // Without this navbar jumps slightly down and then back up, when hiding legend\n        this->graphicsView->updateGeometry();\n        if (this->layout()) {\n            this->layout()->activate();\n        }\n\n        updateGraphicsScene();\n    }\n}\n"
  },
  {
    "path": "src/widgets/VisualNavbar.h",
    "content": "#ifndef VISUALNAVBAR_H\n#define VISUALNAVBAR_H\n\n#include <QToolBar>\n#include <QGraphicsScene>\n\n#include \"core/Cutter.h\"\n\n#include <rz_core.h>\n\n#include <memory>\n\nclass MainWindow;\nclass QGraphicsView;\nclass QGraphicsItemGroup;\n\nclass VisualNavbar : public QToolBar\n{\n    Q_OBJECT\n\n    struct XToAddress\n    {\n        double x_start;\n        double x_end;\n        RVA address_from;\n        RVA address_to;\n    };\n\npublic:\n    explicit VisualNavbar(MainWindow *main, QWidget *parent = nullptr);\n\npublic slots:\n    void paintEvent(QPaintEvent *event) override;\n    void updateGraphicsScene();\n\nprivate slots:\n    void fetchAndPaintData();\n    void fetchStats();\n    void drawSeekCursor();\n    void drawPCCursor();\n    void drawCursor(RVA addr, QColor color, QGraphicsRectItem *&graphicsItem);\n    void on_seekChanged(RVA addr);\n    void showLegendContextMenu(const QPoint &pos);\n\nprivate:\n    QGraphicsView *graphicsView;\n    QGraphicsScene *graphicsScene;\n    QGraphicsRectItem *seekGraphicsItem;\n    QGraphicsRectItem *PCGraphicsItem;\n    QGraphicsItemGroup *legendItem;\n    MainWindow *main;\n\n    UniquePtrC<RzCoreAnalysisStats, &rz_core_analysis_stats_free> stats;\n    unsigned int statsWidth = 0;\n    unsigned int previousWidth = 0;\n\n    QList<XToAddress> xToAddress;\n    bool blockTooltip;\n    bool isDraggable = true;\n\n    RVA localXToAddress(double x);\n    double addressToLocalX(RVA address);\n    QList<QString> sectionsForAddress(RVA address);\n    QString toolTipForAddress(RVA address);\n\n    bool eventFilter(QObject *watched, QEvent *event) override;\n    void handleMouseAction(QMouseEvent *event, const QPoint &scenePos);\n};\n\n#endif // VISUALNAVBAR_H\n"
  }
]