├── .github └── workflows │ └── build.yml ├── .gitignore ├── BSD-3-Clause ├── CMakeLists.txt ├── Config.cmake ├── LICENSE ├── README.md ├── bin └── lxqt-transupdate ├── cmake ├── LXQtTranslate.cmake └── LXQtTranslateDesktopYaml.pl ├── data ├── labwc_tweaks.appdata.xml.in ├── labwc_tweaks.desktop.in ├── labwc_tweaks.svg └── translations │ ├── labwc-tweaks.ts │ ├── labwc-tweaks_ar.ts │ ├── labwc-tweaks_ca.ts │ ├── labwc-tweaks_da.ts │ ├── labwc-tweaks_de.ts │ ├── labwc-tweaks_el.ts │ ├── labwc-tweaks_en_GB.ts │ ├── labwc-tweaks_en_IE.ts │ ├── labwc-tweaks_en_US.ts │ ├── labwc-tweaks_es.ts │ ├── labwc-tweaks_et.ts │ ├── labwc-tweaks_eu.ts │ ├── labwc-tweaks_fi.ts │ ├── labwc-tweaks_fr.ts │ ├── labwc-tweaks_gl.ts │ ├── labwc-tweaks_hu.ts │ ├── labwc-tweaks_it.ts │ ├── labwc-tweaks_ka.ts │ ├── labwc-tweaks_kab.ts │ ├── labwc-tweaks_ko.ts │ ├── labwc-tweaks_lt.ts │ ├── labwc-tweaks_ms.ts │ ├── labwc-tweaks_nl.ts │ ├── labwc-tweaks_pa.ts │ ├── labwc-tweaks_pl.ts │ ├── labwc-tweaks_pt.ts │ ├── labwc-tweaks_pt_BR.ts │ ├── labwc-tweaks_ro.ts │ ├── labwc-tweaks_ru.ts │ ├── labwc-tweaks_sl.ts │ ├── labwc-tweaks_tr.ts │ ├── labwc-tweaks_zh_CN.ts │ ├── labwc_tweaks.desktop.yaml │ ├── labwc_tweaks_ar.desktop.yaml │ ├── labwc_tweaks_ca.desktop.yaml │ ├── labwc_tweaks_da.desktop.yaml │ ├── labwc_tweaks_de.desktop.yaml │ ├── labwc_tweaks_el.desktop.yaml │ ├── labwc_tweaks_en_US.desktop.yaml │ ├── labwc_tweaks_et.desktop.yaml │ ├── labwc_tweaks_fi.desktop.yaml │ ├── labwc_tweaks_fr.desktop.yaml │ ├── labwc_tweaks_hu.desktop.yaml │ ├── labwc_tweaks_it.desktop.yaml │ ├── labwc_tweaks_ka.desktop.yaml │ ├── labwc_tweaks_ko.desktop.yaml │ ├── labwc_tweaks_lt.desktop.yaml │ ├── labwc_tweaks_ms.desktop.yaml │ ├── labwc_tweaks_nl.desktop.yaml │ ├── labwc_tweaks_pl.desktop.yaml │ ├── labwc_tweaks_pt.desktop.yaml │ ├── labwc_tweaks_pt_BR.desktop.yaml │ ├── labwc_tweaks_ru.desktop.yaml │ └── labwc_tweaks_zh_CN.desktop.yaml ├── src ├── .clang-format ├── .editorconfig ├── environment.cpp ├── environment.h ├── evdev-lst-layouts.h ├── gen-layout-list ├── layoutmodel.cpp ├── layoutmodel.h ├── main.cpp ├── maindialog.cpp ├── maindialog.h ├── maindialog.ui ├── theme.c ├── theme.h ├── xml.c └── xml.h └── tests ├── t1000-add-xpath-node.c ├── t1001-nodenames.c ├── tap.c └── tap.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | # Manual builds and PRs in all branches 7 | pull_request: 8 | branches: 9 | - '*' 10 | workflow_dispatch: 11 | branches: 12 | - '*' 13 | jobs: 14 | build: 15 | name: Build 16 | timeout-minutes: 20 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | name: [ 21 | Arch, 22 | Debian 23 | ] 24 | include: 25 | - name: Arch 26 | os: ubuntu-latest 27 | container: archlinux:base-devel 28 | 29 | - name: Debian 30 | os: ubuntu-latest 31 | container: debian:testing 32 | 33 | runs-on: ${{ matrix.os }} 34 | container: ${{ matrix.container }} 35 | 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v4 39 | 40 | - name: Install Arch Linux dependencies 41 | if: matrix.name == 'Arch' 42 | run: | 43 | pacman-key --init 44 | pacman -Syu --noconfirm 45 | packages=( 46 | clang 47 | cmake 48 | git 49 | libxml2 50 | qt6-base 51 | qt6-tools 52 | ) 53 | pacman -S --noconfirm ${packages[@]} 54 | 55 | - name: Install Debian Testing dependencies 56 | if: matrix.name == 'Debian' 57 | run: | 58 | apt-get update 59 | apt-get upgrade -y 60 | apt-get install -y cmake clang g++ gcc git \ 61 | libglib2.0 libgl1-mesa-dev libxml2-dev pkg-config \ 62 | qt6-base-dev qt6-l10n-tools \ 63 | qt6-tools-dev qt6-tools-dev-tools 64 | 65 | # These builds are executed on all runners 66 | - name: Build with gcc 67 | run: | 68 | export CC=gcc 69 | export CXX=g++ 70 | cmake \ 71 | -D CMAKE_BUILD_TYPE=Release \ 72 | -B build-gcc \ 73 | -S . 74 | cmake --build build-gcc --verbose 75 | 76 | - name: Build with clang 77 | run: | 78 | export CC=clang 79 | export CXX=clang++ 80 | cmake \ 81 | -D CMAKE_BUILD_TYPE=Release \ 82 | -B build-clang \ 83 | -S . 84 | cmake --build build-clang --verbose 85 | 86 | - name: Run tests 87 | run: | 88 | ctest --verbose --force-new-ctest-process --test-dir build-gcc 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | *.user* 3 | -------------------------------------------------------------------------------- /BSD-3-Clause: -------------------------------------------------------------------------------- 1 | License: BSD-3-Clause 2 | Redistribution and use in source and binary forms, with or without 3 | modification, are permitted provided that the following conditions 4 | are met: 5 | 1. Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | 2. Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | 3. Neither the name of the University nor the names of its contributors 11 | may be used to endorse or promote products derived from this software 12 | without specific prior written permission. 13 | . 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR 18 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(labwc-tweaks 3 | VERSION 0.1.0 4 | LANGUAGES C CXX 5 | ) 6 | set(CMAKE_AUTOUIC ON) 7 | set(CMAKE_AUTOMOC ON) 8 | set(CMAKE_AUTORCC ON) 9 | 10 | set(CMAKE_CXX_EXTENSIONS OFF) 11 | set(CMAKE_CXX_STANDARD 17) 12 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 13 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 14 | 15 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 16 | 17 | include(Config.cmake) 18 | 19 | # QtCreator doesn't use the system locale and I see no way to prefix with LANG=XYZ.UTF-8 the command, 20 | # so enabling these 2 settings we can test any language, see initLocale() in main.cpp. 21 | set(PROJECT_TRANSLATION_TEST_ENABLED 0 CACHE STRING "Whether to enable translation testing [default: 0]") 22 | set(PROJECT_TRANSLATION_TEST_LANGUAGE "en" CACHE STRING "Country code of language to test in IDE [default: en]") 23 | set(PROJECT_QT_VERSION 6 CACHE STRING "Qt version to use [Default: 6]") 24 | option(PROJECT_TRANSLATIONS_UPDATE "Update source translations [default: OFF]" OFF) 25 | 26 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize=undefined") 27 | #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=undefined") 28 | 29 | find_package(QT NAMES Qt${PROJECT_QT_VERSION}) 30 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools) 31 | find_package(PkgConfig REQUIRED) 32 | pkg_search_module(GLIB REQUIRED glib-2.0) 33 | find_package(LibXml2 REQUIRED) 34 | 35 | set(PROJECT_SOURCES 36 | src/main.cpp 37 | src/maindialog.ui 38 | src/maindialog.cpp 39 | src/maindialog.h 40 | src/layoutmodel.cpp 41 | src/layoutmodel.h 42 | src/environment.cpp 43 | src/environment.h 44 | src/theme.c 45 | src/theme.h 46 | src/xml.c 47 | src/xml.h 48 | ) 49 | set(PROJECT_OTHER_FILES 50 | .github/workflows/build.yml 51 | README.md 52 | ) 53 | file(GLOB PROJECT_TRANSLATION_SOURCES "${PROJECT_TRANSLATIONS_DIR}/*") 54 | source_group("" FILES 55 | ${PROJECT_SOURCES} 56 | ${PROJECT_TRANSLATION_SOURCES} 57 | ) 58 | #=================================================================================================== 59 | # Translations 60 | #=================================================================================================== 61 | include(GNUInstallDirs) 62 | include(LXQtTranslate) 63 | 64 | lxqt_translate_ts(PROJECT_QM_FILES 65 | SOURCES ${PROJECT_SOURCES} 66 | TEMPLATE ${PROJECT_ID} 67 | TRANSLATION_DIR "${PROJECT_TRANSLATIONS_DIR}" 68 | UPDATE_TRANSLATIONS ${PROJECT_TRANSLATIONS_UPDATE} 69 | INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/${PROJECT_ID}/translations" 70 | ) 71 | lxqt_translate_desktop(PROJECT_DESKTOP_FILES 72 | SOURCES "${CMAKE_BINARY_DIR}/${PROJECT_APPSTREAM_ID}.desktop.in" 73 | TRANSLATION_DIR "${PROJECT_TRANSLATIONS_DIR}" 74 | USE_YAML 75 | ) 76 | 77 | #=================================================================================================== 78 | # Tests 79 | #=================================================================================================== 80 | include(CTest) 81 | 82 | add_executable(t1000 tests/t1000-add-xpath-node.c tests/tap.c src/xml.c) 83 | target_link_libraries(t1000 PRIVATE ${GLIB_LDFLAGS} ${LIBXML2_LIBRARIES}) 84 | target_include_directories(t1000 PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR}) 85 | add_test(t1000 t1000) 86 | 87 | add_executable(t1001 tests/t1001-nodenames.c tests/tap.c) 88 | target_link_libraries(t1001 PRIVATE ${GLIB_LDFLAGS} ${LIBXML2_LIBRARIES}) 89 | target_include_directories(t1001 PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR}) 90 | add_test(t1001 t1001) 91 | 92 | #=================================================================================================== 93 | # Application 94 | #=================================================================================================== 95 | qt_add_executable(${PROJECT_NAME} MANUAL_FINALIZATION 96 | ${PROJECT_SOURCES} 97 | ${PROJECT_DESKTOP_FILES} 98 | ${PROJECT_OTHER_FILES} 99 | ${PROJECT_QM_FILES} 100 | ${PROJECT_TRANSLATION_SOURCES} 101 | ) 102 | set(PROJECT_ICON_SYSTEM_PATH "${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/scalable/apps") 103 | file(COPY_FILE "${CMAKE_SOURCE_DIR}/data/${PROJECT_APPSTREAM_ID}.svg" 104 | "${CMAKE_BINARY_DIR}/${PROJECT_APPSTREAM_ID}.svg" 105 | ) 106 | target_compile_definitions(${PROJECT_NAME} PRIVATE 107 | APPLICATION_NAME="${PROJECT_NAME}" 108 | APPLICATION_VERSION="${PROJECT_VERSION}" 109 | PROJECT_ID="${PROJECT_ID}" 110 | PROJECT_APPSTREAM_ID="${PROJECT_APPSTREAM_ID}" 111 | PROJECT_DATA_DIR="${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_NAME}" 112 | PROJECT_ICON_SYSTEM_PATH="${PROJECT_ICON_SYSTEM_PATH}" 113 | PROJECT_TRANSLATION_TEST_ENABLED=${PROJECT_TRANSLATION_TEST_ENABLED} 114 | PROJECT_TRANSLATION_TEST_LANGUAGE="${PROJECT_TRANSLATION_TEST_LANGUAGE}" 115 | ) 116 | target_include_directories(${PROJECT_NAME} PRIVATE 117 | ${GLIB_INCLUDE_DIRS} 118 | ${LIBXML2_INCLUDE_DIR} 119 | src 120 | ) 121 | target_link_libraries(${PROJECT_NAME} PRIVATE 122 | Qt${QT_VERSION_MAJOR}::Widgets 123 | ${GLIB_LDFLAGS} 124 | ${LIBXML2_LIBRARIES} 125 | ) 126 | #target_link_options(${PROJECT_NAME} BEFORE PUBLIC -fsanitize=undefined PUBLIC -fsanitize=address) 127 | #=================================================================================================== 128 | # Installation 129 | #=================================================================================================== 130 | configure_file("${CMAKE_SOURCE_DIR}/data/${PROJECT_APPSTREAM_ID}.desktop.in" 131 | "${CMAKE_BINARY_DIR}/${PROJECT_APPSTREAM_ID}.desktop.in" @ONLY 132 | ) 133 | configure_file("${CMAKE_SOURCE_DIR}/data/${PROJECT_APPSTREAM_ID}.appdata.xml.in" 134 | "${CMAKE_BINARY_DIR}/${PROJECT_APPSTREAM_ID}.appdata.xml" @ONLY 135 | ) 136 | install(TARGETS ${PROJECT_NAME} 137 | BUNDLE DESTINATION . 138 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 139 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 140 | ) 141 | install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_APPSTREAM_ID}.appdata.xml" 142 | DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo" 143 | ) 144 | install(FILES "${PROJECT_DESKTOP_FILES}" 145 | DESTINATION "${CMAKE_INSTALL_DATADIR}/applications" 146 | ) 147 | install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_APPSTREAM_ID}.svg" 148 | # Don't use PROJECT_ICON_SYSTEM_PATH here which is absolute and doesn't take prefixes into account 149 | DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps" 150 | ) 151 | qt_finalize_executable(${PROJECT_NAME}) 152 | #=================================================================================================== 153 | # Configuration report 154 | #=================================================================================================== 155 | message(STATUS " 156 | Project name: ${PROJECT_NAME} 157 | Version: ${PROJECT_VERSION} 158 | Qt version: ${QT_VERSION} 159 | Build type: ${CMAKE_BUILD_TYPE} 160 | Install prefix: ${CMAKE_INSTALL_PREFIX} 161 | Update translations before build: ${PROJECT_TRANSLATIONS_UPDATE} 162 | ") 163 | -------------------------------------------------------------------------------- /Config.cmake: -------------------------------------------------------------------------------- 1 | #=============================================================================== 2 | # Editable project configuration 3 | # 4 | # Essential, non translatable application information (except DESCRIPTION). 5 | # Translatable strings are passed via code. 6 | #=============================================================================== 7 | set(PROJECT_ID "labwc-tweaks") 8 | list(APPEND PROJECT_CATEGORIES "Qt;Settings;DesktopSettings") # Freedesktop menu categories 9 | list(APPEND PROJECT_KEYWORDS "labwc;wayland;compositor") 10 | set(PROJECT_AUTHOR_NAME "Labwc Team") 11 | set(PROJECT_COPYRIGHT_YEAR "2024") # TODO: from git 12 | set(PROJECT_DESCRIPTION "Labwc Wayland compositor settings") 13 | set(PROJECT_ORGANIZATION_NAME "labwc") 14 | set(PROJECT_ORGANIZATION_URL "${PROJECT_ORGANIZATION_NAME}.github.io") 15 | set(PROJECT_ORGANIZATION_ID "io.github.${PROJECT_ORGANIZATION_NAME}") 16 | set(PROJECT_REPOSITORY_URL "https://github.com/${PROJECT_ORGANIZATION_NAME}/${PROJECT_ID}") 17 | set(PROJECT_REPOSITORY_BRANCH "master") 18 | set(PROJECT_HOMEPAGE_URL ${PROJECT_REPOSITORY_URL}) # TODO: "https://${PROJECT_ORGANIZATION_URL}/${PROJECT_ID}" 19 | set(PROJECT_SPDX_ID "GPL-2.0-only") 20 | set(PROJECT_TRANSLATIONS_DIR "${CMAKE_SOURCE_DIR}/data/translations") 21 | set(PROJECT_SCREENSHOT_URL "") 22 | #=============================================================================== 23 | # Appstream 24 | #=============================================================================== 25 | set(PROJECT_APPSTREAM_SPDX_ID "CC0-1.0") 26 | set(PROJECT_APPSTREAM_ID "labwc_tweaks") 27 | #=============================================================================== 28 | # Adapt to CMake variables 29 | #=============================================================================== 30 | set(${PROJECT_NAME}_DESCRIPTION "${PROJECT_DESCRIPTION}") 31 | set(${PROJECT_NAME}_HOMEPAGE_URL "${PROJECT_HOMEPAGE_URL}") 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # labwc-tweaks 2 | 3 | [![CI]](https://github.com/labwc/labwc-tweaks/actions/workflows/build.yml) 4 | 5 | This is a GUI 'Settings' application for labwc. 6 | 7 | ### dependencies 8 | 9 | Runtime: 10 | 11 | - Qt6 base 12 | - libxml2 13 | - glib2 14 | 15 | Build: 16 | 17 | - CMake 18 | - Qt Linguist Tools 19 | - Git (optional, to pull latest VCS checkouts) 20 | 21 | ### build 22 | 23 | `CMAKE_BUILD_TYPE` is usually set to `Release`, though `None` might be a valid [alternative].
24 | `CMAKE_INSTALL_PREFIX` has to be set to `/usr` on most operating systems. 25 | 26 | ```bash 27 | cmake -B build -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr -W no-dev 28 | cmake --build build --verbose 29 | ``` 30 | 31 | ### test 32 | 33 | ```bash 34 | ctest --verbose --force-new-ctest-process --test-dir build 35 | ``` 36 | 37 | ### install 38 | 39 | Using `sudo make install` is discouraged, instead use the system package manager where possible. 40 | 41 | In this packaging simulation, CMake installs the binary to /usr/bin 42 | and data files to their respective locations in a "package" directory. 43 | 44 | ```bash 45 | DESTDIR="$(pwd)/package" cmake --install build 46 | ``` 47 | 48 | If you find it a useful tool and want to expand its scope, feel free. 49 | 50 | ### packages 51 | 52 | [![Packaging status]](https://repology.org/project/labwc-tweaks/versions) 53 | 54 | ### translations 55 | 56 | For contributing translations the [LXQt Weblate] platform can be used. 57 | 58 | [![Translation status]](https://translate.lxqt-project.org/widgets/labwc/) 59 | 60 | ### licenses 61 | 62 | - labwc-tweaks is licensed under the [GPL-2.0-only] license 63 | - LXQt build tools cmake modules are licensed under the [BSD-3-Clause] license. 64 | 65 | 66 | [alternative]: https://wiki.archlinux.org/title/CMake_package_guidelines#Fixing_the_automatic_optimization_flag_override 67 | [BSD-3-Clause]: BSD-3-Clause 68 | [CI]: https://github.com/labwc/labwc-tweaks/actions/workflows/build.yml/badge.svg 69 | [GPL-2.0-only]: LICENSE 70 | [LXQt Weblate]: https://translate.lxqt-project.org/projects/labwc/labwc-tweaks/ 71 | [Packaging status]: https://repology.org/badge/vertical-allrepos/labwc-tweaks.svg 72 | [Translation status]: https://translate.lxqt-project.org/widgets/labwc/-/labwc-tweaks/multi-auto.svg 73 | -------------------------------------------------------------------------------- /bin/lxqt-transupdate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #============================================================================= 4 | # Copyright 2018 Alf Gaida 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # 1. Redistributions of source code must retain the copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # 2. Redistributions in binary form must reproduce the copyright 13 | # notice, this list of conditions and the following disclaimer in the 14 | # documentation and/or other materials provided with the distribution. 15 | # 3. The name of the author may not be used to endorse or promote products 16 | # derived from this software without specific prior written permission. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | #============================================================================= 29 | 30 | # lxqt-transupdate 31 | # Update LXQt translation files. 32 | 33 | # just to be sure - for distributions that user qtchooser 34 | # Debian and derivatives, Fedora, FreeBSD, Mageia, OpenMandriva, PCLinuxOS 35 | export QT_SELECT=6 36 | 37 | TEMPLATES=$(find . -name \*.ts | grep -v '_') 38 | for i in $TEMPLATES; do 39 | echo "\n\n==== $i ====\n" 40 | TRANSDIR=$(dirname $i) 41 | SOURCEDIR=$(dirname $TRANSDIR) 42 | # template-update 43 | echo "== Template Update ==" 44 | echo "lupdate $SOURCEDIR -ts $i -locations absolute -no-obsolete\n" 45 | lupdate $SOURCEDIR -ts $i -locations absolute -no-obsolete 46 | echo 47 | echo "== Language updates ==" 48 | echo "lupdate $SOURCEDIR -ts $TRANSDIR/*_*.ts -locations absolute -no-obsolete\n" 49 | lupdate $SOURCEDIR -ts $TRANSDIR/*_*.ts -locations absolute -no-obsolete 50 | done 51 | -------------------------------------------------------------------------------- /cmake/LXQtTranslate.cmake: -------------------------------------------------------------------------------- 1 | #============================================================================= 2 | # Copyright 2014 Luís Pereira 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # 1. Redistributions of source code must retain the copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 2. Redistributions in binary form must reproduce the copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # 3. The name of the author may not be used to endorse or promote products 14 | # derived from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | #============================================================================= 27 | # 28 | # funtion lxqt_translate_ts(qmFiles 29 | # [UPDATE_TRANSLATIONS [Yes | No]] 30 | # SOURCES 31 | # [UPDATE_OPTIONS] update_options 32 | # [TEMPLATE] translation_template 33 | # [TRANSLATION_DIR] translation_directory 34 | # [INSTALL_DIR] install_directory 35 | # [COMPONENT] component 36 | # ) 37 | # Output: 38 | # qmFiles The generated compiled translations (.qm) files 39 | # 40 | # UPDATE_TRANSLATIONS Optional flag. Setting it to Yes, extracts and 41 | # compiles the translations. Setting it No, only 42 | # compiles them. 43 | # 44 | # UPDATE_OPTIONS Optional options to lupdate when UPDATE_TRANSLATIONS 45 | # is True. 46 | # 47 | # TEMPLATE Optional translations files base name. Defaults to 48 | # ${PROJECT_NAME}. An .ts extensions is added. 49 | # 50 | # TRANSLATION_DIR Optional path to the directory with the .ts files, 51 | # relative to the CMakeList.txt. Defaults to 52 | # "translations". 53 | # 54 | # INSTALL_DIR Optional destination of the file compiled files (qmFiles). 55 | # If not present no installation is performed 56 | # 57 | # COMPONENT Optional install component. Only effective if INSTALL_DIR 58 | # present. Defaults to "Runtime". 59 | # 60 | function(lxqt_translate_ts qmFiles) 61 | set(oneValueArgs 62 | UPDATE_TRANSLATIONS 63 | TEMPLATE 64 | TRANSLATION_DIR 65 | INSTALL_DIR 66 | COMPONENT 67 | ) 68 | set(multiValueArgs SOURCES UPDATE_OPTIONS) 69 | cmake_parse_arguments(TR "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 70 | 71 | if (NOT DEFINED TR_UPDATE_TRANSLATIONS) 72 | set(TR_UPDATE_TRANSLATIONS "No") 73 | endif() 74 | 75 | if (NOT DEFINED TR_UPDATE_OPTIONS) 76 | set(TR_UPDATE_OPTIONS "") 77 | endif() 78 | 79 | 80 | if(NOT DEFINED TR_TEMPLATE) 81 | set(TR_TEMPLATE "${PROJECT_NAME}") 82 | endif() 83 | 84 | if (NOT DEFINED TR_TRANSLATION_DIR) 85 | set(TR_TRANSLATION_DIR "translations") 86 | endif() 87 | get_filename_component(TR_TRANSLATION_DIR "${TR_TRANSLATION_DIR}" ABSOLUTE) 88 | 89 | if (EXISTS "${TR_TRANSLATION_DIR}") 90 | file(GLOB tsFiles "${TR_TRANSLATION_DIR}/${TR_TEMPLATE}_*.ts") 91 | set(templateFile "${TR_TRANSLATION_DIR}/${TR_TEMPLATE}.ts") 92 | endif () 93 | 94 | if (TR_UPDATE_TRANSLATIONS) 95 | qt6_create_translation(QMS 96 | ${TR_SOURCES} 97 | ${templateFile} 98 | OPTIONS ${TR_UPDATE_OPTIONS} 99 | ) 100 | qt6_create_translation(QM 101 | ${TR_SOURCES} 102 | ${tsFiles} 103 | OPTIONS ${TR_UPDATE_OPTIONS} 104 | ) 105 | else() 106 | qt6_add_translation(QM ${tsFiles}) 107 | endif() 108 | 109 | if(TR_UPDATE_TRANSLATIONS) 110 | add_custom_target("update_${TR_TEMPLATE}_ts" ALL DEPENDS ${QMS}) 111 | endif() 112 | 113 | if(DEFINED TR_INSTALL_DIR) 114 | if(NOT DEFINED TR_COMPONENT) 115 | set(TR_COMPONENT "Runtime") 116 | endif() 117 | 118 | install(FILES ${QM} 119 | DESTINATION "${TR_INSTALL_DIR}" 120 | COMPONENT "${TR_COMPONENT}" 121 | ) 122 | endif() 123 | 124 | set(${qmFiles} ${QM} PARENT_SCOPE) 125 | endfunction() 126 | #============================================================================= 127 | # The lxqt_translate_desktop() function was copied from the 128 | # LXQt LXQtTranslate.cmake 129 | # 130 | # Original Author: Alexander Sokolov 131 | # 132 | # funtion lxqt_translate_desktop(_RESULT 133 | # SOURCES 134 | # [TRANSLATION_DIR] translation_directory 135 | # [USE_YAML] 136 | # ) 137 | # Output: 138 | # _RESULT The generated .desktop (.desktop) files 139 | # 140 | # Input: 141 | # 142 | # SOURCES List of input desktop files (.destktop.in) to be translated 143 | # (merged), relative to the CMakeList.txt. 144 | # 145 | # TRANSLATION_DIR Optional path to the directory with the .ts files, 146 | # relative to the CMakeList.txt. Defaults to 147 | # "translations". 148 | # 149 | # USE_YAML Flag if *.desktop.yaml translation should be used. 150 | #============================================================================= 151 | find_package(Perl REQUIRED) 152 | 153 | function(lxqt_translate_desktop _RESULT) 154 | # Parse arguments *************************************** 155 | set(options USE_YAML) 156 | set(oneValueArgs TRANSLATION_DIR) 157 | set(multiValueArgs SOURCES) 158 | 159 | cmake_parse_arguments(_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 160 | 161 | # check for unknown arguments 162 | set(_UNPARSED_ARGS ${_ARGS_UNPARSED_ARGUMENTS}) 163 | if (NOT ${_UNPARSED_ARGS} STREQUAL "") 164 | MESSAGE(FATAL_ERROR 165 | "Unknown arguments '${_UNPARSED_ARGS}'.\n" 166 | "See lxqt_translate_desktop() documentation for more information.\n" 167 | ) 168 | endif() 169 | 170 | if (NOT DEFINED _ARGS_SOURCES) 171 | set(${_RESULT} "" PARENT_SCOPE) 172 | return() 173 | else() 174 | set(_sources ${_ARGS_SOURCES}) 175 | endif() 176 | 177 | if (NOT DEFINED _ARGS_TRANSLATION_DIR) 178 | set(_translationDir "translations") 179 | else() 180 | set(_translationDir ${_ARGS_TRANSLATION_DIR}) 181 | endif() 182 | 183 | get_filename_component (_translationDir ${_translationDir} ABSOLUTE) 184 | 185 | foreach (_inFile ${_sources}) 186 | get_filename_component(_inFile ${_inFile} ABSOLUTE) 187 | get_filename_component(_fileName ${_inFile} NAME_WE) 188 | #Extract the real extension ............ 189 | get_filename_component(_fileExt ${_inFile} EXT) 190 | string(REPLACE ".in" "" _fileExt ${_fileExt}) 191 | string(REGEX REPLACE "^\\.([^.].*)$" "\\1" _fileExt ${_fileExt}) 192 | #....................................... 193 | set(_outFile "${CMAKE_CURRENT_BINARY_DIR}/${_fileName}.${_fileExt}") 194 | 195 | if (_ARGS_USE_YAML) 196 | add_custom_command(OUTPUT ${_outFile} 197 | COMMAND ${PERL_EXECUTABLE} ${CMAKE_SOURCE_DIR}/cmake/LXQtTranslateDesktopYaml.pl ${_inFile} ${_fileName} ${_translationDir}/${_fileName}[_.]*${_fileExt}.yaml >> ${_outFile} 198 | VERBATIM 199 | COMMENT "Generating ${_fileName}.${_fileExt}" 200 | ) 201 | else () 202 | file(GLOB _translations 203 | ${_translationDir}/${_fileName}[_.]*${_fileExt} 204 | ) 205 | list(SORT _translations) 206 | add_custom_command(OUTPUT ${_outFile} 207 | COMMAND grep -v -a "#TRANSLATIONS_DIR=" ${_inFile} > ${_outFile} 208 | VERBATIM 209 | COMMENT "Generating ${_fileName}.${_fileExt}" 210 | ) 211 | if (_translations) 212 | add_custom_command(OUTPUT ${_outFile} 213 | COMMAND grep -h -a "\\[.*]\\s*=" ${_translations} >> ${_outFile} 214 | VERBATIM APPEND 215 | ) 216 | endif () 217 | endif () 218 | 219 | set(__result ${__result} ${_outFile}) 220 | endforeach() 221 | 222 | set(${_RESULT} ${__result} PARENT_SCOPE) 223 | endfunction(lxqt_translate_desktop) 224 | -------------------------------------------------------------------------------- /cmake/LXQtTranslateDesktopYaml.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | 3 | binmode(STDOUT, ":encoding(utf8)"); 4 | binmode(STDERR, ":encoding(utf8)"); 5 | 6 | my $desktop_in = $ARGV[0]; 7 | my $filename_base = $ARGV[1]; 8 | my @translation_files = glob($ARGV[2]); 9 | 10 | my $section_re = qr/^\[([^\]]+)]/o; 11 | my $lang_re = qr/^.*${filename_base}_([^.]+)\..+$/o; 12 | my $strip_re = qr/#TRANSLATIONS_DIR=/o; 13 | 14 | sub flush_translations { 15 | my ($curr_section) = @_; 16 | if (defined $curr_section) { 17 | my $transl_yaml_re = qr/^${curr_section}\/([^: ]+) ?: *([^ ].*)$/; 18 | foreach my $file (@translation_files) { 19 | my $language = ($file =~ $lang_re ? "[$1]" : ''); 20 | open(my $trans_fh, '<:encoding(UTF-8)', $file) or next; 21 | while (my $trans_l = <$trans_fh>) { 22 | if ($trans_l =~ $transl_yaml_re) 23 | { 24 | my ($key, $value) = ($1, $2); 25 | $value =~ s/^\s+|\s+$//; $value =~ s/^['"]//; $value =~ s/['"]$//; 26 | if (length($value)) 27 | { 28 | # Don't flush empty (untranslated) strings 29 | print(STDOUT "$key$language=$value\n"); 30 | } 31 | } 32 | } 33 | close($trans_fh); 34 | } 35 | } 36 | } 37 | 38 | 39 | open(my $fh, '<:encoding(UTF-8)', $desktop_in) or die "Could not open file '$desktop_in' $!"; 40 | my $curr_section = undef; 41 | while (my $line = <$fh>) { 42 | if ($line =~ $section_re) { 43 | flush_translations($curr_section); 44 | $curr_section = $1; 45 | } 46 | $line =~ $strip_re or print(STDOUT $line); 47 | } 48 | flush_translations($curr_section); 49 | 50 | close($fh); 51 | -------------------------------------------------------------------------------- /data/labwc_tweaks.appdata.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | @PROJECT_APPSTREAM_ID@ 4 | @PROJECT_APPSTREAM_SPDX_ID@ 5 | @PROJECT_SPDX_ID@ 6 | @PROJECT_NAME@ 7 | @PROJECT_DESCRIPTION@ 8 | 9 | @PROJECT_DESCRIPTION_HTML@ 10 | @PROJECT_ORGANIZATION_NAME@ 11 | 12 | @PROJECT_AUTHOR_NAME@ 13 | 14 | @PROJECT_HOMEPAGE_URL@ 15 | 16 | 17 | Screenshot 18 | @PROJECT_SCREENSHOT_URL@ 19 | 20 | 21 | 22 | @PROJECT_ID@ 23 | 24 | @PROJECT_APPSTREAM_ID@.desktop 25 | @PROJECT_KEYWORDS_HTML@ 26 | @PROJECT_RELEASES_HTML@ 27 | 28 | -------------------------------------------------------------------------------- /data/labwc_tweaks.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Exec=@PROJECT_ID@ 4 | Icon=@PROJECT_APPSTREAM_ID@ 5 | Categories=@PROJECT_CATEGORIES@; 6 | 7 | -------------------------------------------------------------------------------- /data/labwc_tweaks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 10 | 11 | 12 | 13 | Corner Radius 14 | 15 | 16 | 17 | 18 | Openbox Theme 19 | 20 | 21 | 22 | 23 | Behaviour 24 | 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | 45 | 46 | 47 | 48 | Cursor Size 49 | 50 | 51 | 52 | 53 | Natural Scroll 54 | 55 | 56 | 57 | 58 | Add 59 | 60 | 61 | 62 | 63 | Remove 64 | 65 | 66 | 67 | 68 | Language & Region 69 | 70 | 71 | 72 | 73 | Keyboard Layout 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_ar.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | المظهر 10 | 11 | 12 | 13 | Corner Radius 14 | نصف قطر الأركان 15 | 16 | 17 | 18 | Openbox Theme 19 | سمة أوبن بوكس 20 | 21 | 22 | 23 | Behaviour 24 | السلوك 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | الفأرة و لوح اللمس 30 | 31 | 32 | 33 | Drop Shadows 34 | أسقط الظلال 35 | 36 | 37 | 38 | Placement Policy 39 | سياسة الموضع 40 | 41 | 42 | 43 | Cursor Theme 44 | سمة المؤشر 45 | 46 | 47 | 48 | Cursor Size 49 | حجم المؤشر 50 | 51 | 52 | 53 | Natural Scroll 54 | تمرير طبيعي 55 | 56 | 57 | 58 | Add 59 | أضف 60 | 61 | 62 | 63 | Remove 64 | احذف 65 | 66 | 67 | 68 | Language & Region 69 | اللغة والمنطقة 70 | 71 | 72 | 73 | Keyboard Layout 74 | تخطيط لوحة المفاتيح 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_ca.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Aspecte 10 | 11 | 12 | 13 | Corner Radius 14 | Radi de cantonada 15 | 16 | 17 | 18 | Openbox Theme 19 | Tema de l’Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Comportament 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Ratolí i ratolí tàctil 30 | 31 | 32 | 33 | Drop Shadows 34 | Ombres caigudes 35 | 36 | 37 | 38 | Placement Policy 39 | Política de col·locació 40 | 41 | 42 | 43 | Cursor Theme 44 | Tema dels cursors 45 | 46 | 47 | 48 | Cursor Size 49 | Mida dels cursors 50 | 51 | 52 | 53 | Natural Scroll 54 | Desplaçament natural 55 | 56 | 57 | 58 | Add 59 | Afegeix 60 | 61 | 62 | 63 | Remove 64 | Elimina 65 | 66 | 67 | 68 | Language & Region 69 | Llengua i regió 70 | 71 | 72 | 73 | Keyboard Layout 74 | Disposició del teclat 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_da.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Udseende 10 | 11 | 12 | 13 | Corner Radius 14 | Hjørneradius 15 | 16 | 17 | 18 | Openbox Theme 19 | Openboxtema 20 | 21 | 22 | 23 | Behaviour 24 | Adfærd 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Mus & Touchpad 30 | 31 | 32 | 33 | Drop Shadows 34 | Slagskygge 35 | 36 | 37 | 38 | Placement Policy 39 | Placeringspolitik 40 | 41 | 42 | 43 | Cursor Theme 44 | Markørtema 45 | 46 | 47 | 48 | Cursor Size 49 | Markørstørrelse 50 | 51 | 52 | 53 | Natural Scroll 54 | Naturlig rul 55 | 56 | 57 | 58 | Add 59 | Tilføj 60 | 61 | 62 | 63 | Remove 64 | Fjern 65 | 66 | 67 | 68 | Language & Region 69 | Sprog & Region 70 | 71 | 72 | 73 | Keyboard Layout 74 | Tastaturlayout 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_de.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Erscheinungsbild 10 | 11 | 12 | 13 | Corner Radius 14 | Eckenradius 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox-Thema 20 | 21 | 22 | 23 | Behaviour 24 | Verhalten 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Maus & Tastfeld 30 | 31 | 32 | 33 | Drop Shadows 34 | Schlagschatten 35 | 36 | 37 | 38 | Placement Policy 39 | Platzierungsrichtlinie 40 | 41 | 42 | 43 | Cursor Theme 44 | Mauszeigerthema 45 | 46 | 47 | 48 | Cursor Size 49 | Mauszeigergröße 50 | 51 | 52 | 53 | Natural Scroll 54 | Natürlicher Bildlauf 55 | 56 | 57 | 58 | Add 59 | Hinzufügen 60 | 61 | 62 | 63 | Remove 64 | Entfernen 65 | 66 | 67 | 68 | Language & Region 69 | Sprache & Region 70 | 71 | 72 | 73 | Keyboard Layout 74 | Tastaturbelegung 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_el.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Εμφάνιση 10 | 11 | 12 | 13 | Corner Radius 14 | Γωνιακή ακτίνα 15 | 16 | 17 | 18 | Openbox Theme 19 | Θέμα Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Συμπεριφορά 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Ποντίκι & επιφάνεια αφής 30 | 31 | 32 | 33 | Drop Shadows 34 | Δημιουργία εφέ σκιάς 35 | 36 | 37 | 38 | Placement Policy 39 | Πολιτική Τοποθέτησης 40 | 41 | 42 | 43 | Cursor Theme 44 | Θέμα δρομέα 45 | 46 | 47 | 48 | Cursor Size 49 | Μέγεθος δρομέα 50 | 51 | 52 | 53 | Natural Scroll 54 | Φυσική Κύληση 55 | 56 | 57 | 58 | Add 59 | Προσθήκη 60 | 61 | 62 | 63 | Remove 64 | Αφαίρεση 65 | 66 | 67 | 68 | Language & Region 69 | Γλώσσα & Τοποθεσία 70 | 71 | 72 | 73 | Keyboard Layout 74 | Διάταξη πληκτρολογίου 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_en_GB.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Appearance 10 | 11 | 12 | 13 | Corner Radius 14 | Corner Radius 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox Theme 20 | 21 | 22 | 23 | Behaviour 24 | Behaviour 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Mouse & Touchpad 30 | 31 | 32 | 33 | Drop Shadows 34 | Drop Shadows 35 | 36 | 37 | 38 | Placement Policy 39 | Placement Policy 40 | 41 | 42 | 43 | Cursor Theme 44 | Cursor Theme 45 | 46 | 47 | 48 | Cursor Size 49 | Cursor Size 50 | 51 | 52 | 53 | Natural Scroll 54 | Natural Scroll 55 | 56 | 57 | 58 | Add 59 | Add 60 | 61 | 62 | 63 | Remove 64 | Remove 65 | 66 | 67 | 68 | Language & Region 69 | Language & Region 70 | 71 | 72 | 73 | Keyboard Layout 74 | Keyboard Layout 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_en_IE.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 10 | 11 | 12 | 13 | Corner Radius 14 | 15 | 16 | 17 | 18 | Openbox Theme 19 | 20 | 21 | 22 | 23 | Behaviour 24 | 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | 45 | 46 | 47 | 48 | Cursor Size 49 | 50 | 51 | 52 | 53 | Natural Scroll 54 | 55 | 56 | 57 | 58 | Add 59 | 60 | 61 | 62 | 63 | Remove 64 | 65 | 66 | 67 | 68 | Language & Region 69 | 70 | 71 | 72 | 73 | Keyboard Layout 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_en_US.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 10 | 11 | 12 | 13 | Corner Radius 14 | 15 | 16 | 17 | 18 | Openbox Theme 19 | 20 | 21 | 22 | 23 | Behaviour 24 | 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | 45 | 46 | 47 | 48 | Cursor Size 49 | 50 | 51 | 52 | 53 | Natural Scroll 54 | 55 | 56 | 57 | 58 | Add 59 | 60 | 61 | 62 | 63 | Remove 64 | 65 | 66 | 67 | 68 | Language & Region 69 | 70 | 71 | 72 | 73 | Keyboard Layout 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_es.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Apariencia 10 | 11 | 12 | 13 | Corner Radius 14 | Radio de esquina 15 | 16 | 17 | 18 | Openbox Theme 19 | Tema de Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Comportamiento 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Ratón y panel táctil 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | Tema de cursores 45 | 46 | 47 | 48 | Cursor Size 49 | Tamaño de cursores 50 | 51 | 52 | 53 | Natural Scroll 54 | Desplazamiento natural 55 | 56 | 57 | 58 | Add 59 | Añadir 60 | 61 | 62 | 63 | Remove 64 | Quitar 65 | 66 | 67 | 68 | Language & Region 69 | Idioma y región 70 | 71 | 72 | 73 | Keyboard Layout 74 | Disposición de teclado 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_et.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Välimus 10 | 11 | 12 | 13 | Corner Radius 14 | Nurga raadius 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox'i teema 20 | 21 | 22 | 23 | Behaviour 24 | Käitumine 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Hiir ja puuteplaat 30 | 31 | 32 | 33 | Drop Shadows 34 | Ära kasuta varje 35 | 36 | 37 | 38 | Placement Policy 39 | Paigutuse kord 40 | 41 | 42 | 43 | Cursor Theme 44 | Kursori teema 45 | 46 | 47 | 48 | Cursor Size 49 | Kursori suurus 50 | 51 | 52 | 53 | Natural Scroll 54 | Loomulik kerimine 55 | 56 | 57 | 58 | Add 59 | Lisa 60 | 61 | 62 | 63 | Remove 64 | Eemalda 65 | 66 | 67 | 68 | Language & Region 69 | Keel ja regioon 70 | 71 | 72 | 73 | Keyboard Layout 74 | Klaviatuuri paigutus 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_eu.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Itxura 10 | 11 | 12 | 13 | Corner Radius 14 | Izkinako erradioa 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox Gaia 20 | 21 | 22 | 23 | Behaviour 24 | Portaera 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Sagua eta Ukipen-panela 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | Kokapen-politika 40 | 41 | 42 | 43 | Cursor Theme 44 | Kurtsore Gaia 45 | 46 | 47 | 48 | Cursor Size 49 | Kurtsorearen tamaina 50 | 51 | 52 | 53 | Natural Scroll 54 | 55 | 56 | 57 | 58 | Add 59 | Gehitu 60 | 61 | 62 | 63 | Remove 64 | Kendu 65 | 66 | 67 | 68 | Language & Region 69 | 70 | 71 | 72 | 73 | Keyboard Layout 74 | Teklatuaren Diseinua 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_fi.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Ulkoasu 10 | 11 | 12 | 13 | Corner Radius 14 | Kulman säde 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox-teema 20 | 21 | 22 | 23 | Behaviour 24 | Toiminta 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Hiiri & kosketuslevy 30 | 31 | 32 | 33 | Drop Shadows 34 | Taustavarjot 35 | 36 | 37 | 38 | Placement Policy 39 | Sijoituskäytäntö 40 | 41 | 42 | 43 | Cursor Theme 44 | Osoitinteema 45 | 46 | 47 | 48 | Cursor Size 49 | Osoittimen koko 50 | 51 | 52 | 53 | Natural Scroll 54 | Luonnollinen vieritys 55 | 56 | 57 | 58 | Add 59 | Lisää 60 | 61 | 62 | 63 | Remove 64 | Poista 65 | 66 | 67 | 68 | Language & Region 69 | Kieli & alue 70 | 71 | 72 | 73 | Keyboard Layout 74 | Näppäimistöasettelu 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_fr.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Apparence 10 | 11 | 12 | 13 | Corner Radius 14 | Rayon des coins 15 | 16 | 17 | 18 | Openbox Theme 19 | Thème Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Comportement 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Souris & Pavé tactile 30 | 31 | 32 | 33 | Drop Shadows 34 | Contour ombré 35 | 36 | 37 | 38 | Placement Policy 39 | Politique de placement 40 | 41 | 42 | 43 | Cursor Theme 44 | Thème curseur 45 | 46 | 47 | 48 | Cursor Size 49 | Taille curseur 50 | 51 | 52 | 53 | Natural Scroll 54 | Défilement naturel 55 | 56 | 57 | 58 | Add 59 | Ajouter 60 | 61 | 62 | 63 | Remove 64 | Enlever 65 | 66 | 67 | 68 | Language & Region 69 | Langue & Région 70 | 71 | 72 | 73 | Keyboard Layout 74 | Disposition du clavier 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_gl.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 10 | 11 | 12 | 13 | Corner Radius 14 | 15 | 16 | 17 | 18 | Openbox Theme 19 | 20 | 21 | 22 | 23 | Behaviour 24 | 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | 45 | 46 | 47 | 48 | Cursor Size 49 | 50 | 51 | 52 | 53 | Natural Scroll 54 | 55 | 56 | 57 | 58 | Add 59 | 60 | 61 | 62 | 63 | Remove 64 | 65 | 66 | 67 | 68 | Language & Region 69 | 70 | 71 | 72 | 73 | Keyboard Layout 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_hu.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Megjelenés 10 | 11 | 12 | 13 | Corner Radius 14 | Sarokkerekítés 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox téma 20 | 21 | 22 | 23 | Behaviour 24 | Viselkedés 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Egér és érintőpárna 30 | 31 | 32 | 33 | Drop Shadows 34 | Vetett árnyékok 35 | 36 | 37 | 38 | Placement Policy 39 | Elhelyezési szabály 40 | 41 | 42 | 43 | Cursor Theme 44 | Kurzor témája 45 | 46 | 47 | 48 | Cursor Size 49 | Kurzor mérete 50 | 51 | 52 | 53 | Natural Scroll 54 | Természetes görgetés 55 | 56 | 57 | 58 | Add 59 | Hozzáadás 60 | 61 | 62 | 63 | Remove 64 | Eltávolítás 65 | 66 | 67 | 68 | Language & Region 69 | Nyelv és régió 70 | 71 | 72 | 73 | Keyboard Layout 74 | Billentyűzetkiosztás 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_it.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Aspetto 10 | 11 | 12 | 13 | Corner Radius 14 | Raggio Angolo 15 | 16 | 17 | 18 | Openbox Theme 19 | Tema Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Comportamento 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Mouse e Touchpad 30 | 31 | 32 | 33 | Drop Shadows 34 | Abilita ombre 35 | 36 | 37 | 38 | Placement Policy 39 | Posizionamento 40 | 41 | 42 | 43 | Cursor Theme 44 | Tema Puntatore 45 | 46 | 47 | 48 | Cursor Size 49 | Grandezza Puntatore 50 | 51 | 52 | 53 | Natural Scroll 54 | Scorrimento Naturale 55 | 56 | 57 | 58 | Add 59 | Aggiungi 60 | 61 | 62 | 63 | Remove 64 | Rimuovi 65 | 66 | 67 | 68 | Language & Region 69 | Lingua e Paese 70 | 71 | 72 | 73 | Keyboard Layout 74 | Mappatura Tastiera 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_ka.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | გარეგნობა 10 | 11 | 12 | 13 | Corner Radius 14 | კუთხის რადიუსი 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox-ის თემა 20 | 21 | 22 | 23 | Behaviour 24 | ქცევა 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | თაგუნა და თაჩპედი 30 | 31 | 32 | 33 | Drop Shadows 34 | ჩრდილები 35 | 36 | 37 | 38 | Placement Policy 39 | მოთავსების პოლიტიკა 40 | 41 | 42 | 43 | Cursor Theme 44 | კურსორის თემა 45 | 46 | 47 | 48 | Cursor Size 49 | კურსორის ზომა 50 | 51 | 52 | 53 | Natural Scroll 54 | ბუნებრივი გადახვევა 55 | 56 | 57 | 58 | Add 59 | დამატება 60 | 61 | 62 | 63 | Remove 64 | წაშლა 65 | 66 | 67 | 68 | Language & Region 69 | ენა და რეგიონი 70 | 71 | 72 | 73 | Keyboard Layout 74 | კლავიატურის განლაგება 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_kab.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 10 | 11 | 12 | 13 | Corner Radius 14 | 15 | 16 | 17 | 18 | Openbox Theme 19 | 20 | 21 | 22 | 23 | Behaviour 24 | 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | 45 | 46 | 47 | 48 | Cursor Size 49 | 50 | 51 | 52 | 53 | Natural Scroll 54 | 55 | 56 | 57 | 58 | Add 59 | Rnu 60 | 61 | 62 | 63 | Remove 64 | Kkes 65 | 66 | 67 | 68 | Language & Region 69 | 70 | 71 | 72 | 73 | Keyboard Layout 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_ko.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 모양새 10 | 11 | 12 | 13 | Corner Radius 14 | 모서리 반지름 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox 테마 20 | 21 | 22 | 23 | Behaviour 24 | 동작 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 마우스 & 터치패드 30 | 31 | 32 | 33 | Drop Shadows 34 | 그림자 35 | 36 | 37 | 38 | Placement Policy 39 | 배치 방침 40 | 41 | 42 | 43 | Cursor Theme 44 | 커서 테마 45 | 46 | 47 | 48 | Cursor Size 49 | 커서 크기 50 | 51 | 52 | 53 | Natural Scroll 54 | 부드러운 스크롤 55 | 56 | 57 | 58 | Add 59 | 추가 60 | 61 | 62 | 63 | Remove 64 | 제거 65 | 66 | 67 | 68 | Language & Region 69 | 언어 & 지역 70 | 71 | 72 | 73 | Keyboard Layout 74 | 키보드 자판 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_lt.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Išvaizda 10 | 11 | 12 | 13 | Corner Radius 14 | Kampų spindulys 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox apipavidalinimas 20 | 21 | 22 | 23 | Behaviour 24 | Elgsena 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Pelė ir jutiklinis kilimėlis 30 | 31 | 32 | 33 | Drop Shadows 34 | Mesti šešėlius 35 | 36 | 37 | 38 | Placement Policy 39 | Išdėstymo politika 40 | 41 | 42 | 43 | Cursor Theme 44 | Žymeklio apipavidalinimas 45 | 46 | 47 | 48 | Cursor Size 49 | Žymeklio dydis 50 | 51 | 52 | 53 | Natural Scroll 54 | Natūralus slinkimas 55 | 56 | 57 | 58 | Add 59 | Pridėti 60 | 61 | 62 | 63 | Remove 64 | Šalinti 65 | 66 | 67 | 68 | Language & Region 69 | Kalba ir regionas 70 | 71 | 72 | 73 | Keyboard Layout 74 | Klaviatūros išdėstymas 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_ms.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Penampilan 10 | 11 | 12 | 13 | Corner Radius 14 | Jejari Bucu 15 | 16 | 17 | 18 | Openbox Theme 19 | Tema Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Kelakuan 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Tetikus & Pad Sentuh 30 | 31 | 32 | 33 | Drop Shadows 34 | Bebayang Jatuh 35 | 36 | 37 | 38 | Placement Policy 39 | Dasar Penempatan 40 | 41 | 42 | 43 | Cursor Theme 44 | Tema Kursor 45 | 46 | 47 | 48 | Cursor Size 49 | Saiz Kursor 50 | 51 | 52 | 53 | Natural Scroll 54 | Tatal Semulajadi 55 | 56 | 57 | 58 | Add 59 | Tambah 60 | 61 | 62 | 63 | Remove 64 | Buang 65 | 66 | 67 | 68 | Language & Region 69 | Bahasa & Wilayah 70 | 71 | 72 | 73 | Keyboard Layout 74 | Susun Atur Papan Kekunci 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_nl.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Vormgeving 10 | 11 | 12 | 13 | Corner Radius 14 | Hoekstraal 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox-thema 20 | 21 | 22 | 23 | Behaviour 24 | Gedrag 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Muis en touchpad 30 | 31 | 32 | 33 | Drop Shadows 34 | Valschaduwen 35 | 36 | 37 | 38 | Placement Policy 39 | Plaatsing 40 | 41 | 42 | 43 | Cursor Theme 44 | Cursorthema 45 | 46 | 47 | 48 | Cursor Size 49 | Cursorgrootte 50 | 51 | 52 | 53 | Natural Scroll 54 | Natuurlijk scrollen 55 | 56 | 57 | 58 | Add 59 | Toevoegen 60 | 61 | 62 | 63 | Remove 64 | Verwijderen 65 | 66 | 67 | 68 | Language & Region 69 | Taal en regio 70 | 71 | 72 | 73 | Keyboard Layout 74 | Toetsenbordindeling 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_pa.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | ਦਿੱਖ 10 | 11 | 12 | 13 | Corner Radius 14 | ਕੋਨਾ ਦੇ ਵਿਆਸ 15 | 16 | 17 | 18 | Openbox Theme 19 | ਓਪਨਬਾਕਸ ਥੀਮ 20 | 21 | 22 | 23 | Behaviour 24 | ਰਵੱਈਆ 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | ਮਾਊਸ ਤੇ ਟੱਚਪੈਡ 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | ਸਥਾਪਨ ਨੀਤੀ 40 | 41 | 42 | 43 | Cursor Theme 44 | ਕਰਸਰ ਥੀਮ 45 | 46 | 47 | 48 | Cursor Size 49 | ਕਰਸਰ ਦਾ ਆਕਾਰ 50 | 51 | 52 | 53 | Natural Scroll 54 | ਕੁਦਰਤੀ ਸਕਰੋਲ 55 | 56 | 57 | 58 | Add 59 | ਜੋੜੋ 60 | 61 | 62 | 63 | Remove 64 | ਹਟਾਓ 65 | 66 | 67 | 68 | Language & Region 69 | ਭਾਸ਼ਾ ਤੇ ਖੇਤਰ 70 | 71 | 72 | 73 | Keyboard Layout 74 | ਕੀਬੋਰਡ ਖਾਕਾ 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_pl.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Wygląd 10 | 11 | 12 | 13 | Corner Radius 14 | Promień narożnika 15 | 16 | 17 | 18 | Openbox Theme 19 | Motyw Openboksa 20 | 21 | 22 | 23 | Behaviour 24 | Zachowanie 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Mysz i panel dotykowy 30 | 31 | 32 | 33 | Drop Shadows 34 | Rzucanie cieni 35 | 36 | 37 | 38 | Placement Policy 39 | Zasady rozmieszczania 40 | 41 | 42 | 43 | Cursor Theme 44 | Motyw kursora 45 | 46 | 47 | 48 | Cursor Size 49 | Rozmiar kursora 50 | 51 | 52 | 53 | Natural Scroll 54 | Przewijanie naturalne 55 | 56 | 57 | 58 | Add 59 | Dodaj 60 | 61 | 62 | 63 | Remove 64 | Usuń 65 | 66 | 67 | 68 | Language & Region 69 | Język i region 70 | 71 | 72 | 73 | Keyboard Layout 74 | Układ klawiatury 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_pt.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Aparência 10 | 11 | 12 | 13 | Corner Radius 14 | Raio do canto 15 | 16 | 17 | 18 | Openbox Theme 19 | Tema Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Comportamento 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Rato e Painel tátil 30 | 31 | 32 | 33 | Drop Shadows 34 | Sombras de gotas 35 | 36 | 37 | 38 | Placement Policy 39 | Política de colocação 40 | 41 | 42 | 43 | Cursor Theme 44 | Tema do cursor 45 | 46 | 47 | 48 | Cursor Size 49 | Tamanho do cursor 50 | 51 | 52 | 53 | Natural Scroll 54 | Deslocação natural 55 | 56 | 57 | 58 | Add 59 | Adicionar 60 | 61 | 62 | 63 | Remove 64 | Remover 65 | 66 | 67 | 68 | Language & Region 69 | Idioma e Região 70 | 71 | 72 | 73 | Keyboard Layout 74 | Esquema de teclado 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_pt_BR.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Aparência 10 | 11 | 12 | 13 | Corner Radius 14 | Arrendondamento dos Cantos 15 | 16 | 17 | 18 | Openbox Theme 19 | Tema do Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Comportamento 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Mouse & Touchpad 30 | 31 | 32 | 33 | Drop Shadows 34 | Sombreamento nas Janelas 35 | 36 | 37 | 38 | Placement Policy 39 | Política de Posicionamento 40 | 41 | 42 | 43 | Cursor Theme 44 | Tema do Cursor 45 | 46 | 47 | 48 | Cursor Size 49 | Tamanho do Cursor 50 | 51 | 52 | 53 | Natural Scroll 54 | Rolagem Natural 55 | 56 | 57 | 58 | Add 59 | Adicionar 60 | 61 | 62 | 63 | Remove 64 | Remover 65 | 66 | 67 | 68 | Language & Region 69 | Idioma & Região 70 | 71 | 72 | 73 | Keyboard Layout 74 | Idioma do Teclado 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_ro.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Aspect 10 | 11 | 12 | 13 | Corner Radius 14 | Raza colțului 15 | 16 | 17 | 18 | Openbox Theme 19 | Tema Openbox 20 | 21 | 22 | 23 | Behaviour 24 | 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Mouse și tastatură 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | Temă Cursor 45 | 46 | 47 | 48 | Cursor Size 49 | Dimensiune Cursor 50 | 51 | 52 | 53 | Natural Scroll 54 | Derulare Naturală 55 | 56 | 57 | 58 | Add 59 | 60 | 61 | 62 | 63 | Remove 64 | 65 | 66 | 67 | 68 | Language & Region 69 | Limbă și Regiune 70 | 71 | 72 | 73 | Keyboard Layout 74 | Aspect tastatură 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_ru.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Внешний вид 10 | 11 | 12 | 13 | Corner Radius 14 | Радиус углов 15 | 16 | 17 | 18 | Openbox Theme 19 | Тема Openbox 20 | 21 | 22 | 23 | Behaviour 24 | Поведение 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Мышь и сенсорная панель 30 | 31 | 32 | 33 | Drop Shadows 34 | Тени 35 | 36 | 37 | 38 | Placement Policy 39 | Правила расположения 40 | 41 | 42 | 43 | Cursor Theme 44 | Тема курсора 45 | 46 | 47 | 48 | Cursor Size 49 | Размер курсора 50 | 51 | 52 | 53 | Natural Scroll 54 | Обратное направление прокрутки 55 | 56 | 57 | 58 | Add 59 | Добавить 60 | 61 | 62 | 63 | Remove 64 | Убрать 65 | 66 | 67 | 68 | Language & Region 69 | Язык и регион 70 | 71 | 72 | 73 | Keyboard Layout 74 | Раскладка клавиатура 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_sl.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 10 | 11 | 12 | 13 | Corner Radius 14 | 15 | 16 | 17 | 18 | Openbox Theme 19 | 20 | 21 | 22 | 23 | Behaviour 24 | 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 30 | 31 | 32 | 33 | Drop Shadows 34 | 35 | 36 | 37 | 38 | Placement Policy 39 | 40 | 41 | 42 | 43 | Cursor Theme 44 | 45 | 46 | 47 | 48 | Cursor Size 49 | 50 | 51 | 52 | 53 | Natural Scroll 54 | 55 | 56 | 57 | 58 | Add 59 | 60 | 61 | 62 | 63 | Remove 64 | 65 | 66 | 67 | 68 | Language & Region 69 | 70 | 71 | 72 | 73 | Keyboard Layout 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_tr.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | Görünüş 10 | 11 | 12 | 13 | Corner Radius 14 | Köşe Yuvarlatma 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox Teması 20 | 21 | 22 | 23 | Behaviour 24 | Davranış 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | Fare ve Dokunmatik Yüzey 30 | 31 | 32 | 33 | Drop Shadows 34 | Alt Gölgeler 35 | 36 | 37 | 38 | Placement Policy 39 | Yerleştirme Politikası 40 | 41 | 42 | 43 | Cursor Theme 44 | İmleç Teması 45 | 46 | 47 | 48 | Cursor Size 49 | İmleç Boyutu 50 | 51 | 52 | 53 | Natural Scroll 54 | Doğal Kaydırma 55 | 56 | 57 | 58 | Add 59 | Ekle 60 | 61 | 62 | 63 | Remove 64 | Kaldır 65 | 66 | 67 | 68 | Language & Region 69 | Dil ve Bölge 70 | 71 | 72 | 73 | Keyboard Layout 74 | Klavye Düzeni 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc-tweaks_zh_CN.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MainDialog 6 | 7 | 8 | Appearance 9 | 外观 10 | 11 | 12 | 13 | Corner Radius 14 | 圆角半径 15 | 16 | 17 | 18 | Openbox Theme 19 | Openbox 主题 20 | 21 | 22 | 23 | Behaviour 24 | 行为 25 | 26 | 27 | 28 | Mouse & Touchpad 29 | 鼠标与触摸板 30 | 31 | 32 | 33 | Drop Shadows 34 | 下拉阴影 35 | 36 | 37 | 38 | Placement Policy 39 | 窗口堆放策略 40 | 41 | 42 | 43 | Cursor Theme 44 | 光标主题 45 | 46 | 47 | 48 | Cursor Size 49 | 光标大小 50 | 51 | 52 | 53 | Natural Scroll 54 | 自然滚动 55 | 56 | 57 | 58 | Add 59 | 添加 60 | 61 | 62 | 63 | Remove 64 | 删除 65 | 66 | 67 | 68 | Language & Region 69 | 语言与地区 70 | 71 | 72 | 73 | Keyboard Layout 74 | 键盘布局 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc Tweaks" 2 | Desktop Entry/GenericName: "Compositor Settings" 3 | Desktop Entry/Comment: "Labwc Wayland compositor settings" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_ar.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "تطويعات Labwc" 2 | Desktop Entry/GenericName: "تكوين الإعدادات" 3 | Desktop Entry/Comment: "تكوين إعدادات وايلاند Labwc" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_ca.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Ajustos de Labwc" 2 | Desktop Entry/GenericName: "Configuració del compositor" 3 | Desktop Entry/Comment: "Configuració del compositor Labwc Wayland" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_da.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc Tweaks" 2 | Desktop Entry/GenericName: "Kompositorindstillinger" 3 | Desktop Entry/Comment: "Labwc Wayland kompositorindstillinger" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_de.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc-Optimierungen" 2 | Desktop Entry/GenericName: "Kompositor-Einstellungen" 3 | Desktop Entry/Comment: "Labwc Wayland-Kompositor-Einstellungen" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_el.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Τροποποιήσεις Labwc" 2 | Desktop Entry/GenericName: "Ρυθμίσεις Συνθέτη" 3 | Desktop Entry/Comment: "Ρυθμίσεις συνθέτη Labwc Wayland" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_en_US.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "" 2 | Desktop Entry/GenericName: "" 3 | Desktop Entry/Comment: "" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_et.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc peenhäälestus" 2 | Desktop Entry/GenericName: "Komposiitori seadistused" 3 | Desktop Entry/Comment: "Labwc Waylandi komposiitori seadistused" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_fi.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc-säädöt" 2 | Desktop Entry/GenericName: "Koostimen asetukset" 3 | Desktop Entry/Comment: "Labwc Wayland-koostimen asetukset" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_fr.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Réglages de Labwc" 2 | Desktop Entry/GenericName: "Paramètres du Compositeur" 3 | Desktop Entry/Comment: "Paramètres du compositeur Labwc Wayland" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_hu.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc Finomhangoló" 2 | Desktop Entry/GenericName: "Kompozitor beállítások" 3 | Desktop Entry/Comment: "Labwc Wayland kompozitor beállítások" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_it.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Impostazioni Labwc" 2 | Desktop Entry/GenericName: "Impostazioni del gestore finestre labwc" 3 | Desktop Entry/Comment: "Impostazioni di Labwc, gestore delle finestre in Wayland" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_ka.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc-ის დეტალები" 2 | Desktop Entry/GenericName: "კომპოზიტორის მორგება" 3 | Desktop Entry/Comment: "Labwc Wayland კომპოზიტორის პარამეტრები" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_ko.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc 트윅" 2 | Desktop Entry/GenericName: "컴포지터 설정" 3 | Desktop Entry/Comment: "Labwc Wayland 컴포지터 설정입니다" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_lt.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc patobulinimai" 2 | Desktop Entry/GenericName: "Tvarkytojo nustatymai" 3 | Desktop Entry/Comment: "Labwc „Wayland“ tvarkytojo nustatymai" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_ms.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Pulasan Labwc" 2 | Desktop Entry/GenericName: "Aturan Penggubah" 3 | Desktop Entry/Comment: "Aturan Penggubah Labwc Wayland" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_nl.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc-afstellingen" 2 | Desktop Entry/GenericName: "Vensterbeheerderinstellingen" 3 | Desktop Entry/Comment: "Labwc Wayland-instellingen" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_pl.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Usprawnienia Labwc" 2 | Desktop Entry/GenericName: "Ustawienia kompozytora" 3 | Desktop Entry/Comment: "Ustawienia kompozytora Wayland Labwc" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_pt.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Ajustes Labwc" 2 | Desktop Entry/GenericName: "Definições do Compositor" 3 | Desktop Entry/Comment: "Definições do compositor Labwc Wayland" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_pt_BR.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Ajustes do Labwc" 2 | Desktop Entry/GenericName: "Configurações do Compositor" 3 | Desktop Entry/Comment: "Configurações do Compositor Labwc" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_ru.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "Labwc Tweaks" 2 | Desktop Entry/GenericName: "Настройки композитора" 3 | Desktop Entry/Comment: "Настройки композитора Labwc Wayland" 4 | -------------------------------------------------------------------------------- /data/translations/labwc_tweaks_zh_CN.desktop.yaml: -------------------------------------------------------------------------------- 1 | Desktop Entry/Name: "labwc 配置调整" 2 | Desktop Entry/GenericName: "混成器设置" 3 | Desktop Entry/Comment: "Labwc Wayland 混成器设置" 4 | -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: WebKit 2 | Standard: c++20 3 | ColumnLimit: 100 4 | CommentPragmas: "^!|^:|^ SPDX-License-Identifier:" 5 | PointerBindsToType: false 6 | SpaceAfterTemplateKeyword: false 7 | BreakBeforeBinaryOperators: NonAssignment 8 | BreakBeforeBraces: Custom 9 | BraceWrapping: 10 | AfterClass: true 11 | AfterControlStatement: false 12 | AfterEnum: false 13 | AfterFunction: true 14 | AfterNamespace: false 15 | AfterObjCDeclaration: false 16 | AfterStruct: true 17 | AfterUnion: false 18 | BeforeCatch: false 19 | BeforeElse: false 20 | IndentBraces: false 21 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 22 | ConstructorInitializerIndentWidth: 4 23 | ContinuationIndentWidth: 8 24 | NamespaceIndentation: None 25 | IndentPPDirectives: AfterHash 26 | PPIndentWidth: 2 27 | AlignAfterOpenBracket: true 28 | AlwaysBreakTemplateDeclarations: true 29 | AllowShortFunctionsOnASingleLine: Inline 30 | SortIncludes: false 31 | ForEachMacros: [ foreach, Q_FOREACH, forever, Q_FOREVER ] 32 | BreakConstructorInitializers: BeforeColon 33 | FixNamespaceComments: true 34 | ShortNamespaceLines: 1 35 | AlignEscapedNewlines: Left 36 | SpaceBeforeCpp11BracedList: false 37 | 38 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{cpp,h}] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 4 10 | max_line_length = 100 11 | -------------------------------------------------------------------------------- /src/environment.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | #define _POSIX_C_SOURCE 200809L 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | QString environment_get(const char *key) 10 | { 11 | QString filename = qgetenv("HOME") + "/.config/labwc/environment"; 12 | QFile inputFile(filename); 13 | inputFile.open(QIODevice::ReadOnly); 14 | if (!inputFile.isOpen()) { 15 | return nullptr; 16 | } 17 | 18 | QTextStream stream(&inputFile); 19 | QString line; 20 | while (stream.readLineInto(&line)) { 21 | if (line.isEmpty() || line.startsWith("#")) { 22 | continue; 23 | } 24 | 25 | QStringList elements = line.split('='); 26 | if (elements.count() != 2) { 27 | continue; 28 | } 29 | if (elements[0].trimmed() == QString(key)) { 30 | return elements[1].trimmed(); 31 | } 32 | } 33 | return nullptr; 34 | } 35 | 36 | void environment_set(const char *key, const char *value) 37 | { 38 | if (!key || !*key) { 39 | return; 40 | } 41 | if (!value || !*value) { 42 | return; 43 | } 44 | /* set cursor for labwc - should cover 'replace' or 'append' */ 45 | char xcur[4096] = { 0 }; 46 | strcpy(xcur, key); 47 | strcat(xcur, "="); 48 | char filename[4096]; 49 | char bufname[4096]; 50 | char *home = getenv("HOME"); 51 | snprintf(filename, sizeof(filename), "%s/%s", home, ".config/labwc/environment"); 52 | snprintf(bufname, sizeof(bufname), "%s/%s", home, ".config/labwc/buf"); 53 | FILE *fe = fopen(filename, "r"); 54 | FILE *fw = fopen(bufname, "a"); 55 | if ((fe == NULL) || (fw == NULL)) { 56 | perror("Unable to open file!"); 57 | return; 58 | } 59 | char chunk[128]; 60 | while (fgets(chunk, sizeof(chunk), fe) != NULL) { 61 | if (strstr(chunk, xcur) != NULL) { 62 | continue; 63 | } else { 64 | fprintf(fw, "%s", chunk); 65 | } 66 | } 67 | fclose(fe); 68 | if (value) { 69 | fprintf(fw, "%s\n", strcat(xcur, value)); 70 | } 71 | fclose(fw); 72 | rename(bufname, filename); 73 | } 74 | 75 | void environment_set_num(const char *key, int value) 76 | { 77 | char buffer[255]; 78 | snprintf(buffer, 255, "%d", value); 79 | 80 | environment_set(key, buffer); 81 | } 82 | -------------------------------------------------------------------------------- /src/environment.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | #ifndef ENVIRONMENT_H 3 | #define ENVIRONMENT_H 4 | #include 5 | 6 | QString environment_get(const char *key); 7 | void environment_set(const char *key, const char *value); 8 | void environment_set_num(const char *key, int value); 9 | 10 | #endif /* ENVIRONMENT_H */ 11 | -------------------------------------------------------------------------------- /src/gen-layout-list: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from enum import Enum 3 | 4 | HEADER="""#pragma once 5 | #include 6 | 7 | // Auto-generated based on "/usr/share/X11/xkb/rules/evdev.lst" 8 | 9 | struct layout 10 | { 11 | const char *code; 12 | const char *description; 13 | }; 14 | 15 | static std::vector evdev_lst_layouts = {""" 16 | 17 | FOOTER="};" 18 | 19 | class Layout(): 20 | def __init__(self, layout, variant, description): 21 | self.layout=layout 22 | self.variant=variant 23 | self.description=description 24 | def __lt__(self, other): 25 | return self.description < other.description 26 | def get_layout(self): 27 | if not self.variant: 28 | return self.layout 29 | return f"{self.layout}({self.variant})" 30 | def get_description(self): 31 | return self.description 32 | 33 | def generate_code(layouts): 34 | print(HEADER) 35 | for layout in layouts: 36 | print(f' {{ "{layout.get_layout()}", "{layout.get_description()}" }},') 37 | print(FOOTER) 38 | 39 | def main(): 40 | with open("/usr/share/X11/xkb/rules/evdev.lst", 'r', encoding='UTF-8') as f: 41 | lines = f.read().split('\n') 42 | 43 | section_type=Enum('section_type', 'NONE LAYOUT VARIANT') 44 | section = section_type.NONE 45 | 46 | layouts = [] 47 | 48 | for line in lines: 49 | if line.startswith('!'): 50 | if line == "! layout": 51 | section = section_type.LAYOUT 52 | elif line == "! variant": 53 | section = section_type.VARIANT 54 | else: 55 | section = section_type.NONE 56 | continue 57 | 58 | # 59 | # The 'layout' section looks like this: 60 | # ! layout 61 | # al Albanian 62 | # et Amharic 63 | # am Armenian 64 | # ara Arabic 65 | # eg Arabic (Egypt) 66 | # ... 67 | # 68 | if section == section_type.LAYOUT: 69 | fields = line.strip().split(None, maxsplit=1) 70 | if not fields: 71 | continue 72 | layout = fields[0] 73 | description = fields[1] 74 | layouts.append(Layout(layout, None, description)) 75 | 76 | # 77 | # The 'variant' section looks like this: 78 | # ! variant 79 | # plisi al: Albanian (Plisi) 80 | # veqilharxhi al: Albanian (Veqilharxhi) 81 | # phonetic am: Armenian (phonetic) 82 | # phonetic-alt am: Armenian (alt. phonetic) 83 | # eastern am: Armenian (eastern) 84 | # 85 | if section == section_type.VARIANT: 86 | fields = line.strip().split(None, maxsplit=1) 87 | if not fields: 88 | continue 89 | variant = fields[0] 90 | fields = fields[1].strip().split(':', maxsplit=1) 91 | if not fields: 92 | continue 93 | layout = fields[0] 94 | description = fields[1].strip() 95 | layouts.append(Layout(layout, variant, description)) 96 | 97 | generate_code(sorted(layouts)) 98 | 99 | if __name__ == '__main__': 100 | main() 101 | 102 | -------------------------------------------------------------------------------- /src/layoutmodel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "environment.h" 4 | #include "layoutmodel.h" 5 | #include "evdev-lst-layouts.h" 6 | 7 | Layout::Layout(QString code, QString desc) 8 | { 9 | m_code = code; 10 | m_desc = desc; 11 | } 12 | 13 | Layout::~Layout() { } 14 | 15 | LayoutModel::LayoutModel(QObject *parent) : QAbstractListModel(parent) 16 | { 17 | QString xkb_default_layout = environment_get("XKB_DEFAULT_LAYOUT"); 18 | QStringList layoutElements = xkb_default_layout.split(','); 19 | 20 | // We don't advise using XKB_DEFAULT_LAYOUT, but handle it just in case by adding it to the 21 | // respective layouts, for example like "latam(deadtilde)" 22 | QString xkb_default_variant = environment_get("XKB_DEFAULT_VARIANT"); 23 | QStringList variantElements = xkb_default_variant.split(',', Qt::KeepEmptyParts); 24 | int i = 0; 25 | foreach (QString element, variantElements) { 26 | if (layoutElements.size() <= i) { 27 | break; 28 | } 29 | // Let's not add another (variant) if one is already specified. 30 | if (layoutElements[i].contains("(")) { 31 | continue; 32 | } 33 | if (!element.isEmpty()) { 34 | layoutElements[i] += "(" + element + ")"; 35 | } 36 | ++i; 37 | } 38 | 39 | foreach (QString element, layoutElements) { 40 | for (auto layout : evdev_lst_layouts) { 41 | if (element == QString(layout.code)) { 42 | addLayout(QString(layout.code), QString(layout.description)); 43 | } 44 | } 45 | } 46 | } 47 | 48 | LayoutModel::~LayoutModel() { } 49 | 50 | char *LayoutModel::getXkbDefaultLayout() 51 | { 52 | QString ret; 53 | QVectorIterator> iter(m_layouts); 54 | while (iter.hasNext()) { 55 | ret += iter.next().get()->code(); 56 | if (iter.hasNext()) { 57 | ret += ","; 58 | } 59 | } 60 | return ret.toLatin1().data(); 61 | } 62 | 63 | int LayoutModel::rowCount(const QModelIndex &parent) const 64 | { 65 | return m_layouts.size(); 66 | } 67 | 68 | QVariant LayoutModel::data(const QModelIndex &index, int role) const 69 | { 70 | if (!index.isValid()) { 71 | return {}; 72 | } 73 | 74 | const int row = index.row(); 75 | switch (role) { 76 | case Qt::DisplayRole: 77 | return m_layouts.at(row)->desc() + " [" + m_layouts.at(row)->code() + "]"; 78 | } 79 | return {}; 80 | } 81 | 82 | void LayoutModel::update(void) 83 | { 84 | QModelIndex topLeft = createIndex(0, 0); 85 | emit dataChanged(topLeft, topLeft, { Qt::DisplayRole }); 86 | } 87 | 88 | void LayoutModel::addLayout(const QString &code, const QString &desc) 89 | { 90 | QVectorIterator> iter(m_layouts); 91 | while (iter.hasNext()) { 92 | if (iter.next().get()->code() == code) { 93 | qDebug() << "cannot add the same layout twice"; 94 | return; 95 | } 96 | } 97 | 98 | m_layouts.append(QSharedPointer(new Layout(code, desc))); 99 | update(); 100 | } 101 | 102 | void LayoutModel::deleteLayout(int index) 103 | { 104 | if (index < 0 || index >= m_layouts.size()) { 105 | return; 106 | } 107 | m_layouts.remove(index); 108 | update(); 109 | } 110 | -------------------------------------------------------------------------------- /src/layoutmodel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Layout 5 | { 6 | public: 7 | Layout(QString code, QString desc); 8 | ~Layout(); 9 | 10 | private: 11 | QString m_code; 12 | QString m_desc; 13 | 14 | public: 15 | QString code() const { return m_code; } 16 | QString desc() const { return m_desc; } 17 | }; 18 | 19 | class LayoutModel : public QAbstractListModel 20 | { 21 | Q_OBJECT 22 | 23 | public: 24 | LayoutModel(QObject *parent = nullptr); 25 | ~LayoutModel(); 26 | 27 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 28 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 29 | void addLayout(const QString &code, const QString &desc); 30 | void deleteLayout(int index); 31 | char *getXkbDefaultLayout(); 32 | 33 | private: 34 | void update(void); 35 | QVector> m_layouts; 36 | }; 37 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "maindialog.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void initLocale(QTranslator *qtTranslator, QTranslator *translator) 9 | { 10 | QApplication *app = qApp; 11 | #if PROJECT_TRANSLATION_TEST_ENABLED 12 | QLocale locale(QLocale(PROJECT_TRANSLATION_TEST_LANGUAGE)); 13 | QLocale::setDefault(locale); 14 | #else 15 | QLocale locale = QLocale::system(); 16 | #endif 17 | // Qt translations (buttons text and the like) 18 | QString translationsPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); 19 | QString translationsFileName = QStringLiteral("qt_") + locale.name(); 20 | 21 | if (qtTranslator->load(translationsFileName, translationsPath)) 22 | app->installTranslator(qtTranslator); 23 | 24 | translationsFileName = QString(PROJECT_ID) + '_' + locale.name(); // E.g. "_en" 25 | 26 | // Try first in the same binary directory, in case we are building, 27 | // otherwise read from system data 28 | translationsPath = QCoreApplication::applicationDirPath(); 29 | 30 | bool isLoaded = translator->load(translationsFileName, translationsPath); 31 | if (!isLoaded) { 32 | // "/usr/share//translations 33 | isLoaded = translator->load(translationsFileName, 34 | QStringLiteral(PROJECT_DATA_DIR) 35 | + QStringLiteral("/translations")); 36 | } 37 | app->installTranslator(translator); 38 | } 39 | 40 | int main(int argc, char *argv[]) 41 | { 42 | QApplication app(argc, argv); 43 | app.setApplicationName(PROJECT_ID); 44 | 45 | QTranslator qtTranslator, translator; 46 | initLocale(&qtTranslator, &translator); 47 | 48 | MainDialog w; 49 | w.show(); 50 | 51 | // Make work the window icon also when the application is not (yet) installed 52 | QString iconSuffix = QString("%1%2%3").arg("/", PROJECT_APPSTREAM_ID, QStringLiteral(".svg")); 53 | QString icoLocalPath = QCoreApplication::applicationDirPath() + iconSuffix; 54 | QString icoSysPath = QStringLiteral(PROJECT_ICON_SYSTEM_PATH) + iconSuffix; 55 | 56 | // If icoLocalPath exists, set to icolocalPath; else set to icoSysPath 57 | QIcon appIcon = (QFileInfo(icoLocalPath).exists()) ? QIcon(icoLocalPath) : QIcon(icoSysPath); 58 | 59 | w.setWindowIcon(appIcon); 60 | 61 | return app.exec(); 62 | } 63 | -------------------------------------------------------------------------------- /src/maindialog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "environment.h" 5 | #include "evdev-lst-layouts.h" 6 | #include "layoutmodel.h" 7 | #include "maindialog.h" 8 | #include "./ui_maindialog.h" 9 | 10 | extern "C" { 11 | #include "theme.h" 12 | #include "xml.h" 13 | } 14 | 15 | MainDialog::MainDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MainDialog) 16 | { 17 | ui->setupUi(this); 18 | 19 | ui->list->setFixedWidth(ui->list->sizeHintForColumn(0) + 2 * ui->list->frameWidth()); 20 | 21 | m_model = new LayoutModel(this); 22 | ui->layoutView->setModel(m_model); 23 | 24 | std::string config_dir = 25 | std::getenv("LABWC_CONFIG_DIR") ?: std::getenv("HOME") + std::string("/.config/labwc"); 26 | std::string config_file = config_dir + "/rc.xml"; 27 | initConfig(config_file); 28 | 29 | QObject::connect(ui->buttonBox, &QDialogButtonBox::clicked, [&](QAbstractButton *button) { 30 | if (ui->buttonBox->standardButton(button) == QDialogButtonBox::Apply) { 31 | onApply(); 32 | } 33 | }); 34 | 35 | connect(ui->layoutAdd, &QPushButton::pressed, this, &MainDialog::addSelectedLayout); 36 | connect(ui->layoutRemove, &QPushButton::pressed, this, &MainDialog::deleteSelectedLayout); 37 | 38 | activate(); 39 | } 40 | 41 | MainDialog::~MainDialog() 42 | { 43 | delete ui; 44 | xml_finish(); 45 | } 46 | 47 | void MainDialog::addSelectedLayout(void) 48 | { 49 | const char *description = ui->layoutCombo->currentText().toLatin1().data(); 50 | for (auto layout : evdev_lst_layouts) { 51 | if (!strcmp(description, layout.description)) { 52 | m_model->addLayout(QString(layout.code), QString(layout.description)); 53 | } 54 | } 55 | } 56 | 57 | void MainDialog::deleteSelectedLayout(void) 58 | { 59 | m_model->deleteLayout(ui->layoutView->currentIndex().row()); 60 | } 61 | 62 | void MainDialog::activate() 63 | { 64 | /* # APPEARANCE */ 65 | 66 | /* Openbox Theme */ 67 | struct themes openbox_themes = { 0 }; 68 | theme_find(&openbox_themes, "themes", "openbox-3/themerc"); 69 | int active = -1; 70 | const char *active_id = xml_get("/labwc_config/theme/name"); 71 | for (int i = 0; i < openbox_themes.nr; ++i) { 72 | struct theme *theme = openbox_themes.data + i; 73 | if (active_id && !strcmp(theme->name, active_id)) { 74 | active = i; 75 | } 76 | ui->openboxTheme->addItem(theme->name); 77 | } 78 | if (active != -1) { 79 | ui->openboxTheme->setCurrentIndex(active); 80 | } 81 | theme_free_vector(&openbox_themes); 82 | 83 | /* Corner Radius */ 84 | ui->cornerRadius->setValue(xml_get_int("/labwc_config/theme/cornerradius")); 85 | 86 | /* Drop Shadows */ 87 | ui->dropShadows->addItem("no"); 88 | ui->dropShadows->addItem("yes"); 89 | ui->dropShadows->setCurrentIndex(xml_get_bool_text("/labwc_config/theme/dropShadows")); 90 | 91 | /* # BEHAVIOUR */ 92 | std::vector policies = { "", "Automatic", "Cascade", "Center", "Cursor" }; 93 | active = -1; 94 | active_id = xml_get("/labwc_config/placement/policy"); 95 | int i = 0; 96 | for (auto policy : policies) { 97 | if (active_id && !strcasecmp(policy, active_id)) { 98 | active = i; 99 | } 100 | ui->placementPolicy->addItem(policy); 101 | ++i; 102 | } 103 | if (active != -1) { 104 | ui->placementPolicy->setCurrentIndex(active); 105 | } 106 | 107 | /* # MOUSE & TOUCHPAD */ 108 | 109 | /* Cursor Theme */ 110 | struct themes cursor_themes = { 0 }; 111 | theme_find(&cursor_themes, "icons", "cursors"); 112 | 113 | active_id = getenv("XCURSOR_THEME") ?: (char *)""; 114 | active = -1; 115 | for (int i = 0; i < cursor_themes.nr; ++i) { 116 | struct theme *theme = cursor_themes.data + i; 117 | if (!strcmp(theme->name, active_id)) { 118 | active = i; 119 | } 120 | ui->cursorTheme->addItem(theme->name); 121 | } 122 | if (active != -1) { 123 | ui->cursorTheme->setCurrentIndex(active); 124 | } 125 | theme_free_vector(&cursor_themes); 126 | 127 | /* Cursor Size */ 128 | ui->cursorSize->setValue(atoi(getenv("XCURSOR_SIZE") ?: "24")); 129 | 130 | /* Natural Scroll */ 131 | ui->naturalScroll->addItem("no"); 132 | ui->naturalScroll->addItem("yes"); 133 | ui->naturalScroll->setCurrentIndex( 134 | xml_get_bool_text("/labwc_config/libinput/device/naturalscroll")); 135 | 136 | /* # LANGUAGE */ 137 | 138 | /* Keyboard Layout */ 139 | ui->layoutCombo->addItem(tr("Select layout to add...")); 140 | for (auto layout : evdev_lst_layouts) { 141 | ui->layoutCombo->addItem(QString(layout.description)); 142 | } 143 | } 144 | 145 | void MainDialog::initConfig(std::string &config_file) 146 | { 147 | xml_init(config_file.data()); 148 | 149 | /* Ensure all relevant nodes exist before we start getting/setting */ 150 | xpath_add_node("/labwc_config/theme/cornerRadius"); 151 | xpath_add_node("/labwc_config/theme/name"); 152 | xpath_add_node("/labwc_config/theme/dropShadows"); 153 | xpath_add_node("/labwc_config/placement/policy"); 154 | xpath_add_node("/labwc_config/libinput/device/naturalScroll"); 155 | 156 | xml_save(); 157 | } 158 | 159 | void MainDialog::onApply() 160 | { 161 | /* ~/.config/labwc/rc.xml */ 162 | xml_set_num("/labwc_config/theme/cornerradius", ui->cornerRadius->value()); 163 | xml_set("/labwc_config/theme/name", ui->openboxTheme->currentText().toLatin1().data()); 164 | xml_set("/labwc_config/theme/dropShadows", ui->dropShadows->currentText().toLatin1().data()); 165 | xml_set("/labwc_config/libinput/device/naturalscroll", 166 | ui->naturalScroll->currentText().toLatin1().data()); 167 | xml_set("/labwc_config/placement/policy", ui->placementPolicy->currentText().toLatin1().data()); 168 | xml_save(); 169 | 170 | /* ~/.config/labwc/environment */ 171 | environment_set("XCURSOR_THEME", ui->cursorTheme->currentText().toLatin1().data()); 172 | environment_set_num("XCURSOR_SIZE", ui->cursorSize->value()); 173 | 174 | /* 175 | * We include variants in XKB_DEFAULT_LAYOUT, for example "latam(deadtilde),ru(phonetic),gr", 176 | * so XKB_DEFAULT_VARIANT is set to empty. 177 | */ 178 | const char *layout = m_model->getXkbDefaultLayout(); 179 | if (layout && *layout) { 180 | environment_set("XKB_DEFAULT_LAYOUT", layout); 181 | environment_set("XKB_DEFAULT_VARIANT", ""); 182 | } 183 | 184 | /* reconfigure labwc */ 185 | if (!fork()) { 186 | execl("/bin/sh", "/bin/sh", "-c", "labwc -r", (void *)NULL); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/maindialog.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINDIALOG_H 2 | #define MAINDIALOG_H 3 | #include 4 | #include "layoutmodel.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class MainDialog; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class MainDialog : public QDialog 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | MainDialog(QWidget *parent = nullptr); 18 | ~MainDialog(); 19 | void activate(); 20 | 21 | private slots: 22 | void addSelectedLayout(void); 23 | void deleteSelectedLayout(void); 24 | 25 | private: 26 | LayoutModel *m_model; 27 | 28 | void initConfig(std::string &config_file); 29 | void onApply(); 30 | 31 | Ui::MainDialog *ui; 32 | }; 33 | #endif // MAINDIALOG_H 34 | -------------------------------------------------------------------------------- /src/maindialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 640 10 | 480 11 | 12 | 13 | 14 | 15 | 6 16 | 17 | 18 | 6 19 | 20 | 21 | 6 22 | 23 | 24 | 6 25 | 26 | 27 | 28 | 29 | 30 | 6 31 | 32 | 33 | 6 34 | 35 | 36 | 6 37 | 38 | 39 | 6 40 | 41 | 42 | 43 | 44 | 45 | 0 46 | 0 47 | 48 | 49 | 50 | QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents 51 | 52 | 53 | 0 54 | 55 | 56 | 57 | Appearance 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | Behaviour 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Mouse & Touchpad 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Language & Region 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 6 95 | 96 | 97 | 6 98 | 99 | 100 | 6 101 | 102 | 103 | 6 104 | 105 | 106 | 107 | 108 | Labwc Theme 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Corner Radius 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | Drop Shadows 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 6 141 | 142 | 143 | 6 144 | 145 | 146 | 6 147 | 148 | 149 | 6 150 | 151 | 152 | 153 | 154 | Placement Policy 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 6 167 | 168 | 169 | 6 170 | 171 | 172 | 6 173 | 174 | 175 | 6 176 | 177 | 178 | 179 | 180 | Cursor Theme 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | Cursor Size 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | Natural Scroll 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 6 213 | 214 | 215 | 6 216 | 217 | 218 | 6 219 | 220 | 221 | 6 222 | 223 | 224 | 225 | 226 | Keyboard Layout 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 16777215 237 | 72 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | Add 251 | 252 | 253 | 254 | 255 | 256 | 257 | Remove 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | Qt::Orientation::Vertical 267 | 268 | 269 | 270 | 20 271 | 40 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | Qt::Orientation::Horizontal 289 | 290 | 291 | QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Close 292 | 293 | 294 | false 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | buttonBox 304 | accepted() 305 | MainDialog 306 | accept() 307 | 308 | 309 | 248 310 | 254 311 | 312 | 313 | 157 314 | 274 315 | 316 | 317 | 318 | 319 | buttonBox 320 | rejected() 321 | MainDialog 322 | reject() 323 | 324 | 325 | 316 326 | 260 327 | 328 | 329 | 286 330 | 274 331 | 332 | 333 | 334 | 335 | list 336 | currentRowChanged(int) 337 | stack 338 | setCurrentIndex(int) 339 | 340 | 341 | 145 342 | 248 343 | 344 | 345 | 459 346 | 248 347 | 348 | 349 | 350 | 351 | 352 | -------------------------------------------------------------------------------- /src/theme.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | #define _POSIX_C_SOURCE 200809L 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "theme.h" 13 | 14 | static struct theme * 15 | grow_vector_by_one_theme(struct themes *themes) 16 | { 17 | if (themes->nr == themes->alloc) { 18 | themes->alloc = (themes->alloc + 16) * 2; 19 | themes->data = realloc(themes->data, themes->alloc * sizeof(struct theme)); 20 | } 21 | struct theme *theme = themes->data + themes->nr; 22 | memset(theme, 0, sizeof(*theme)); 23 | themes->nr++; 24 | return theme; 25 | } 26 | 27 | static bool 28 | vector_contains(struct themes *themes, const char *needle) 29 | { 30 | assert(needle); 31 | for (int i = 0; i < themes->nr; ++i) { 32 | struct theme *theme = themes->data + i; 33 | if (!theme || !theme->name) { 34 | continue; 35 | } 36 | if ((!strcmp(theme->name, needle))) { 37 | return true; 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | static bool 44 | isdir(const char *path, const char *dirname) 45 | { 46 | char buf[4096]; 47 | snprintf(buf, sizeof(buf), "%s/%s", path, dirname); 48 | struct stat st; 49 | return (!stat(buf, &st) && S_ISDIR(st.st_mode)); 50 | } 51 | 52 | /** 53 | * add_theme_if_icon_theme - add theme iff it is a proper icon theme 54 | * @themes: vector 55 | * @path: path to directory to search in 56 | * The criteria for deciding if a icon theme is a "proper icon theme" is to 57 | * verify the existance of a subdirectory other than "cursors" 58 | */ 59 | static void 60 | add_theme_if_icon_theme(struct themes *themes, const char *path) 61 | { 62 | struct dirent *entry; 63 | DIR *dp; 64 | struct stat st; 65 | 66 | dp = opendir(path); 67 | if (!dp) { 68 | return; 69 | } 70 | while ((entry = readdir(dp))) { 71 | if (entry->d_name[0] == '.' || !isdir(path, entry->d_name)) { 72 | continue; 73 | } 74 | 75 | char buf[4096]; 76 | snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name); 77 | /* filter 'hicolor' as it is not a complete icon set */ 78 | if (strstr(buf, "hicolor") != NULL) { 79 | continue; 80 | } 81 | 82 | /* process subdirectories within the theme directory */ 83 | struct dirent *sub_entry; 84 | DIR *sub_dp; 85 | sub_dp = opendir(buf); 86 | if (!sub_dp) { 87 | return; 88 | } 89 | 90 | /* 91 | * Add theme if directory other than 'cursors' exists. 92 | * This could be "scalable", "22x22", or whatever... 93 | */ 94 | while ((sub_entry = readdir(sub_dp))) { 95 | if (sub_entry->d_name[0] == '.' || !isdir(buf, sub_entry->d_name)) { 96 | continue; 97 | } 98 | if (!strcmp(sub_entry->d_name, "cursors")) { 99 | continue; 100 | } 101 | 102 | /* We've found a directory other than "cursors"! */ 103 | struct theme *theme = NULL; 104 | if (!stat(buf, &st)) { 105 | theme = grow_vector_by_one_theme(themes); 106 | theme->name = strdup(entry->d_name); 107 | theme->path = strdup(buf); 108 | } 109 | break; 110 | } 111 | closedir(sub_dp); 112 | } 113 | closedir(dp); 114 | } 115 | 116 | /** 117 | * process_dir - find themes and store them in vector 118 | * @themes: vector 119 | * @path: path to directory to search in 120 | * @filename: filename for which a successful stat will count as 'found theme', 121 | * using the schema @path//@filename 122 | * 123 | * For example, to find the Numix openbox theme at 124 | * /usr/share/themes/Numix/openbox-3/themerc, the following parameters would 125 | * be used: 126 | * @path = "/usr/share/themes" 127 | * @filename = "openbox-3/themerc" 128 | */ 129 | static void 130 | process_dir(struct themes *themes, const char *path, const char *filename) 131 | { 132 | struct dirent *entry; 133 | DIR *dp; 134 | struct stat st; 135 | struct theme *theme = NULL; 136 | 137 | dp = opendir(path); 138 | if (!dp) { 139 | return; 140 | } 141 | while ((entry = readdir(dp))) { 142 | if (entry->d_name[0] != '.' && isdir(path, entry->d_name)) { 143 | char buf[4096]; 144 | snprintf(buf, sizeof(buf), "%s/%s/%s", path, entry->d_name, filename); 145 | /* filter 'hicolor' as it is not a complete icon set */ 146 | if (strstr(buf, "hicolor") != NULL) { 147 | continue; 148 | } 149 | if (!stat(buf, &st) && !vector_contains(themes, entry->d_name)) { 150 | theme = grow_vector_by_one_theme(themes); 151 | theme->name = strdup(entry->d_name); 152 | theme->path = strdup(buf); 153 | } 154 | } 155 | } 156 | closedir(dp); 157 | } 158 | 159 | /* Sort system themes in alphabetical order */ 160 | static int 161 | compare(const void *a, const void *b) 162 | { 163 | const struct theme *theme_a = (struct theme *)a; 164 | const struct theme *theme_b = (struct theme *)b; 165 | return strcasecmp(theme_a->name, theme_b->name); 166 | } 167 | 168 | static struct { 169 | const char *prefix; 170 | const char *path; 171 | } dirs[] = { 172 | { "XDG_DATA_HOME", "" }, 173 | { "HOME", ".local/share" }, 174 | { "XDG_DATA_DIRS", "" }, 175 | { NULL, "/usr/share" }, 176 | { NULL, "/usr/local/share" }, 177 | { NULL, "/opt/share" }, 178 | }; 179 | 180 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 181 | 182 | void 183 | theme_find(struct themes *themes, const char *middle, const char *end) 184 | { 185 | struct theme *theme = NULL; 186 | 187 | /* 188 | * Always start with an empty entry to allow users to set it to nothing 189 | * to use default values. 190 | */ 191 | theme = grow_vector_by_one_theme(themes); 192 | theme->name = strdup(""); 193 | 194 | char path[4096]; 195 | for (uint32_t i = 0; i < ARRAY_SIZE(dirs); ++i) { 196 | if (dirs[i].prefix) { 197 | char *prefix = getenv(dirs[i].prefix); 198 | if (!prefix) { 199 | continue; 200 | } 201 | snprintf(path, sizeof(path), "%s/%s/%s", prefix, dirs[i].path, middle); 202 | } else { 203 | snprintf(path, sizeof(path), "%s/%s", dirs[i].path, middle); 204 | } 205 | 206 | if (end) { 207 | /* 208 | * Add theme if 209 | * "$DATA_DIR/@middle//@end" exists 210 | */ 211 | process_dir(themes, path, end); 212 | } else { 213 | /* 214 | * Add icon theme iff "$DATA_DIR/@middle//" 215 | * contains a subdirectory other than "cursors". 216 | * Note: searching for index.theme only is not good 217 | * enough because some cursor themes contain the same 218 | * file and some themes contain both cursors and icons. 219 | */ 220 | add_theme_if_icon_theme(themes, path); 221 | } 222 | } 223 | 224 | /* 225 | * In some distros Adwaita is built-in to gtk+-3.0 and subsequently no 226 | * theme dir exists. In this case we add it manually. 227 | */ 228 | if (!vector_contains(themes, "Adwaita")) { 229 | theme = grow_vector_by_one_theme(themes); 230 | theme->name = strdup("Adwaita"); 231 | theme->path = NULL; 232 | } 233 | 234 | qsort(themes->data, themes->nr, sizeof(struct theme), compare); 235 | } 236 | 237 | void 238 | theme_free_vector(struct themes *themes) 239 | { 240 | for (int i = 0; i < themes->nr; ++i) { 241 | struct theme *theme = themes->data + i; 242 | free(theme->name); 243 | free(theme->path); 244 | } 245 | free(themes->data); 246 | themes->data = NULL; 247 | } 248 | 249 | -------------------------------------------------------------------------------- /src/theme.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | #ifndef THEME_H 3 | #define THEME_H 4 | #include 5 | 6 | struct theme { 7 | char *name; 8 | char *path; 9 | }; 10 | 11 | struct themes { 12 | struct theme *data; 13 | int nr, alloc; 14 | }; 15 | 16 | void theme_find(struct themes *themes, const char *middle, const char *end); 17 | void theme_free_vector(struct themes *themes); 18 | 19 | #endif /* THEME_H */ 20 | -------------------------------------------------------------------------------- /src/xml.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | #define _POSIX_C_SOURCE 200809L 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "xml.h" 17 | 18 | static struct ctx { 19 | char *filename; 20 | xmlDoc *doc; 21 | xmlXPathContextPtr xpath_ctx_ptr; 22 | const char *nodename; 23 | const char *value; 24 | xmlNode *node; 25 | enum { 26 | XML_MODE_SETTING = 0, 27 | XML_MODE_GETTING, 28 | } mode; 29 | } ctx; 30 | 31 | static void 32 | entry(xmlNode *node, char *nodename, char *content) 33 | { 34 | if (!nodename) 35 | return; 36 | if (!strcasecmp(nodename, ctx.nodename)) { 37 | if (ctx.mode == XML_MODE_SETTING) { 38 | xmlNodeSetContent(node, (const xmlChar *)ctx.value); 39 | } else if (ctx.mode == XML_MODE_GETTING) { 40 | ctx.value = (char *)content; 41 | ctx.node = node; 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * nodename - return simplistic xpath style nodename 48 | * For example: is represented by nodename /a/b/c 49 | */ 50 | static char * 51 | nodename(xmlNode *node, char *buf, int len) 52 | { 53 | if (!node || !node->name) { 54 | return NULL; 55 | } 56 | 57 | /* Ignore superflous '/text' in node name */ 58 | if (node->parent && !strcmp((char *)node->name, "text")) { 59 | node = node->parent; 60 | } 61 | 62 | buf += len; 63 | *--buf = 0; 64 | len--; 65 | 66 | for (;;) { 67 | const char *name = (char *)node->name; 68 | int i = strlen(name); 69 | while (--i >= 0) { 70 | unsigned char c = name[i]; 71 | *--buf = tolower(c); 72 | if (!--len) 73 | return buf; 74 | } 75 | node = node->parent; 76 | if (!node || !node->name) { 77 | *--buf = '/'; 78 | return buf; 79 | } 80 | *--buf = '/'; 81 | if (!--len) 82 | return buf; 83 | } 84 | } 85 | 86 | static void 87 | process_node(xmlNode *node) 88 | { 89 | char *content; 90 | static char buffer[256]; 91 | char *name; 92 | 93 | content = (char *)node->content; 94 | if (xmlIsBlankNode(node)) { 95 | return; 96 | } 97 | name = nodename(node, buffer, sizeof(buffer)); 98 | entry(node, name, content); 99 | } 100 | 101 | static void xml_tree_walk(xmlNode *node); 102 | 103 | static void 104 | traverse(xmlNode *n) 105 | { 106 | process_node(n); 107 | for (xmlAttr *attr = n->properties; attr; attr = attr->next) { 108 | xml_tree_walk(attr->children); 109 | } 110 | xml_tree_walk(n->children); 111 | } 112 | 113 | static void 114 | xml_tree_walk(xmlNode *node) 115 | { 116 | for (xmlNode *n = node; n && n->name; n = n->next) { 117 | if (!strcasecmp((char *)n->name, "comment")) { 118 | continue; 119 | } 120 | traverse(n); 121 | } 122 | } 123 | 124 | static const char rcxml_template[] = 125 | "\n" 126 | "\n" 127 | " \n" 128 | " \n" 129 | "\n"; 130 | 131 | static void 132 | create_basic_rcxml(const char *filename) 133 | { 134 | FILE *file = fopen(filename, "w"); 135 | if (!file) { 136 | fprintf(stderr, "warn: fopen(%s) failed\n", filename); 137 | return; 138 | } 139 | if (!fwrite(rcxml_template, sizeof(rcxml_template)-1, 1, file)) { 140 | fprintf(stderr, "warn: error writing to %s", filename); 141 | } 142 | fclose(file); 143 | } 144 | 145 | void 146 | xml_init(const char *filename) 147 | { 148 | LIBXML_TEST_VERSION 149 | 150 | if (access(filename, F_OK)) { 151 | create_basic_rcxml(filename); 152 | } 153 | 154 | /* Use XML_PARSE_NOBLANKS for xmlSaveFormatFile() to indent properly */ 155 | ctx.filename = strdup(filename); 156 | ctx.doc = xmlReadFile(filename, NULL, XML_PARSE_NOBLANKS); 157 | if (!ctx.doc) { 158 | fprintf(stderr, "warn: xmlReadFile('%s')\n", filename); 159 | } 160 | ctx.xpath_ctx_ptr = xmlXPathNewContext(ctx.doc); 161 | if (!ctx.xpath_ctx_ptr) { 162 | fprintf(stderr, "warn: xmlXPathNewContext()\n"); 163 | xmlFreeDoc(ctx.doc); 164 | } 165 | } 166 | 167 | void 168 | xml_save(void) 169 | { 170 | xmlSaveFormatFile(ctx.filename, ctx.doc, 1); 171 | } 172 | 173 | void 174 | xml_save_as(const char *filename) 175 | { 176 | xmlSaveFormatFile(filename, ctx.doc, 1); 177 | } 178 | 179 | void 180 | xml_finish(void) 181 | { 182 | xmlXPathFreeContext(ctx.xpath_ctx_ptr); 183 | xmlFreeDoc(ctx.doc); 184 | xmlCleanupParser(); 185 | free(ctx.filename); 186 | } 187 | 188 | void 189 | xml_set(const char *nodename, const char *value) 190 | { 191 | ctx.nodename = nodename; 192 | ctx.value = value; 193 | ctx.mode = XML_MODE_SETTING; 194 | xml_tree_walk(xmlDocGetRootElement(ctx.doc)); 195 | } 196 | 197 | void 198 | xml_set_num(const char *nodename, double value) 199 | { 200 | char buf[64]; 201 | snprintf(buf, sizeof(buf), "%.0f", value); 202 | ctx.nodename = nodename; 203 | ctx.value = buf; 204 | ctx.mode = XML_MODE_SETTING; 205 | xml_tree_walk(xmlDocGetRootElement(ctx.doc)); 206 | } 207 | 208 | const char * 209 | xml_get(const char *nodename) 210 | { 211 | ctx.nodename = nodename; 212 | ctx.mode = XML_MODE_GETTING; 213 | xml_tree_walk(xmlDocGetRootElement(ctx.doc)); 214 | return ctx.value; 215 | } 216 | 217 | int 218 | xml_get_int(const char *nodename) 219 | { 220 | ctx.nodename = nodename; 221 | ctx.mode = XML_MODE_GETTING; 222 | xml_tree_walk(xmlDocGetRootElement(ctx.doc)); 223 | return ctx.value ? atoi(ctx.value) : 0; 224 | } 225 | 226 | int 227 | xml_get_bool_text(const char *nodename) 228 | { 229 | const char *value = xml_get(nodename); 230 | 231 | /* handle and where no value has been specified */ 232 | if (!value || !*value) { 233 | return -1; 234 | } 235 | if (!strcasecmp(value, "yes") || !strcasecmp(value, "true")) { 236 | return 1; 237 | } else if (!strcasecmp(value, "no") || !strcasecmp(value, "false")) { 238 | return 0; 239 | } else { 240 | return -1; 241 | } 242 | } 243 | 244 | /* case-insensitive */ 245 | static xmlNode * 246 | xml_get_node(const char *nodename) 247 | { 248 | ctx.node = NULL; 249 | ctx.nodename = nodename; 250 | ctx.mode = XML_MODE_GETTING; 251 | xml_tree_walk(xmlDocGetRootElement(ctx.doc)); 252 | return ctx.node; 253 | } 254 | 255 | char * 256 | xpath_get_content(const char *xpath_expr) 257 | { 258 | xmlChar *ret = NULL; 259 | xmlXPathObjectPtr object = xmlXPathEvalExpression((xmlChar *)xpath_expr, ctx.xpath_ctx_ptr); 260 | if (!object) { 261 | fprintf(stderr, "warn: xmlXPathEvalExpression()\n"); 262 | return NULL; 263 | } 264 | if (!object->nodesetval) { 265 | fprintf(stderr, "warn: no nodesetval\n"); 266 | goto out; 267 | } 268 | for (int i = 0; i < object->nodesetval->nodeNr; i++) { 269 | if (!object->nodesetval->nodeTab[i]) { 270 | continue; 271 | } 272 | 273 | /* Just grab the first node and go */ 274 | ret = xmlNodeGetContent(object->nodesetval->nodeTab[i]); 275 | goto out; 276 | 277 | /* 278 | * We could process the node here and do things like: 279 | * xmlNode *children = object->nodesetval->nodeTab[i]->children; 280 | * for (xmlNode *cur = children; cur; cur = cur->next) { } 281 | */ 282 | } 283 | 284 | out: 285 | xmlXPathFreeObject(object); 286 | return (char *)ret; 287 | } 288 | 289 | /* case-sensitive */ 290 | static xmlNode * 291 | xpath_get_node(xmlChar *expr) 292 | { 293 | xmlNode *ret = NULL; 294 | xmlXPathObjectPtr object = xmlXPathEvalExpression(expr, ctx.xpath_ctx_ptr); 295 | if (!object) { 296 | fprintf(stderr, "warn: xmlXPathEvalExpression()\n"); 297 | return NULL; 298 | } 299 | if (!object->nodesetval) { 300 | fprintf(stderr, "warn: no nodesetval\n"); 301 | goto out2; 302 | } 303 | 304 | for (int i = 0; i < object->nodesetval->nodeNr; i++) { 305 | if (!object->nodesetval->nodeTab[i]) { 306 | continue; 307 | } 308 | ret = object->nodesetval->nodeTab[i]; 309 | break; 310 | } 311 | out2: 312 | xmlXPathFreeObject(object); 313 | return ret; 314 | } 315 | 316 | void 317 | xpath_add_node(const char *xpath_expr) 318 | { 319 | if (xml_get_node(xpath_expr)) { 320 | return; 321 | } 322 | 323 | /* find existing parent */ 324 | char *parent_expr = strdup(xpath_expr); 325 | xmlNode *parent_node = NULL; 326 | while (parent_expr && *parent_expr) { 327 | parent_node = xpath_get_node((xmlChar *)parent_expr); 328 | if (parent_node) { 329 | break; 330 | } 331 | char *p = strrchr(parent_expr, '/'); 332 | if (p && *p) { 333 | *p = '\0'; 334 | } else { 335 | break; 336 | } 337 | } 338 | assert(parent_expr); 339 | if (!*parent_expr) { 340 | /* the whole xpath expression is new, so add to root */ 341 | parent_node = xmlDocGetRootElement(ctx.doc); 342 | } 343 | 344 | /* add new nodes */ 345 | gchar **nodes = g_strsplit(xpath_expr + strlen(parent_expr), "/", -1); 346 | for (gchar **s = nodes; *s; s++) { 347 | if (*s && **s) { 348 | parent_node = xmlNewChild(parent_node, NULL, (xmlChar *)*s, NULL); 349 | } 350 | } 351 | g_free(parent_expr); 352 | g_strfreev(nodes); 353 | } 354 | -------------------------------------------------------------------------------- /src/xml.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only */ 2 | #ifndef __XML_H 3 | #define __XML_H 4 | 5 | void xml_init(const char *filename); 6 | void xml_save(void); 7 | void xml_save_as(const char *filename); 8 | void xml_finish(void); 9 | void xml_set(const char *nodename, const char *value); 10 | void xml_set_num(const char *nodename, double value); 11 | const char *xml_get(const char *nodename); 12 | int xml_get_int(const char *nodename); 13 | int xml_get_bool_text(const char *nodename); 14 | 15 | /** 16 | * xpath_get_content() - Get content of node specified by xpath 17 | * @xpath_expr: xpath expression for node 18 | */ 19 | char *xpath_get_content(const char *xpath_expr); 20 | 21 | /** 22 | * xpath_add_node - add xml nodes from xpath 23 | * @xpath_expr: xpath expression for new node 24 | * For example xpath_expr="/labwc_config/a/b/c" creates 25 | * 26 | */ 27 | void xpath_add_node(const char *xpath_expr); 28 | 29 | #endif /* __XML_H */ 30 | -------------------------------------------------------------------------------- /tests/t1000-add-xpath-node.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "tap.h" 7 | #include "../src/xml.h" 8 | 9 | static char template[] = 10 | "\n" 11 | "\n" 12 | " \n" 13 | " \n" 14 | " \n" 15 | "\n"; 16 | 17 | void show_diff(const char *filename, const char *buf, size_t size) 18 | { 19 | char cmd[1000]; 20 | snprintf(cmd, sizeof(cmd), "diff -u - %s >&2", filename); 21 | FILE *f = popen(cmd, "w"); 22 | fwrite(buf, size, 1, f); 23 | pclose(f); 24 | } 25 | 26 | void test(const char *filename, const char *expect) 27 | { 28 | gsize length; gchar *actual; 29 | g_file_get_contents(filename, &actual, &length, NULL); 30 | bool is_equal = strcmp(actual, expect) == 0; 31 | ok1(is_equal); 32 | if (!is_equal) 33 | show_diff(filename, expect, strlen(expect)); 34 | g_free(actual); 35 | } 36 | 37 | int main(int argc, char **argv) 38 | { 39 | char in[] = "/tmp/t1000-expect_XXXXXX"; 40 | char out[] = "/tmp/t1000-actual"; 41 | 42 | plan(4); 43 | 44 | int fd = mkstemp(in); 45 | if (fd < 0) 46 | exit(EXIT_FAILURE); 47 | write(fd, template, sizeof(template) - 1); 48 | 49 | /* test 1 */ 50 | diag("add node using xpath (lowercase)"); 51 | xml_init(in); 52 | xpath_add_node("/labwc_config/theme/cornerradius"); 53 | xml_save_as(out); 54 | xml_finish(); 55 | test(out, 56 | "\n" 57 | "\n" 58 | " \n" 59 | " \n" 60 | " \n" 61 | " \n" 62 | " \n" 63 | " \n" 64 | "\n"); 65 | 66 | /* test 2 */ 67 | diag("add node using xpath (camelCase)"); 68 | xml_init(in); 69 | xpath_add_node("/labwc_config/theme/cornerRadius"); 70 | xml_save_as(out); 71 | xml_finish(); 72 | test(out, 73 | "\n" 74 | "\n" 75 | " \n" 76 | " \n" 77 | " \n" 78 | " \n" 79 | " \n" 80 | " \n" 81 | "\n"); 82 | 83 | /* test 3 */ 84 | diag("check xpath does not add duplicate entries - when identical"); 85 | xml_init(in); 86 | xpath_add_node("/labwc_config/theme/cornerradius"); 87 | xpath_add_node("/labwc_config/theme/cornerradius"); 88 | xml_save_as(out); 89 | xml_finish(); 90 | test(out, 91 | "\n" 92 | "\n" 93 | " \n" 94 | " \n" 95 | " \n" 96 | " \n" 97 | " \n" 98 | " \n" 99 | "\n"); 100 | 101 | /* test 4 */ 102 | diag("check xpath does not add duplicate entries - even if they have differing capitalisation"); 103 | xml_init(in); 104 | xpath_add_node("/labwc_config/theme/cornerradius"); 105 | xpath_add_node("/labwc_config/theme/Cornerradius"); 106 | xml_save_as(out); 107 | xml_finish(); 108 | test(out, 109 | "\n" 110 | "\n" 111 | " \n" 112 | " \n" 113 | " \n" 114 | " \n" 115 | " \n" 116 | " \n" 117 | "\n"); 118 | 119 | unlink(in); 120 | unlink(out); 121 | return exit_status(); 122 | } 123 | 124 | -------------------------------------------------------------------------------- /tests/t1001-nodenames.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "tap.h" 7 | #include "../src/xml.c" 8 | 9 | static char template[] = 10 | "\n" 11 | "\n" 12 | " \n" 13 | " \n" 14 | " \n" 15 | "\n"; 16 | 17 | void test(const char *nodename, const char *expect) 18 | { 19 | bool is_equal = strcmp(nodename, expect) == 0; 20 | ok1(is_equal); 21 | if (!is_equal) 22 | fprintf(stderr, "%s\n%s\n", nodename, expect); 23 | } 24 | 25 | int main(int argc, char **argv) 26 | { 27 | char in[] = "/tmp/t1001-expect_XXXXXX"; 28 | static char buffer[256] = { 0 }; 29 | 30 | plan(1); 31 | 32 | int fd = mkstemp(in); 33 | if (fd < 0) 34 | exit(EXIT_FAILURE); 35 | write(fd, template, sizeof(template) - 1); 36 | 37 | /* test 1 */ 38 | diag("generate simple xpath style nodename"); 39 | xml_init(in); 40 | xmlNode *node = xpath_get_node((xmlChar *)"/labwc_config/core/gap"); 41 | char *name = nodename(node, buffer, sizeof(buffer)); 42 | xml_finish(); 43 | test(name, "/labwc_config/core/gap"); 44 | 45 | unlink(in); 46 | return exit_status(); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /tests/tap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "tap.h" 9 | 10 | static int nr_tests_run; 11 | static int nr_tests_expected; 12 | static int nr_tests_failed; 13 | 14 | void plan(int nr_tests) 15 | { 16 | static bool run_once; 17 | 18 | if (run_once) 19 | return; 20 | run_once = true; 21 | printf("1..%d\n", nr_tests); 22 | nr_tests_expected = nr_tests; 23 | } 24 | 25 | void diag(const char *fmt, ...) 26 | { 27 | va_list params; 28 | 29 | fprintf(stdout, "# "); 30 | va_start(params, fmt); 31 | vfprintf(stdout, fmt, params); 32 | va_end(params); 33 | fprintf(stdout, "\n"); 34 | } 35 | 36 | int ok(int result, const char *testname, ...) 37 | { 38 | va_list params; 39 | 40 | ++nr_tests_run; 41 | if (!result) { 42 | printf("not "); 43 | nr_tests_failed++; 44 | } 45 | printf("ok %d", nr_tests_run); 46 | if (testname) { 47 | printf(" - "); 48 | va_start(params, testname); 49 | vfprintf(stdout, testname, params); 50 | va_end(params); 51 | } 52 | printf("\n"); 53 | if (!result) 54 | diag(" Failed test"); 55 | return result ? 1 : 0; 56 | } 57 | 58 | int exit_status(void) 59 | { 60 | int ret; 61 | 62 | if (nr_tests_expected != nr_tests_run) { 63 | diag("expected=%d; run=%d; failed=%d", nr_tests_expected, 64 | nr_tests_run, nr_tests_failed); 65 | } 66 | if (nr_tests_expected < nr_tests_run) 67 | ret = nr_tests_run - nr_tests_expected; 68 | else 69 | ret = nr_tests_failed + nr_tests_expected - nr_tests_run; 70 | if (ret > 255) 71 | ret = 255; 72 | return ret; 73 | } 74 | -------------------------------------------------------------------------------- /tests/tap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimalist, partial TAP implementation 3 | * 4 | * Copyright Johan Malm 2020 5 | */ 6 | 7 | #ifndef TAP_H 8 | #define TAP_H 9 | 10 | #define ok1(__x__) (ok(__x__, "%s", #__x__)) 11 | 12 | void plan(int nr_tests); 13 | void diag(const char *fmt, ...); 14 | int ok(int result, const char *test_name, ...); 15 | int exit_status(void); 16 | 17 | #endif /* TAP_H */ 18 | --------------------------------------------------------------------------------