├── .clang-format ├── .github └── workflows │ └── check.yml ├── .gitignore ├── CMakeLists.txt ├── ChangeLog ├── LICENSES └── LGPL-2.1-or-later.txt ├── Messages.sh ├── README.md ├── cmake ├── FindPthread.cmake ├── FindRimeData.cmake └── cmake_uninstall.cmake.in ├── data ├── 48x48 │ └── apps │ │ ├── fcitx-rime.png │ │ ├── fcitx_rime_deploy.png │ │ ├── fcitx_rime_disable.png │ │ ├── fcitx_rime_im.png │ │ ├── fcitx_rime_latin.png │ │ ├── fcitx_rime_sync.png │ │ ├── org.fcitx.Fcitx5.fcitx-rime.png │ │ ├── org.fcitx.Fcitx5.fcitx_rime_deploy.png │ │ ├── org.fcitx.Fcitx5.fcitx_rime_disable.png │ │ ├── org.fcitx.Fcitx5.fcitx_rime_im.png │ │ ├── org.fcitx.Fcitx5.fcitx_rime_latin.png │ │ └── org.fcitx.Fcitx5.fcitx_rime_sync.png ├── CMakeLists.txt ├── fcitx5.yaml └── scalable │ └── apps │ ├── fcitx-rime.svg │ ├── fcitx_rime_deploy.svg │ ├── fcitx_rime_disable.svg │ ├── fcitx_rime_im.svg │ ├── fcitx_rime_latin.svg │ ├── fcitx_rime_sync.svg │ ├── org.fcitx.Fcitx5.fcitx-rime.svg │ ├── org.fcitx.Fcitx5.fcitx_rime_deploy.svg │ ├── org.fcitx.Fcitx5.fcitx_rime_disable.svg │ ├── org.fcitx.Fcitx5.fcitx_rime_im.svg │ ├── org.fcitx.Fcitx5.fcitx_rime_latin.svg │ └── org.fcitx.Fcitx5.fcitx_rime_sync.svg ├── org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in ├── po ├── CMakeLists.txt ├── LINGUAS ├── ca.po ├── da.po ├── de.po ├── fcitx5-rime.pot ├── he.po ├── ja.po ├── ko.po ├── ru.po ├── tr.po ├── vi.po ├── zh_CN.po └── zh_TW.po └── src ├── CMakeLists.txt ├── rime-addon.conf.in.in ├── rime.conf.in ├── rimeaction.cpp ├── rimeaction.h ├── rimecandidate.cpp ├── rimecandidate.h ├── rimeengine.cpp ├── rimeengine.h ├── rimefactory.cpp ├── rimefactory.h ├── rimeservice.cpp ├── rimeservice.h ├── rimesession.cpp ├── rimesession.h ├── rimestate.cpp └── rimestate.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -4 5 | ConstructorInitializerIndentWidth: 4 6 | AlignEscapedNewlinesLeft: false 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: true 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortIfStatementsOnASingleLine: false 11 | AllowShortLoopsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: All 13 | AlwaysBreakTemplateDeclarations: true 14 | AlwaysBreakBeforeMultilineStrings: false 15 | BreakBeforeBinaryOperators: false 16 | BreakBeforeTernaryOperators: true 17 | BreakConstructorInitializersBeforeComma: false 18 | BinPackParameters: true 19 | ColumnLimit: 80 20 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 21 | DerivePointerAlignment: false 22 | ExperimentalAutoDetectBinPacking: false 23 | IndentCaseLabels: false 24 | IndentWrappedFunctionNames: false 25 | IndentFunctionDeclarationAfterType: false 26 | MaxEmptyLinesToKeep: 1 27 | KeepEmptyLinesAtTheStartOfBlocks: true 28 | NamespaceIndentation: None 29 | ObjCSpaceAfterProperty: false 30 | ObjCSpaceBeforeProtocolList: true 31 | PenaltyBreakBeforeFirstCallParameter: 19 32 | PenaltyBreakComment: 300 33 | PenaltyBreakString: 1000 34 | PenaltyBreakFirstLessLess: 120 35 | PenaltyExcessCharacter: 1000000 36 | PenaltyReturnTypeOnItsOwnLine: 60 37 | PointerAlignment: Right 38 | SpacesBeforeTrailingComments: 1 39 | Cpp11BracedListStyle: true 40 | Standard: Cpp11 41 | IndentWidth: 4 42 | TabWidth: 4 43 | UseTab: Never 44 | BreakBeforeBraces: Attach 45 | SpacesInParentheses: false 46 | SpacesInAngles: false 47 | SpaceInEmptyParentheses: false 48 | SpacesInCStyleCastParentheses: false 49 | SpacesInContainerLiterals: true 50 | SpaceBeforeAssignmentOperators: true 51 | ContinuationIndentWidth: 4 52 | CommentPragmas: '^ IWYU pragma:' 53 | ForEachMacros: [ Q_FOREACH, BOOST_FOREACH ] 54 | SpaceBeforeParens: ControlStatements 55 | DisableFormat: false 56 | SortIncludes: true 57 | ... 58 | 59 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | clang-format: 11 | name: Check clang-format 12 | runs-on: ubuntu-latest 13 | container: archlinux:latest 14 | steps: 15 | - name: Install dependencies 16 | run: | 17 | pacman -Syu --noconfirm git clang diffutils 18 | git config --global --add safe.directory $GITHUB_WORKSPACE 19 | - uses: actions/checkout@v4 20 | - uses: fcitx/github-actions@clang-format 21 | check: 22 | name: Build and test 23 | needs: clang-format 24 | runs-on: ubuntu-latest 25 | container: archlinux:latest 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | compiler: [gcc, clang] 30 | include: 31 | - compiler: gcc 32 | cxx_compiler: g++ 33 | - compiler: clang 34 | cxx_compiler: clang++ 35 | env: 36 | CC: ${{ matrix.compiler }} 37 | CXX: ${{ matrix.cxx_compiler }} 38 | steps: 39 | - name: Install dependencies 40 | run: | 41 | pacman -Syu --noconfirm base-devel clang cmake ninja extra-cmake-modules fmt libuv librime librime-data 42 | - uses: actions/checkout@v4 43 | with: 44 | repository: fcitx/fcitx5 45 | path: fcitx5 46 | - name: Cache fcitx5 data files 47 | uses: actions/cache@v4 48 | with: 49 | path: 'fcitx5/**/*.tar.*' 50 | key: ${{ runner.os }}-${{ hashFiles('fcitx5/src/modules/spell/CMakeLists.txt') 51 | }} 52 | - name: Build and Install fcitx5 53 | uses: fcitx/github-actions@cmake 54 | with: 55 | path: fcitx5 56 | cmake-option: >- 57 | -DENABLE_KEYBOARD=Off -DENABLE_X11=Off -DENABLE_WAYLAND=Off -DENABLE_ENCHANT=Off 58 | -DENABLE_DBUS=Off -DENABLE_SERVER=Off -DENABLE_EMOJI=Off -DUSE_SYSTEMD=Off 59 | - uses: actions/checkout@v4 60 | with: 61 | path: fcitx5-rime 62 | - name: Init CodeQL 63 | uses: github/codeql-action/init@v3 64 | with: 65 | languages: cpp 66 | source-root: fcitx5-rime 67 | - name: Build and Install fcitx5-rime 68 | uses: fcitx/github-actions@cmake 69 | with: 70 | path: fcitx5-rime 71 | - name: Test 72 | run: | 73 | ctest --test-dir fcitx5-rime/build 74 | - name: CodeQL Analysis 75 | uses: github/codeql-action/analyze@v2 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | build/* 3 | *.kdev4 4 | .kdev_include_paths 5 | .directory 6 | *.kate-swp 7 | *.orig 8 | tags 9 | astyle.sh 10 | .cache 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6.0) 2 | 3 | project(fcitx5-rime VERSION 5.1.10) 4 | 5 | set(REQUIRED_FCITX_VERSION 5.1.12) 6 | 7 | find_package(ECM 1.0.0 REQUIRED) 8 | set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) 9 | include(FeatureSummary) 10 | include(GNUInstallDirs) 11 | include(ECMSetupVersion) 12 | include(ECMUninstallTarget) 13 | 14 | find_package(Gettext REQUIRED) 15 | find_package(Fcitx5Core ${REQUIRED_FCITX_VERSION} REQUIRED) 16 | find_package(Fcitx5Module REQUIRED COMPONENTS Notifications) 17 | find_package(PkgConfig REQUIRED) 18 | find_package(Pthread REQUIRED) 19 | 20 | if (NOT DEFINED RIME_TARGET) 21 | pkg_check_modules(Rime REQUIRED IMPORTED_TARGET "rime>=1.7.0") 22 | set(RIME_TARGET PkgConfig::Rime) 23 | endif() 24 | 25 | if ("${Rime_VERSION}" VERSION_LESS "1.10.0") 26 | add_definitions(-DFCITX_RIME_NO_HIGHLIGHT_CANDIDATE) 27 | endif() 28 | if ("${Rime_VERSION}" VERSION_LESS "1.8.0") 29 | add_definitions(-DFCITX_RIME_NO_DELETE_CANDIDATE) 30 | endif() 31 | 32 | if(NOT DEFINED RIME_DATA_DIR) 33 | find_package(RimeData REQUIRED) 34 | endif(NOT DEFINED RIME_DATA_DIR) 35 | message(STATUS "Precompiler macro RIME_DATA_DIR is set to \"${RIME_DATA_DIR}\"") 36 | add_definitions(-DRIME_DATA_DIR="${RIME_DATA_DIR}") 37 | add_definitions(-DFCITX_GETTEXT_DOMAIN=\"fcitx5-rime\") 38 | add_definitions(-DFCITX_RIME_VERSION=\"${PROJECT_VERSION}\") 39 | fcitx5_add_i18n_definition() 40 | 41 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") 42 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 43 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 44 | 45 | include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake") 46 | 47 | add_subdirectory(po) 48 | add_subdirectory(src) 49 | add_subdirectory(data) 50 | 51 | fcitx5_translate_desktop_file(org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in 52 | org.fcitx.Fcitx5.Addon.Rime.metainfo.xml XML) 53 | 54 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.Addon.Rime.metainfo.xml" DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) 55 | 56 | feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) 57 | 58 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2013-07-15: CSSlayer 2 | * 0.2.2 3 | - Fix Issue 11 4 | - Fix Issue 12 5 | - Update icon artwork 6 | - Add License 7 | -------------------------------------------------------------------------------- /LICENSES/LGPL-2.1-or-later.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 7 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this license 10 | document, but changing it is not allowed. 11 | 12 | [This is the first released version of the Lesser GPL. It also counts as the 13 | successor of the GNU Library Public License, version 2, hence the version 14 | number 2.1.] 15 | 16 | Preamble 17 | 18 | The licenses for most software are designed to take away your freedom to share 19 | and change it. By contrast, the GNU General Public Licenses are intended to 20 | guarantee your freedom to share and change free software--to make sure the 21 | software is free for all its users. 22 | 23 | This license, the Lesser General Public License, applies to some specially 24 | designated software packages--typically libraries--of the Free Software Foundation 25 | and other authors who decide to use it. You can use it too, but we suggest 26 | you first think carefully about whether this license or the ordinary General 27 | Public License is the better strategy to use in any particular case, based 28 | on the explanations below. 29 | 30 | When we speak of free software, we are referring to freedom of use, not price. 31 | Our General Public Licenses are designed to make sure that you have the freedom 32 | to distribute copies of free software (and charge for this service if you 33 | wish); that you receive source code or can get it if you want it; that you 34 | can change the software and use pieces of it in new free programs; and that 35 | you are informed that you can do these things. 36 | 37 | To protect your rights, we need to make restrictions that forbid distributors 38 | to deny you these rights or to ask you to surrender these rights. These restrictions 39 | translate to certain responsibilities for you if you distribute copies of 40 | the library or if you modify it. 41 | 42 | For example, if you distribute copies of the library, whether gratis or for 43 | a fee, you must give the recipients all the rights that we gave you. You must 44 | make sure that they, too, receive or can get the source code. If you link 45 | other code with the library, you must provide complete object files to the 46 | recipients, so that they can relink them with the library after making changes 47 | to the library and recompiling it. And you must show them these terms so they 48 | know their rights. 49 | 50 | We protect your rights with a two-step method: (1) we copyright the library, 51 | and (2) we offer you this license, which gives you legal permission to copy, 52 | distribute and/or modify the library. 53 | 54 | To protect each distributor, we want to make it very clear that there is no 55 | warranty for the free library. Also, if the library is modified by someone 56 | else and passed on, the recipients should know that what they have is not 57 | the original version, so that the original author's reputation will not be 58 | affected by problems that might be introduced by others. 59 | 60 | Finally, software patents pose a constant threat to the existence of any free 61 | program. We wish to make sure that a company cannot effectively restrict the 62 | users of a free program by obtaining a restrictive license from a patent holder. 63 | Therefore, we insist that any patent license obtained for a version of the 64 | library must be consistent with the full freedom of use specified in this 65 | license. 66 | 67 | Most GNU software, including some libraries, is covered by the ordinary GNU 68 | General Public License. This license, the GNU Lesser General Public License, 69 | applies to certain designated libraries, and is quite different from the ordinary 70 | General Public License. We use this license for certain libraries in order 71 | to permit linking those libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using a shared 74 | library, the combination of the two is legally speaking a combined work, a 75 | derivative of the original library. The ordinary General Public License therefore 76 | permits such linking only if the entire combination fits its criteria of freedom. 77 | The Lesser General Public License permits more lax criteria for linking other 78 | code with the library. 79 | 80 | We call this license the "Lesser" General Public License because it does Less 81 | to protect the user's freedom than the ordinary General Public License. It 82 | also provides other free software developers Less of an advantage over competing 83 | non-free programs. These disadvantages are the reason we use the ordinary 84 | General Public License for many libraries. However, the Lesser license provides 85 | advantages in certain special circumstances. 86 | 87 | For example, on rare occasions, there may be a special need to encourage the 88 | widest possible use of a certain library, so that it becomes a de-facto standard. 89 | To achieve this, non-free programs must be allowed to use the library. A more 90 | frequent case is that a free library does the same job as widely used non-free 91 | libraries. In this case, there is little to gain by limiting the free library 92 | to free software only, so we use the Lesser General Public License. 93 | 94 | In other cases, permission to use a particular library in non-free programs 95 | enables a greater number of people to use a large body of free software. For 96 | example, permission to use the GNU C Library in non-free programs enables 97 | many more people to use the whole GNU operating system, as well as its variant, 98 | the GNU/Linux operating system. 99 | 100 | Although the Lesser General Public License is Less protective of the users' 101 | freedom, it does ensure that the user of a program that is linked with the 102 | Library has the freedom and the wherewithal to run that program using a modified 103 | version of the Library. 104 | 105 | The precise terms and conditions for copying, distribution and modification 106 | follow. Pay close attention to the difference between a "work based on the 107 | library" and a "work that uses the library". The former contains code derived 108 | from the library, whereas the latter must be combined with the library in 109 | order to run. 110 | 111 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 112 | 113 | 0. This License Agreement applies to any software library or other program 114 | which contains a notice placed by the copyright holder or other authorized 115 | party saying it may be distributed under the terms of this Lesser General 116 | Public License (also called "this License"). Each licensee is addressed as 117 | "you". 118 | 119 | A "library" means a collection of software functions and/or data prepared 120 | so as to be conveniently linked with application programs (which use some 121 | of those functions and data) to form executables. 122 | 123 | The "Library", below, refers to any such software library or work which has 124 | been distributed under these terms. A "work based on the Library" means either 125 | the Library or any derivative work under copyright law: that is to say, a 126 | work containing the Library or a portion of it, either verbatim or with modifications 127 | and/or translated straightforwardly into another language. (Hereinafter, translation 128 | is included without limitation in the term "modification".) 129 | 130 | "Source code" for a work means the preferred form of the work for making modifications 131 | to it. For a library, complete source code means all the source code for all 132 | modules it contains, plus any associated interface definition files, plus 133 | the scripts used to control compilation and installation of the library. 134 | 135 | Activities other than copying, distribution and modification are not covered 136 | by this License; they are outside its scope. The act of running a program 137 | using the Library is not restricted, and output from such a program is covered 138 | only if its contents constitute a work based on the Library (independent of 139 | the use of the Library in a tool for writing it). Whether that is true depends 140 | on what the Library does and what the program that uses the Library does. 141 | 142 | 1. You may copy and distribute verbatim copies of the Library's complete source 143 | code as you receive it, in any medium, provided that you conspicuously and 144 | appropriately publish on each copy an appropriate copyright notice and disclaimer 145 | of warranty; keep intact all the notices that refer to this License and to 146 | the absence of any warranty; and distribute a copy of this License along with 147 | the Library. 148 | 149 | You may charge a fee for the physical act of transferring a copy, and you 150 | may at your option offer warranty protection in exchange for a fee. 151 | 152 | 2. You may modify your copy or copies of the Library or any portion of it, 153 | thus forming a work based on the Library, and copy and distribute such modifications 154 | or work under the terms of Section 1 above, provided that you also meet all 155 | of these conditions: 156 | 157 | a) The modified work must itself be a software library. 158 | 159 | b) You must cause the files modified to carry prominent notices stating that 160 | you changed the files and the date of any change. 161 | 162 | c) You must cause the whole of the work to be licensed at no charge to all 163 | third parties under the terms of this License. 164 | 165 | d) If a facility in the modified Library refers to a function or a table of 166 | data to be supplied by an application program that uses the facility, other 167 | than as an argument passed when the facility is invoked, then you must make 168 | a good faith effort to ensure that, in the event an application does not supply 169 | such function or table, the facility still operates, and performs whatever 170 | part of its purpose remains meaningful. 171 | 172 | (For example, a function in a library to compute square roots has a purpose 173 | that is entirely well-defined independent of the application. Therefore, Subsection 174 | 2d requires that any application-supplied function or table used by this function 175 | must be optional: if the application does not supply it, the square root function 176 | must still compute square roots.) 177 | 178 | These requirements apply to the modified work as a whole. If identifiable 179 | sections of that work are not derived from the Library, and can be reasonably 180 | considered independent and separate works in themselves, then this License, 181 | and its terms, do not apply to those sections when you distribute them as 182 | separate works. But when you distribute the same sections as part of a whole 183 | which is a work based on the Library, the distribution of the whole must be 184 | on the terms of this License, whose permissions for other licensees extend 185 | to the entire whole, and thus to each and every part regardless of who wrote 186 | it. 187 | 188 | Thus, it is not the intent of this section to claim rights or contest your 189 | rights to work written entirely by you; rather, the intent is to exercise 190 | the right to control the distribution of derivative or collective works based 191 | on the Library. 192 | 193 | In addition, mere aggregation of another work not based on the Library with 194 | the Library (or with a work based on the Library) on a volume of a storage 195 | or distribution medium does not bring the other work under the scope of this 196 | License. 197 | 198 | 3. You may opt to apply the terms of the ordinary GNU General Public License 199 | instead of this License to a given copy of the Library. To do this, you must 200 | alter all the notices that refer to this License, so that they refer to the 201 | ordinary GNU General Public License, version 2, instead of to this License. 202 | (If a newer version than version 2 of the ordinary GNU General Public License 203 | has appeared, then you can specify that version instead if you wish.) Do not 204 | make any other change in these notices. 205 | 206 | Once this change is made in a given copy, it is irreversible for that copy, 207 | so the ordinary GNU General Public License applies to all subsequent copies 208 | and derivative works made from that copy. 209 | 210 | This option is useful when you wish to copy part of the code of the Library 211 | into a program that is not a library. 212 | 213 | 4. You may copy and distribute the Library (or a portion or derivative of 214 | it, under Section 2) in object code or executable form under the terms of 215 | Sections 1 and 2 above provided that you accompany it with the complete corresponding 216 | machine-readable source code, which must be distributed under the terms of 217 | Sections 1 and 2 above on a medium customarily used for software interchange. 218 | 219 | If distribution of object code is made by offering access to copy from a designated 220 | place, then offering equivalent access to copy the source code from the same 221 | place satisfies the requirement to distribute the source code, even though 222 | third parties are not compelled to copy the source along with the object code. 223 | 224 | 5. A program that contains no derivative of any portion of the Library, but 225 | is designed to work with the Library by being compiled or linked with it, 226 | is called a "work that uses the Library". Such a work, in isolation, is not 227 | a derivative work of the Library, and therefore falls outside the scope of 228 | this License. 229 | 230 | However, linking a "work that uses the Library" with the Library creates an 231 | executable that is a derivative of the Library (because it contains portions 232 | of the Library), rather than a "work that uses the library". The executable 233 | is therefore covered by this License. Section 6 states terms for distribution 234 | of such executables. 235 | 236 | When a "work that uses the Library" uses material from a header file that 237 | is part of the Library, the object code for the work may be a derivative work 238 | of the Library even though the source code is not. Whether this is true is 239 | especially significant if the work can be linked without the Library, or if 240 | the work is itself a library. The threshold for this to be true is not precisely 241 | defined by law. 242 | 243 | If such an object file uses only numerical parameters, data structure layouts 244 | and accessors, and small macros and small inline functions (ten lines or less 245 | in length), then the use of the object file is unrestricted, regardless of 246 | whether it is legally a derivative work. (Executables containing this object 247 | code plus portions of the Library will still fall under Section 6.) 248 | 249 | Otherwise, if the work is a derivative of the Library, you may distribute 250 | the object code for the work under the terms of Section 6. Any executables 251 | containing that work also fall under Section 6, whether or not they are linked 252 | directly with the Library itself. 253 | 254 | 6. As an exception to the Sections above, you may also combine or link a "work 255 | that uses the Library" with the Library to produce a work containing portions 256 | of the Library, and distribute that work under terms of your choice, provided 257 | that the terms permit modification of the work for the customer's own use 258 | and reverse engineering for debugging such modifications. 259 | 260 | You must give prominent notice with each copy of the work that the Library 261 | is used in it and that the Library and its use are covered by this License. 262 | You must supply a copy of this License. If the work during execution displays 263 | copyright notices, you must include the copyright notice for the Library among 264 | them, as well as a reference directing the user to the copy of this License. 265 | Also, you must do one of these things: 266 | 267 | a) Accompany the work with the complete corresponding machine-readable source 268 | code for the Library including whatever changes were used in the work (which 269 | must be distributed under Sections 1 and 2 above); and, if the work is an 270 | executable linked with the Library, with the complete machine-readable "work 271 | that uses the Library", as object code and/or source code, so that the user 272 | can modify the Library and then relink to produce a modified executable containing 273 | the modified Library. (It is understood that the user who changes the contents 274 | of definitions files in the Library will not necessarily be able to recompile 275 | the application to use the modified definitions.) 276 | 277 | b) Use a suitable shared library mechanism for linking with the Library. A 278 | suitable mechanism is one that (1) uses at run time a copy of the library 279 | already present on the user's computer system, rather than copying library 280 | functions into the executable, and (2) will operate properly with a modified 281 | version of the library, if the user installs one, as long as the modified 282 | version is interface-compatible with the version that the work was made with. 283 | 284 | c) Accompany the work with a written offer, valid for at least three years, 285 | to give the same user the materials specified in Subsection 6a, above, for 286 | a charge no more than the cost of performing this distribution. 287 | 288 | d) If distribution of the work is made by offering access to copy from a designated 289 | place, offer equivalent access to copy the above specified materials from 290 | the same place. 291 | 292 | e) Verify that the user has already received a copy of these materials or 293 | that you have already sent this user a copy. 294 | 295 | For an executable, the required form of the "work that uses the Library" must 296 | include any data and utility programs needed for reproducing the executable 297 | from it. However, as a special exception, the materials to be distributed 298 | need not include anything that is normally distributed (in either source or 299 | binary form) with the major components (compiler, kernel, and so on) of the 300 | operating system on which the executable runs, unless that component itself 301 | accompanies the executable. 302 | 303 | It may happen that this requirement contradicts the license restrictions of 304 | other proprietary libraries that do not normally accompany the operating system. 305 | Such a contradiction means you cannot use both them and the Library together 306 | in an executable that you distribute. 307 | 308 | 7. You may place library facilities that are a work based on the Library side-by-side 309 | in a single library together with other library facilities not covered by 310 | this License, and distribute such a combined library, provided that the separate 311 | distribution of the work based on the Library and of the other library facilities 312 | is otherwise permitted, and provided that you do these two things: 313 | 314 | a) Accompany the combined library with a copy of the same work based on the 315 | Library, uncombined with any other library facilities. This must be distributed 316 | under the terms of the Sections above. 317 | 318 | b) Give prominent notice with the combined library of the fact that part of 319 | it is a work based on the Library, and explaining where to find the accompanying 320 | uncombined form of the same work. 321 | 322 | 8. You may not copy, modify, sublicense, link with, or distribute the Library 323 | except as expressly provided under this License. Any attempt otherwise to 324 | copy, modify, sublicense, link with, or distribute the Library is void, and 325 | will automatically terminate your rights under this License. However, parties 326 | who have received copies, or rights, from you under this License will not 327 | have their licenses terminated so long as such parties remain in full compliance. 328 | 329 | 9. You are not required to accept this License, since you have not signed 330 | it. However, nothing else grants you permission to modify or distribute the 331 | Library or its derivative works. These actions are prohibited by law if you 332 | do not accept this License. Therefore, by modifying or distributing the Library 333 | (or any work based on the Library), you indicate your acceptance of this License 334 | to do so, and all its terms and conditions for copying, distributing or modifying 335 | the Library or works based on it. 336 | 337 | 10. Each time you redistribute the Library (or any work based on the Library), 338 | the recipient automatically receives a license from the original licensor 339 | to copy, distribute, link with or modify the Library subject to these terms 340 | and conditions. You may not impose any further restrictions on the recipients' 341 | exercise of the rights granted herein. You are not responsible for enforcing 342 | compliance by third parties with this License. 343 | 344 | 11. If, as a consequence of a court judgment or allegation of patent infringement 345 | or for any other reason (not limited to patent issues), conditions are imposed 346 | on you (whether by court order, agreement or otherwise) that contradict the 347 | conditions of this License, they do not excuse you from the conditions of 348 | this License. If you cannot distribute so as to satisfy simultaneously your 349 | obligations under this License and any other pertinent obligations, then as 350 | a consequence you may not distribute the Library at all. For example, if a 351 | patent license would not permit royalty-free redistribution of the Library 352 | by all those who receive copies directly or indirectly through you, then the 353 | only way you could satisfy both it and this License would be to refrain entirely 354 | from distribution of the Library. 355 | 356 | If any portion of this section is held invalid or unenforceable under any 357 | particular circumstance, the balance of the section is intended to apply, 358 | and the section as a whole is intended to apply in other circumstances. 359 | 360 | It is not the purpose of this section to induce you to infringe any patents 361 | or other property right claims or to contest validity of any such claims; 362 | this section has the sole purpose of protecting the integrity of the free 363 | software distribution system which is implemented by public license practices. 364 | Many people have made generous contributions to the wide range of software 365 | distributed through that system in reliance on consistent application of that 366 | system; it is up to the author/donor to decide if he or she is willing to 367 | distribute software through any other system and a licensee cannot impose 368 | that choice. 369 | 370 | This section is intended to make thoroughly clear what is believed to be a 371 | consequence of the rest of this License. 372 | 373 | 12. If the distribution and/or use of the Library is restricted in certain 374 | countries either by patents or by copyrighted interfaces, the original copyright 375 | holder who places the Library under this License may add an explicit geographical 376 | distribution limitation excluding those countries, so that distribution is 377 | permitted only in or among countries not thus excluded. In such case, this 378 | License incorporates the limitation as if written in the body of this License. 379 | 380 | 13. The Free Software Foundation may publish revised and/or new versions of 381 | the Lesser General Public License from time to time. Such new versions will 382 | be similar in spirit to the present version, but may differ in detail to address 383 | new problems or concerns. 384 | 385 | Each version is given a distinguishing version number. If the Library specifies 386 | a version number of this License which applies to it and "any later version", 387 | you have the option of following the terms and conditions either of that version 388 | or of any later version published by the Free Software Foundation. If the 389 | Library does not specify a license version number, you may choose any version 390 | ever published by the Free Software Foundation. 391 | 392 | 14. If you wish to incorporate parts of the Library into other free programs 393 | whose distribution conditions are incompatible with these, write to the author 394 | to ask for permission. For software which is copyrighted by the Free Software 395 | Foundation, write to the Free Software Foundation; we sometimes make exceptions 396 | for this. Our decision will be guided by the two goals of preserving the free 397 | status of all derivatives of our free software and of promoting the sharing 398 | and reuse of software generally. 399 | 400 | NO WARRANTY 401 | 402 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 403 | THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 404 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY 405 | "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 406 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 407 | FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 408 | OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 409 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 410 | 411 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 412 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 413 | THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 414 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE 415 | OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA 416 | OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES 417 | OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH 418 | HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 419 | END OF TERMS AND CONDITIONS 420 | 421 | How to Apply These Terms to Your New Libraries 422 | 423 | If you develop a new library, and you want it to be of the greatest possible 424 | use to the public, we recommend making it free software that everyone can 425 | redistribute and change. You can do so by permitting redistribution under 426 | these terms (or, alternatively, under the terms of the ordinary General Public 427 | License). 428 | 429 | To apply these terms, attach the following notices to the library. It is safest 430 | to attach them to the start of each source file to most effectively convey 431 | the exclusion of warranty; and each file should have at least the "copyright" 432 | line and a pointer to where the full notice is found. 433 | 434 | 435 | 436 | Copyright (C) 437 | 438 | This library is free software; you can redistribute it and/or modify it under 439 | the terms of the GNU Lesser General Public License as published by the Free 440 | Software Foundation; either version 2.1 of the License, or (at your option) 441 | any later version. 442 | 443 | This library is distributed in the hope that it will be useful, but WITHOUT 444 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 445 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 446 | details. 447 | 448 | You should have received a copy of the GNU Lesser General Public License along 449 | with this library; if not, write to the Free Software Foundation, Inc., 51 450 | Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 451 | 452 | Also add information on how to contact you by electronic and paper mail. 453 | 454 | You should also get your employer (if you work as a programmer) or your school, 455 | if any, to sign a "copyright disclaimer" for the library, if necessary. Here 456 | is a sample; alter the names: 457 | 458 | Yoyodyne, Inc., hereby disclaims all copyright interest in 459 | 460 | the library `Frob' (a library for tweaking knobs) written 461 | 462 | by James Random Hacker. 463 | 464 | < signature of Ty Coon > , 1 April 1990 465 | 466 | Ty Coon, President of Vice 467 | 468 | That's all there is to it! 469 | -------------------------------------------------------------------------------- /Messages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | gen_pot cxx:appdata:ui:desktop fcitx5-rime po . 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## RIME support for Fcitx 2 | 3 | RIME(中州韻輸入法引擎) is _mainly_ a Traditional Chinese input method engine. 4 | 5 | [![Jenkins Build Status](https://img.shields.io/jenkins/s/https/jenkins.fcitx-im.org/job/fcitx5-rime.svg)](https://jenkins.fcitx-im.org/job/fcitx5-rime/) 6 | 7 | [![Coverity Scan Status](https://img.shields.io/coverity/scan/13835.svg)](https://scan.coverity.com/projects/fcitx-fcitx5-rime) 8 | -------------------------------------------------------------------------------- /cmake/FindPthread.cmake: -------------------------------------------------------------------------------- 1 | # Try to find Pthread functionality 2 | # Once done this will define 3 | # 4 | # PTHREAD_FOUND - system has Pthread 5 | # PTHREAD_INCLUDE_DIR - Pthread include directory 6 | # PTHREAD_LIBRARIES - Libraries needed to use Pthread 7 | # 8 | # TODO: This will enable translations only if Gettext functionality is 9 | # present in libc. Must have more robust system for release, where Gettext 10 | # functionality can also reside in standalone Gettext library, or the one 11 | # embedded within kdelibs (cf. gettext.m4 from Gettext source). 12 | # 13 | # Copyright (c) 2006, Chusslove Illich, 14 | # Copyright (c) 2007, Alexander Neundorf, 15 | # Copyright (c) 2016, Xuetian Weng 16 | # 17 | # Redistribution and use is allowed according to the terms of the BSD license. 18 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 19 | 20 | find_path(PTHREAD_INCLUDE_DIR NAMES pthread.h) 21 | 22 | if(PTHREAD_INCLUDE_DIR) 23 | include(CheckFunctionExists) 24 | check_function_exists(pthread_create PTHREAD_LIBC_HAS_PTHREAD_CREATE) 25 | 26 | if (PTHREAD_LIBC_HAS_PTHREAD_CREATE) 27 | set(PTHREAD_LIBRARIES) 28 | set(PTHREAD_LIB_FOUND TRUE) 29 | else (PTHREAD_LIBC_HAS_PTHREAD_CREATE) 30 | find_library(PTHREAD_LIBRARIES NAMES pthread libpthread ) 31 | if(PTHREAD_LIBRARIES) 32 | set(PTHREAD_LIB_FOUND TRUE) 33 | endif(PTHREAD_LIBRARIES) 34 | endif (PTHREAD_LIBC_HAS_PTHREAD_CREATE) 35 | 36 | endif(PTHREAD_INCLUDE_DIR) 37 | 38 | include(FindPackageHandleStandardArgs) 39 | find_package_handle_standard_args(Pthread 40 | FOUND_VAR 41 | PTHREAD_FOUND 42 | REQUIRED_VARS 43 | PTHREAD_INCLUDE_DIR PTHREAD_LIB_FOUND 44 | ) 45 | 46 | if(PTHREAD_FOUND AND NOT TARGET Pthread::Pthread) 47 | if (PTHREAD_LIBRARIES) 48 | add_library(Pthread::Pthread UNKNOWN IMPORTED) 49 | set_target_properties(Pthread::Pthread PROPERTIES 50 | IMPORTED_LOCATION "${PTHREAD_LIBRARIES}") 51 | else() 52 | add_library(Pthread::Pthread INTERFACE IMPORTED ) 53 | endif() 54 | set_target_properties(Pthread::Pthread PROPERTIES 55 | INTERFACE_INCLUDE_DIRECTORIES "${PTHREAD_INCLUDE_DIR}" 56 | ) 57 | endif() 58 | 59 | mark_as_advanced(PTHREAD_INCLUDE_DIR PTHREAD_LIBRARIES PTHREAD_LIBC_HAS_PTHREAD_CREATE PTHREAD_LIB_FOUND) 60 | -------------------------------------------------------------------------------- /cmake/FindRimeData.cmake: -------------------------------------------------------------------------------- 1 | # Author: Marguerite Su 2 | # License: GPL 3 | # Description: find Rime schema collection package. 4 | # RIME_DATA_FOUND - System has rime-data package 5 | # RIME_DATA_DIR - rime-data absolute path 6 | 7 | set(RIME_DATA_FIND_DIR "${CMAKE_INSTALL_PREFIX}/share/rime-data" 8 | "${CMAKE_INSTALL_PREFIX}/share/rime/data" 9 | "/usr/share/rime-data" 10 | "/usr/share/rime/data") 11 | 12 | set(RIME_DATA_FOUND FALSE) 13 | 14 | foreach(_RIME_DATA_DIR ${RIME_DATA_FIND_DIR}) 15 | if (IS_DIRECTORY ${_RIME_DATA_DIR}) 16 | set(RIME_DATA_FOUND True) 17 | set(RIME_DATA_DIR ${_RIME_DATA_DIR}) 18 | endif (IS_DIRECTORY ${_RIME_DATA_DIR}) 19 | endforeach(_RIME_DATA_DIR) 20 | 21 | include(FindPackageHandleStandardArgs) 22 | find_package_handle_standard_args(RimeData DEFAULT_MSG RIME_DATA_DIR) 23 | mark_as_advanced(RIME_DATA_DIR) 24 | -------------------------------------------------------------------------------- /cmake/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 3 | endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | 5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach (file ${files}) 8 | message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 9 | if (EXISTS "$ENV{DESTDIR}${file}" OR IS_SYMLINK "$ENV{DESTDIR}${file}") 10 | execute_process( 11 | COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" 12 | OUTPUT_VARIABLE rm_out 13 | RESULT_VARIABLE rm_retval 14 | ) 15 | if(NOT ${rm_retval} EQUAL 0) 16 | message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 17 | endif (NOT ${rm_retval} EQUAL 0) 18 | else (EXISTS "$ENV{DESTDIR}${file}" OR IS_SYMLINK "$ENV{DESTDIR}${file}") 19 | message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 20 | endif (EXISTS "$ENV{DESTDIR}${file}" OR IS_SYMLINK "$ENV{DESTDIR}${file}") 21 | endforeach(file) 22 | -------------------------------------------------------------------------------- /data/48x48/apps/fcitx-rime.png: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx-rime.png -------------------------------------------------------------------------------- /data/48x48/apps/fcitx_rime_deploy.png: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_deploy.png -------------------------------------------------------------------------------- /data/48x48/apps/fcitx_rime_disable.png: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_disable.png -------------------------------------------------------------------------------- /data/48x48/apps/fcitx_rime_im.png: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_im.png -------------------------------------------------------------------------------- /data/48x48/apps/fcitx_rime_latin.png: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_latin.png -------------------------------------------------------------------------------- /data/48x48/apps/fcitx_rime_sync.png: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_sync.png -------------------------------------------------------------------------------- /data/48x48/apps/org.fcitx.Fcitx5.fcitx-rime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcitx/fcitx5-rime/d58991193aca771305435954de4614117eab9009/data/48x48/apps/org.fcitx.Fcitx5.fcitx-rime.png -------------------------------------------------------------------------------- /data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcitx/fcitx5-rime/d58991193aca771305435954de4614117eab9009/data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_deploy.png -------------------------------------------------------------------------------- /data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcitx/fcitx5-rime/d58991193aca771305435954de4614117eab9009/data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_disable.png -------------------------------------------------------------------------------- /data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_im.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcitx/fcitx5-rime/d58991193aca771305435954de4614117eab9009/data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_im.png -------------------------------------------------------------------------------- /data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_latin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcitx/fcitx5-rime/d58991193aca771305435954de4614117eab9009/data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_latin.png -------------------------------------------------------------------------------- /data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fcitx/fcitx5-rime/d58991193aca771305435954de4614117eab9009/data/48x48/apps/org.fcitx.Fcitx5.fcitx_rime_sync.png -------------------------------------------------------------------------------- /data/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install(DIRECTORY 48x48 scalable DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor" 2 | PATTERN .* EXCLUDE 3 | PATTERN *~ EXCLUDE) 4 | 5 | install(FILES fcitx5.yaml DESTINATION "${RIME_DATA_DIR}") 6 | -------------------------------------------------------------------------------- /data/fcitx5.yaml: -------------------------------------------------------------------------------- 1 | # Fcitx5 settings 2 | # encoding: utf-8 3 | 4 | config_version: "0.22" 5 | 6 | # set app options based on app name 7 | # e.g. 8 | # "app_options/kwrite": 9 | # - ascii_mode: true 10 | # You may obtain the app name via 11 | # dbus-send --print-reply=literal --dest=org.fcitx.Fcitx5 /controller org.fcitx.Fcitx.Controller1.DebugInfo 12 | app_options: {} 13 | -------------------------------------------------------------------------------- /data/scalable/apps/fcitx-rime.svg: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx-rime.svg -------------------------------------------------------------------------------- /data/scalable/apps/fcitx_rime_deploy.svg: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_deploy.svg -------------------------------------------------------------------------------- /data/scalable/apps/fcitx_rime_disable.svg: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_disable.svg -------------------------------------------------------------------------------- /data/scalable/apps/fcitx_rime_im.svg: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_im.svg -------------------------------------------------------------------------------- /data/scalable/apps/fcitx_rime_latin.svg: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_latin.svg -------------------------------------------------------------------------------- /data/scalable/apps/fcitx_rime_sync.svg: -------------------------------------------------------------------------------- 1 | org.fcitx.Fcitx5.fcitx_rime_sync.svg -------------------------------------------------------------------------------- /data/scalable/apps/org.fcitx.Fcitx5.fcitx-rime.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 42 | 44 | 45 | 47 | image/svg+xml 48 | 50 | 51 | 52 | 53 | 54 | 59 | 64 | 67 | 74 | 80 | 86 | 92 | 98 | 104 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /data/scalable/apps/org.fcitx.Fcitx5.fcitx_rime_deploy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 58 | 61 | 64 | 74 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /data/scalable/apps/org.fcitx.Fcitx5.fcitx_rime_disable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 64 | 67 | 74 | 80 | 85 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /data/scalable/apps/org.fcitx.Fcitx5.fcitx_rime_im.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 64 | 71 | 74 | 81 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /data/scalable/apps/org.fcitx.Fcitx5.fcitx_rime_latin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 41 | 43 | 44 | 46 | image/svg+xml 47 | 49 | 50 | 51 | 52 | 53 | 58 | 60 | 67 | A 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /data/scalable/apps/org.fcitx.Fcitx5.fcitx_rime_sync.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 45 | 47 | 48 | 50 | image/svg+xml 51 | 53 | 54 | 55 | 56 | 57 | 62 | 65 | 70 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.fcitx.Fcitx5.Addon.Rime 4 | org.fcitx.Fcitx5 5 | CC0-1.0 6 | LGPL-2.1+ 7 | Rime for Fcitx 5 8 | Rime input method 9 | 10 | The Fcitx Team 11 | 12 | https://fcitx-im.org 13 | https://github.com/fcitx/fcitx5-rime/issues 14 | https://github.com/fcitx/fcitx5-rime 15 | Fcitx 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /po/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | fcitx5_install_translation(fcitx5-rime) 2 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | 2 | ca 3 | da 4 | de 5 | he 6 | ja 7 | ko 8 | ru 9 | tr 10 | vi 11 | zh_CN 12 | zh_TW 13 | -------------------------------------------------------------------------------- /po/ca.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # Robert Antoni Buj i Gelonch , 2017 8 | # 9 | msgid "" 10 | msgstr "" 11 | "Project-Id-Version: fcitx5-rime\n" 12 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 13 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 14 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 15 | "Last-Translator: Robert Antoni Buj i Gelonch , 2017\n" 16 | "Language-Team: Catalan (https://app.transifex.com/fcitx/teams/12005/ca/)\n" 17 | "Language: ca\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 22 | 23 | #: src/rimeengine.h:97 24 | msgid "Action when switching input method" 25 | msgstr "" 26 | 27 | #: src/rimeengine.h:58 28 | msgid "All" 29 | msgstr "" 30 | 31 | #: src/rimeengine.h:73 32 | msgid "Clear" 33 | msgstr "" 34 | 35 | #: src/rimeengine.h:76 36 | msgid "Commit commit preview" 37 | msgstr "" 38 | 39 | #: src/rimeengine.h:75 40 | msgid "Commit composing text" 41 | msgstr "" 42 | 43 | #: src/rimeengine.h:64 44 | msgid "Commit preview" 45 | msgstr "" 46 | 47 | #: src/rimeengine.h:74 48 | msgid "Commit raw input" 49 | msgstr "" 50 | 51 | #: src/rimeengine.h:64 52 | msgid "Composing text" 53 | msgstr "" 54 | 55 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 56 | msgid "Deploy" 57 | msgstr "Desplega" 58 | 59 | #: src/rimeengine.h:63 60 | msgid "Do not show" 61 | msgstr "" 62 | 63 | #: src/rimeengine.h:91 64 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 65 | msgstr "" 66 | 67 | #: src/rimeengine.h:58 68 | msgid "Follow Global Configuration" 69 | msgstr "" 70 | 71 | #: src/rimecandidate.cpp:164 72 | msgid "Forget word" 73 | msgstr "" 74 | 75 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 76 | msgid "Latin Mode" 77 | msgstr "" 78 | 79 | #: src/rimeengine.h:59 80 | msgid "No" 81 | msgstr "" 82 | 83 | #: src/rimeengine.h:81 84 | msgid "Preedit Mode" 85 | msgstr "" 86 | 87 | #: src/rimeengine.h:59 88 | msgid "Program" 89 | msgstr "" 90 | 91 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 92 | msgid "Rime" 93 | msgstr "Rime" 94 | 95 | #: src/rime-addon.conf.in.in:3 96 | msgid "Rime Wrapper For Fcitx" 97 | msgstr "Contenidor rime per a fcitx" 98 | 99 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 100 | msgid "Rime for Fcitx 5" 101 | msgstr "" 102 | 103 | #: src/rimeengine.cpp:542 104 | msgid "Rime has encountered an error. See log for details." 105 | msgstr "" 106 | 107 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 108 | msgid "Rime input method" 109 | msgstr "" 110 | 111 | #: src/rimeengine.cpp:529 112 | msgid "Rime is ready." 113 | msgstr "Rime està preparat." 114 | 115 | #: src/rimeengine.cpp:526 116 | msgid "" 117 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 118 | "is finished..." 119 | msgstr "" 120 | 121 | #: src/rimeengine.h:84 122 | msgid "Shared Input State" 123 | msgstr "" 124 | 125 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 126 | msgid "Synchronize" 127 | msgstr "Sincronitza" 128 | 129 | #: src/rimeengine.h:100 130 | msgid "User data dir" 131 | msgstr "" 132 | -------------------------------------------------------------------------------- /po/da.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # scootergrisen, 2021 7 | # 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: fcitx5-rime\n" 11 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 12 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 13 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 14 | "Last-Translator: scootergrisen, 2021\n" 15 | "Language-Team: Danish (https://app.transifex.com/fcitx/teams/12005/da/)\n" 16 | "Language: da\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 21 | 22 | #: src/rimeengine.h:97 23 | msgid "Action when switching input method" 24 | msgstr "" 25 | 26 | #: src/rimeengine.h:58 27 | msgid "All" 28 | msgstr "" 29 | 30 | #: src/rimeengine.h:73 31 | msgid "Clear" 32 | msgstr "" 33 | 34 | #: src/rimeengine.h:76 35 | msgid "Commit commit preview" 36 | msgstr "" 37 | 38 | #: src/rimeengine.h:75 39 | msgid "Commit composing text" 40 | msgstr "" 41 | 42 | #: src/rimeengine.h:64 43 | msgid "Commit preview" 44 | msgstr "" 45 | 46 | #: src/rimeengine.h:74 47 | msgid "Commit raw input" 48 | msgstr "" 49 | 50 | #: src/rimeengine.h:64 51 | msgid "Composing text" 52 | msgstr "" 53 | 54 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 55 | msgid "Deploy" 56 | msgstr "Udsend" 57 | 58 | #: src/rimeengine.h:63 59 | msgid "Do not show" 60 | msgstr "" 61 | 62 | #: src/rimeengine.h:91 63 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 64 | msgstr "" 65 | 66 | #: src/rimeengine.h:58 67 | msgid "Follow Global Configuration" 68 | msgstr "" 69 | 70 | #: src/rimecandidate.cpp:164 71 | msgid "Forget word" 72 | msgstr "" 73 | 74 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 75 | msgid "Latin Mode" 76 | msgstr "Latinsk tilstand" 77 | 78 | #: src/rimeengine.h:59 79 | msgid "No" 80 | msgstr "" 81 | 82 | #: src/rimeengine.h:81 83 | msgid "Preedit Mode" 84 | msgstr "" 85 | 86 | #: src/rimeengine.h:59 87 | msgid "Program" 88 | msgstr "" 89 | 90 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 91 | msgid "Rime" 92 | msgstr "Rime" 93 | 94 | #: src/rime-addon.conf.in.in:3 95 | msgid "Rime Wrapper For Fcitx" 96 | msgstr "Rime-wrapper til Fcitx" 97 | 98 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 99 | msgid "Rime for Fcitx 5" 100 | msgstr "Rime til Fcitx 5" 101 | 102 | #: src/rimeengine.cpp:542 103 | msgid "Rime has encountered an error. See log for details." 104 | msgstr "" 105 | 106 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 107 | msgid "Rime input method" 108 | msgstr "Rime-inputmetode" 109 | 110 | #: src/rimeengine.cpp:529 111 | msgid "Rime is ready." 112 | msgstr "Rime er klar." 113 | 114 | #: src/rimeengine.cpp:526 115 | msgid "" 116 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 117 | "is finished..." 118 | msgstr "" 119 | "Rime er ved at blive vedligeholdt. Det kan tage nogle sekunder. Vent " 120 | "venligst til den er færdig ..." 121 | 122 | #: src/rimeengine.h:84 123 | msgid "Shared Input State" 124 | msgstr "" 125 | 126 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 127 | msgid "Synchronize" 128 | msgstr "Synkroniser" 129 | 130 | #: src/rimeengine.h:100 131 | msgid "User data dir" 132 | msgstr "" 133 | -------------------------------------------------------------------------------- /po/de.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # mar well , 2018 8 | # Ettore Atalan , 2022 9 | # 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: fcitx5-rime\n" 13 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 14 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 15 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 16 | "Last-Translator: Ettore Atalan , 2022\n" 17 | "Language-Team: German (https://app.transifex.com/fcitx/teams/12005/de/)\n" 18 | "Language: de\n" 19 | "MIME-Version: 1.0\n" 20 | "Content-Type: text/plain; charset=UTF-8\n" 21 | "Content-Transfer-Encoding: 8bit\n" 22 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 23 | 24 | #: src/rimeengine.h:97 25 | msgid "Action when switching input method" 26 | msgstr "" 27 | 28 | #: src/rimeengine.h:58 29 | msgid "All" 30 | msgstr "" 31 | 32 | #: src/rimeengine.h:73 33 | msgid "Clear" 34 | msgstr "" 35 | 36 | #: src/rimeengine.h:76 37 | msgid "Commit commit preview" 38 | msgstr "" 39 | 40 | #: src/rimeengine.h:75 41 | msgid "Commit composing text" 42 | msgstr "" 43 | 44 | #: src/rimeengine.h:64 45 | msgid "Commit preview" 46 | msgstr "" 47 | 48 | #: src/rimeengine.h:74 49 | msgid "Commit raw input" 50 | msgstr "" 51 | 52 | #: src/rimeengine.h:64 53 | msgid "Composing text" 54 | msgstr "" 55 | 56 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 57 | msgid "Deploy" 58 | msgstr "Einsetzen" 59 | 60 | #: src/rimeengine.h:63 61 | msgid "Do not show" 62 | msgstr "" 63 | 64 | #: src/rimeengine.h:91 65 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 66 | msgstr "" 67 | 68 | #: src/rimeengine.h:58 69 | msgid "Follow Global Configuration" 70 | msgstr "" 71 | 72 | #: src/rimecandidate.cpp:164 73 | msgid "Forget word" 74 | msgstr "" 75 | 76 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 77 | msgid "Latin Mode" 78 | msgstr "Lateinischer Modus" 79 | 80 | #: src/rimeengine.h:59 81 | msgid "No" 82 | msgstr "" 83 | 84 | #: src/rimeengine.h:81 85 | msgid "Preedit Mode" 86 | msgstr "" 87 | 88 | #: src/rimeengine.h:59 89 | msgid "Program" 90 | msgstr "" 91 | 92 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 93 | msgid "Rime" 94 | msgstr "Rime" 95 | 96 | #: src/rime-addon.conf.in.in:3 97 | msgid "Rime Wrapper For Fcitx" 98 | msgstr "Rime Wrapper für Fcitx" 99 | 100 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 101 | msgid "Rime for Fcitx 5" 102 | msgstr "" 103 | 104 | #: src/rimeengine.cpp:542 105 | msgid "Rime has encountered an error. See log for details." 106 | msgstr "" 107 | 108 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 109 | msgid "Rime input method" 110 | msgstr "" 111 | 112 | #: src/rimeengine.cpp:529 113 | msgid "Rime is ready." 114 | msgstr "Rime ist bereit." 115 | 116 | #: src/rimeengine.cpp:526 117 | msgid "" 118 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 119 | "is finished..." 120 | msgstr "" 121 | 122 | #: src/rimeengine.h:84 123 | msgid "Shared Input State" 124 | msgstr "" 125 | 126 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 127 | msgid "Synchronize" 128 | msgstr "Synchronisieren" 129 | 130 | #: src/rimeengine.h:100 131 | msgid "User data dir" 132 | msgstr "" 133 | -------------------------------------------------------------------------------- /po/fcitx5-rime.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: fcitx5-rime\n" 8 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 9 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 11 | "Last-Translator: FULL NAME \n" 12 | "Language-Team: LANGUAGE \n" 13 | "Language: LANG\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: src/rimeengine.h:97 19 | msgid "Action when switching input method" 20 | msgstr "" 21 | 22 | #: src/rimeengine.h:58 23 | msgid "All" 24 | msgstr "" 25 | 26 | #: src/rimeengine.h:73 27 | msgid "Clear" 28 | msgstr "" 29 | 30 | #: src/rimeengine.h:76 31 | msgid "Commit commit preview" 32 | msgstr "" 33 | 34 | #: src/rimeengine.h:75 35 | msgid "Commit composing text" 36 | msgstr "" 37 | 38 | #: src/rimeengine.h:64 39 | msgid "Commit preview" 40 | msgstr "" 41 | 42 | #: src/rimeengine.h:74 43 | msgid "Commit raw input" 44 | msgstr "" 45 | 46 | #: src/rimeengine.h:64 47 | msgid "Composing text" 48 | msgstr "" 49 | 50 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 51 | msgid "Deploy" 52 | msgstr "" 53 | 54 | #: src/rimeengine.h:63 55 | msgid "Do not show" 56 | msgstr "" 57 | 58 | #: src/rimeengine.h:91 59 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 60 | msgstr "" 61 | 62 | #: src/rimeengine.h:58 63 | msgid "Follow Global Configuration" 64 | msgstr "" 65 | 66 | #: src/rimecandidate.cpp:164 67 | msgid "Forget word" 68 | msgstr "" 69 | 70 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 71 | msgid "Latin Mode" 72 | msgstr "" 73 | 74 | #: src/rimeengine.h:59 75 | msgid "No" 76 | msgstr "" 77 | 78 | #: src/rimeengine.h:81 79 | msgid "Preedit Mode" 80 | msgstr "" 81 | 82 | #: src/rimeengine.h:59 83 | msgid "Program" 84 | msgstr "" 85 | 86 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 87 | msgid "Rime" 88 | msgstr "" 89 | 90 | #: src/rime-addon.conf.in.in:3 91 | msgid "Rime Wrapper For Fcitx" 92 | msgstr "" 93 | 94 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 95 | msgid "Rime for Fcitx 5" 96 | msgstr "" 97 | 98 | #: src/rimeengine.cpp:542 99 | msgid "Rime has encountered an error. See log for details." 100 | msgstr "" 101 | 102 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 103 | msgid "Rime input method" 104 | msgstr "" 105 | 106 | #: src/rimeengine.cpp:529 107 | msgid "Rime is ready." 108 | msgstr "" 109 | 110 | #: src/rimeengine.cpp:526 111 | msgid "" 112 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 113 | "is finished..." 114 | msgstr "" 115 | 116 | #: src/rimeengine.h:84 117 | msgid "Shared Input State" 118 | msgstr "" 119 | 120 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 121 | msgid "Synchronize" 122 | msgstr "" 123 | 124 | #: src/rimeengine.h:100 125 | msgid "User data dir" 126 | msgstr "" 127 | -------------------------------------------------------------------------------- /po/he.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # 63f334ffc0709ba0fc2361b80bf3c0f0_00ffd1e , 2020 7 | # 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: fcitx5-rime\n" 11 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 12 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 13 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 14 | "Last-Translator: 63f334ffc0709ba0fc2361b80bf3c0f0_00ffd1e " 15 | ", 2020\n" 16 | "Language-Team: Hebrew (https://app.transifex.com/fcitx/teams/12005/he/)\n" 17 | "Language: he\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " 22 | "1 == 0) ? 1: 2;\n" 23 | 24 | #: src/rimeengine.h:97 25 | msgid "Action when switching input method" 26 | msgstr "" 27 | 28 | #: src/rimeengine.h:58 29 | msgid "All" 30 | msgstr "" 31 | 32 | #: src/rimeengine.h:73 33 | msgid "Clear" 34 | msgstr "" 35 | 36 | #: src/rimeengine.h:76 37 | msgid "Commit commit preview" 38 | msgstr "" 39 | 40 | #: src/rimeengine.h:75 41 | msgid "Commit composing text" 42 | msgstr "" 43 | 44 | #: src/rimeengine.h:64 45 | msgid "Commit preview" 46 | msgstr "" 47 | 48 | #: src/rimeengine.h:74 49 | msgid "Commit raw input" 50 | msgstr "" 51 | 52 | #: src/rimeengine.h:64 53 | msgid "Composing text" 54 | msgstr "" 55 | 56 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 57 | msgid "Deploy" 58 | msgstr "" 59 | 60 | #: src/rimeengine.h:63 61 | msgid "Do not show" 62 | msgstr "" 63 | 64 | #: src/rimeengine.h:91 65 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 66 | msgstr "" 67 | 68 | #: src/rimeengine.h:58 69 | msgid "Follow Global Configuration" 70 | msgstr "" 71 | 72 | #: src/rimecandidate.cpp:164 73 | msgid "Forget word" 74 | msgstr "" 75 | 76 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 77 | msgid "Latin Mode" 78 | msgstr "" 79 | 80 | #: src/rimeengine.h:59 81 | msgid "No" 82 | msgstr "" 83 | 84 | #: src/rimeengine.h:81 85 | msgid "Preedit Mode" 86 | msgstr "" 87 | 88 | #: src/rimeengine.h:59 89 | msgid "Program" 90 | msgstr "" 91 | 92 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 93 | msgid "Rime" 94 | msgstr "" 95 | 96 | #: src/rime-addon.conf.in.in:3 97 | msgid "Rime Wrapper For Fcitx" 98 | msgstr "" 99 | 100 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 101 | msgid "Rime for Fcitx 5" 102 | msgstr "" 103 | 104 | #: src/rimeengine.cpp:542 105 | msgid "Rime has encountered an error. See log for details." 106 | msgstr "" 107 | 108 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 109 | msgid "Rime input method" 110 | msgstr "" 111 | 112 | #: src/rimeengine.cpp:529 113 | msgid "Rime is ready." 114 | msgstr "" 115 | 116 | #: src/rimeengine.cpp:526 117 | msgid "" 118 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 119 | "is finished..." 120 | msgstr "" 121 | 122 | #: src/rimeengine.h:84 123 | msgid "Shared Input State" 124 | msgstr "" 125 | 126 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 127 | msgid "Synchronize" 128 | msgstr "סנכרון" 129 | 130 | #: src/rimeengine.h:100 131 | msgid "User data dir" 132 | msgstr "" 133 | -------------------------------------------------------------------------------- /po/ja.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # Takuro Onoue , 2020 8 | # UTUMI Hirosi , 2021 9 | # Aaron Muir Hamilton , 2022 10 | # 11 | msgid "" 12 | msgstr "" 13 | "Project-Id-Version: fcitx5-rime\n" 14 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 15 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 16 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 17 | "Last-Translator: Aaron Muir Hamilton , 2022\n" 18 | "Language-Team: Japanese (https://app.transifex.com/fcitx/teams/12005/ja/)\n" 19 | "Language: ja\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Plural-Forms: nplurals=1; plural=0;\n" 24 | 25 | #: src/rimeengine.h:97 26 | msgid "Action when switching input method" 27 | msgstr "" 28 | 29 | #: src/rimeengine.h:58 30 | msgid "All" 31 | msgstr "" 32 | 33 | #: src/rimeengine.h:73 34 | msgid "Clear" 35 | msgstr "" 36 | 37 | #: src/rimeengine.h:76 38 | msgid "Commit commit preview" 39 | msgstr "" 40 | 41 | #: src/rimeengine.h:75 42 | msgid "Commit composing text" 43 | msgstr "" 44 | 45 | #: src/rimeengine.h:64 46 | msgid "Commit preview" 47 | msgstr "" 48 | 49 | #: src/rimeengine.h:74 50 | msgid "Commit raw input" 51 | msgstr "" 52 | 53 | #: src/rimeengine.h:64 54 | msgid "Composing text" 55 | msgstr "" 56 | 57 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 58 | msgid "Deploy" 59 | msgstr "デプロイ" 60 | 61 | #: src/rimeengine.h:63 62 | msgid "Do not show" 63 | msgstr "" 64 | 65 | #: src/rimeengine.h:91 66 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 67 | msgstr "プリエディットカーソルをプリエディットの先頭に固定する" 68 | 69 | #: src/rimeengine.h:58 70 | msgid "Follow Global Configuration" 71 | msgstr "" 72 | 73 | #: src/rimecandidate.cpp:164 74 | msgid "Forget word" 75 | msgstr "" 76 | 77 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 78 | msgid "Latin Mode" 79 | msgstr "ラテンモード" 80 | 81 | #: src/rimeengine.h:59 82 | msgid "No" 83 | msgstr "" 84 | 85 | #: src/rimeengine.h:81 86 | msgid "Preedit Mode" 87 | msgstr "" 88 | 89 | #: src/rimeengine.h:59 90 | msgid "Program" 91 | msgstr "" 92 | 93 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 94 | msgid "Rime" 95 | msgstr "Rime" 96 | 97 | #: src/rime-addon.conf.in.in:3 98 | msgid "Rime Wrapper For Fcitx" 99 | msgstr "Fcitx 用 Rime ラッパー" 100 | 101 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 102 | msgid "Rime for Fcitx 5" 103 | msgstr "Rime for Fcitx 5" 104 | 105 | #: src/rimeengine.cpp:542 106 | msgid "Rime has encountered an error. See log for details." 107 | msgstr "" 108 | 109 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 110 | msgid "Rime input method" 111 | msgstr "Rime 入力メソッド" 112 | 113 | #: src/rimeengine.cpp:529 114 | msgid "Rime is ready." 115 | msgstr "Rimeの準備ができました。" 116 | 117 | #: src/rimeengine.cpp:526 118 | msgid "" 119 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 120 | "is finished..." 121 | msgstr "" 122 | "Rime はメンテナンス中です。数秒かかる場合があります。終了するまでお待ちくださ" 123 | "い..." 124 | 125 | #: src/rimeengine.h:84 126 | msgid "Shared Input State" 127 | msgstr "" 128 | 129 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 130 | msgid "Synchronize" 131 | msgstr "同期" 132 | 133 | #: src/rimeengine.h:100 134 | msgid "User data dir" 135 | msgstr "ユーザーデータディレクトリ" 136 | -------------------------------------------------------------------------------- /po/ko.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # Junghee Lee , 2022 8 | # 9 | msgid "" 10 | msgstr "" 11 | "Project-Id-Version: fcitx5-rime\n" 12 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 13 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 14 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 15 | "Last-Translator: Junghee Lee , 2022\n" 16 | "Language-Team: Korean (https://app.transifex.com/fcitx/teams/12005/ko/)\n" 17 | "Language: ko\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Plural-Forms: nplurals=1; plural=0;\n" 22 | 23 | #: src/rimeengine.h:97 24 | msgid "Action when switching input method" 25 | msgstr "" 26 | 27 | #: src/rimeengine.h:58 28 | msgid "All" 29 | msgstr "" 30 | 31 | #: src/rimeengine.h:73 32 | msgid "Clear" 33 | msgstr "" 34 | 35 | #: src/rimeengine.h:76 36 | msgid "Commit commit preview" 37 | msgstr "" 38 | 39 | #: src/rimeengine.h:75 40 | msgid "Commit composing text" 41 | msgstr "" 42 | 43 | #: src/rimeengine.h:64 44 | msgid "Commit preview" 45 | msgstr "" 46 | 47 | #: src/rimeengine.h:74 48 | msgid "Commit raw input" 49 | msgstr "" 50 | 51 | #: src/rimeengine.h:64 52 | msgid "Composing text" 53 | msgstr "" 54 | 55 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 56 | msgid "Deploy" 57 | msgstr "배치" 58 | 59 | #: src/rimeengine.h:63 60 | msgid "Do not show" 61 | msgstr "" 62 | 63 | #: src/rimeengine.h:91 64 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 65 | msgstr "사전편집 시작 부분에 포함된 사전편집 커서 수정" 66 | 67 | #: src/rimeengine.h:58 68 | msgid "Follow Global Configuration" 69 | msgstr "" 70 | 71 | #: src/rimecandidate.cpp:164 72 | msgid "Forget word" 73 | msgstr "" 74 | 75 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 76 | msgid "Latin Mode" 77 | msgstr "로마자 모드" 78 | 79 | #: src/rimeengine.h:59 80 | msgid "No" 81 | msgstr "" 82 | 83 | #: src/rimeengine.h:81 84 | msgid "Preedit Mode" 85 | msgstr "" 86 | 87 | #: src/rimeengine.h:59 88 | msgid "Program" 89 | msgstr "" 90 | 91 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 92 | msgid "Rime" 93 | msgstr "Rime" 94 | 95 | #: src/rime-addon.conf.in.in:3 96 | msgid "Rime Wrapper For Fcitx" 97 | msgstr "Fcitx용 Rime 래퍼" 98 | 99 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 100 | msgid "Rime for Fcitx 5" 101 | msgstr "Fcitx5용 Rime" 102 | 103 | #: src/rimeengine.cpp:542 104 | msgid "Rime has encountered an error. See log for details." 105 | msgstr "" 106 | 107 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 108 | msgid "Rime input method" 109 | msgstr "Rime 입력 방식" 110 | 111 | #: src/rimeengine.cpp:529 112 | msgid "Rime is ready." 113 | msgstr "Rime이 준비되었습니다." 114 | 115 | #: src/rimeengine.cpp:526 116 | msgid "" 117 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 118 | "is finished..." 119 | msgstr "" 120 | "라임이 점검중입니다. 몇 초 정도 걸릴 수 있습니다. 완료될 때까지 기다려 주십시" 121 | "오..." 122 | 123 | #: src/rimeengine.h:84 124 | msgid "Shared Input State" 125 | msgstr "" 126 | 127 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 128 | msgid "Synchronize" 129 | msgstr "동기화" 130 | 131 | #: src/rimeengine.h:100 132 | msgid "User data dir" 133 | msgstr "사용자 데이터 디렉터리" 134 | -------------------------------------------------------------------------------- /po/ru.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # Dmitry , 2024 8 | # 9 | msgid "" 10 | msgstr "" 11 | "Project-Id-Version: fcitx5-rime\n" 12 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 13 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 14 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 15 | "Last-Translator: Dmitry , 2024\n" 16 | "Language-Team: Russian (https://app.transifex.com/fcitx/teams/12005/ru/)\n" 17 | "Language: ru\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " 22 | "n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " 23 | "(n%100>=11 && n%100<=14)? 2 : 3);\n" 24 | 25 | #: src/rimeengine.h:97 26 | msgid "Action when switching input method" 27 | msgstr "Действие при переключении метода ввода" 28 | 29 | #: src/rimeengine.h:58 30 | msgid "All" 31 | msgstr "Все" 32 | 33 | #: src/rimeengine.h:73 34 | msgid "Clear" 35 | msgstr "Очистить" 36 | 37 | #: src/rimeengine.h:76 38 | msgid "Commit commit preview" 39 | msgstr "Отправить превью" 40 | 41 | #: src/rimeengine.h:75 42 | msgid "Commit composing text" 43 | msgstr "Составить текст" 44 | 45 | #: src/rimeengine.h:64 46 | msgid "Commit preview" 47 | msgstr "Отправить превью" 48 | 49 | #: src/rimeengine.h:74 50 | msgid "Commit raw input" 51 | msgstr "Совершать необработанный ввод" 52 | 53 | #: src/rimeengine.h:64 54 | msgid "Composing text" 55 | msgstr "Составление текста" 56 | 57 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 58 | msgid "Deploy" 59 | msgstr "Развернуть" 60 | 61 | #: src/rimeengine.h:63 62 | msgid "Do not show" 63 | msgstr "Не показывать" 64 | 65 | #: src/rimeengine.h:91 66 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 67 | msgstr "" 68 | "Зафиксировать встроенный курсор предварительного редактирования в начале." 69 | 70 | #: src/rimeengine.h:58 71 | msgid "Follow Global Configuration" 72 | msgstr "Следовать глобальной конфигурации" 73 | 74 | #: src/rimecandidate.cpp:164 75 | msgid "Forget word" 76 | msgstr "Забыть слово" 77 | 78 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 79 | msgid "Latin Mode" 80 | msgstr "Режим Латиницы" 81 | 82 | #: src/rimeengine.h:59 83 | msgid "No" 84 | msgstr "Нет" 85 | 86 | #: src/rimeengine.h:81 87 | msgid "Preedit Mode" 88 | msgstr "Режим Предварительного Редактирования" 89 | 90 | #: src/rimeengine.h:59 91 | msgid "Program" 92 | msgstr "Программа" 93 | 94 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 95 | msgid "Rime" 96 | msgstr "Rime" 97 | 98 | #: src/rime-addon.conf.in.in:3 99 | msgid "Rime Wrapper For Fcitx" 100 | msgstr "Обертка Rime для Fcitx" 101 | 102 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 103 | msgid "Rime for Fcitx 5" 104 | msgstr "Rime для Fcitx 5" 105 | 106 | #: src/rimeengine.cpp:542 107 | msgid "Rime has encountered an error. See log for details." 108 | msgstr "Rime обнаружил ошибку. Подробности смотрите в журнале." 109 | 110 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 111 | msgid "Rime input method" 112 | msgstr "Метод ввода Rime" 113 | 114 | #: src/rimeengine.cpp:529 115 | msgid "Rime is ready." 116 | msgstr "Rime готов." 117 | 118 | #: src/rimeengine.cpp:526 119 | msgid "" 120 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 121 | "is finished..." 122 | msgstr "" 123 | "Rime находится на техническом обслуживании. Это может занять несколько " 124 | "секунд. Пожалуйста, дождитесь окончания..." 125 | 126 | #: src/rimeengine.h:84 127 | msgid "Shared Input State" 128 | msgstr "Общее состояние ввода" 129 | 130 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 131 | msgid "Synchronize" 132 | msgstr "Синхронизировать" 133 | 134 | #: src/rimeengine.h:100 135 | msgid "User data dir" 136 | msgstr "Каталог пользовательских данных" 137 | -------------------------------------------------------------------------------- /po/tr.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # abc Def , 2021 8 | # 9 | msgid "" 10 | msgstr "" 11 | "Project-Id-Version: fcitx5-rime\n" 12 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 13 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 14 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 15 | "Last-Translator: abc Def , 2021\n" 16 | "Language-Team: Turkish (https://app.transifex.com/fcitx/teams/12005/tr/)\n" 17 | "Language: tr\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 22 | 23 | #: src/rimeengine.h:97 24 | msgid "Action when switching input method" 25 | msgstr "" 26 | 27 | #: src/rimeengine.h:58 28 | msgid "All" 29 | msgstr "" 30 | 31 | #: src/rimeengine.h:73 32 | msgid "Clear" 33 | msgstr "" 34 | 35 | #: src/rimeengine.h:76 36 | msgid "Commit commit preview" 37 | msgstr "" 38 | 39 | #: src/rimeengine.h:75 40 | msgid "Commit composing text" 41 | msgstr "" 42 | 43 | #: src/rimeengine.h:64 44 | msgid "Commit preview" 45 | msgstr "" 46 | 47 | #: src/rimeengine.h:74 48 | msgid "Commit raw input" 49 | msgstr "" 50 | 51 | #: src/rimeengine.h:64 52 | msgid "Composing text" 53 | msgstr "" 54 | 55 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 56 | msgid "Deploy" 57 | msgstr "Dağıt" 58 | 59 | #: src/rimeengine.h:63 60 | msgid "Do not show" 61 | msgstr "" 62 | 63 | #: src/rimeengine.h:91 64 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 65 | msgstr "" 66 | 67 | #: src/rimeengine.h:58 68 | msgid "Follow Global Configuration" 69 | msgstr "" 70 | 71 | #: src/rimecandidate.cpp:164 72 | msgid "Forget word" 73 | msgstr "" 74 | 75 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 76 | msgid "Latin Mode" 77 | msgstr "Latin Modu" 78 | 79 | #: src/rimeengine.h:59 80 | msgid "No" 81 | msgstr "" 82 | 83 | #: src/rimeengine.h:81 84 | msgid "Preedit Mode" 85 | msgstr "" 86 | 87 | #: src/rimeengine.h:59 88 | msgid "Program" 89 | msgstr "" 90 | 91 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 92 | msgid "Rime" 93 | msgstr "Rime" 94 | 95 | #: src/rime-addon.conf.in.in:3 96 | msgid "Rime Wrapper For Fcitx" 97 | msgstr "Fcitx için Rime Wrapper" 98 | 99 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 100 | msgid "Rime for Fcitx 5" 101 | msgstr "Fcitx 5 için Rime" 102 | 103 | #: src/rimeengine.cpp:542 104 | msgid "Rime has encountered an error. See log for details." 105 | msgstr "" 106 | 107 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 108 | msgid "Rime input method" 109 | msgstr "Rime giriş yöntemi" 110 | 111 | #: src/rimeengine.cpp:529 112 | msgid "Rime is ready." 113 | msgstr "Rime hazır." 114 | 115 | #: src/rimeengine.cpp:526 116 | msgid "" 117 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 118 | "is finished..." 119 | msgstr "Rime bakımda. Birkaç saniye sürebilir. Lütfen bitene kadar bekleyin..." 120 | 121 | #: src/rimeengine.h:84 122 | msgid "Shared Input State" 123 | msgstr "" 124 | 125 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 126 | msgid "Synchronize" 127 | msgstr "Eşitleme" 128 | 129 | #: src/rimeengine.h:100 130 | msgid "User data dir" 131 | msgstr "" 132 | -------------------------------------------------------------------------------- /po/vi.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # Huy Võ , 2025 8 | # 9 | msgid "" 10 | msgstr "" 11 | "Project-Id-Version: fcitx5-rime\n" 12 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 13 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 14 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 15 | "Last-Translator: Huy Võ , 2025\n" 16 | "Language-Team: Vietnamese (https://app.transifex.com/fcitx/teams/12005/vi/)\n" 17 | "Language: vi\n" 18 | "MIME-Version: 1.0\n" 19 | "Content-Type: text/plain; charset=UTF-8\n" 20 | "Content-Transfer-Encoding: 8bit\n" 21 | "Plural-Forms: nplurals=1; plural=0;\n" 22 | 23 | #: src/rimeengine.h:97 24 | msgid "Action when switching input method" 25 | msgstr "Hành động khi chuyển đổi kiểu gõ" 26 | 27 | #: src/rimeengine.h:58 28 | msgid "All" 29 | msgstr "Tất cả" 30 | 31 | #: src/rimeengine.h:73 32 | msgid "Clear" 33 | msgstr "" 34 | 35 | #: src/rimeengine.h:76 36 | msgid "Commit commit preview" 37 | msgstr "" 38 | 39 | #: src/rimeengine.h:75 40 | msgid "Commit composing text" 41 | msgstr "" 42 | 43 | #: src/rimeengine.h:64 44 | msgid "Commit preview" 45 | msgstr "" 46 | 47 | #: src/rimeengine.h:74 48 | msgid "Commit raw input" 49 | msgstr "" 50 | 51 | #: src/rimeengine.h:64 52 | msgid "Composing text" 53 | msgstr "" 54 | 55 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 56 | msgid "Deploy" 57 | msgstr "Triển khai" 58 | 59 | #: src/rimeengine.h:63 60 | msgid "Do not show" 61 | msgstr "" 62 | 63 | #: src/rimeengine.h:91 64 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 65 | msgstr "" 66 | 67 | #: src/rimeengine.h:58 68 | msgid "Follow Global Configuration" 69 | msgstr "Theo Cấu Hình Chung" 70 | 71 | #: src/rimecandidate.cpp:164 72 | msgid "Forget word" 73 | msgstr "" 74 | 75 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 76 | msgid "Latin Mode" 77 | msgstr "" 78 | 79 | #: src/rimeengine.h:59 80 | msgid "No" 81 | msgstr "" 82 | 83 | #: src/rimeengine.h:81 84 | msgid "Preedit Mode" 85 | msgstr "" 86 | 87 | #: src/rimeengine.h:59 88 | msgid "Program" 89 | msgstr "" 90 | 91 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 92 | msgid "Rime" 93 | msgstr "Rime" 94 | 95 | #: src/rime-addon.conf.in.in:3 96 | msgid "Rime Wrapper For Fcitx" 97 | msgstr "Bộ Wrapper cho Fcitx" 98 | 99 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 100 | msgid "Rime for Fcitx 5" 101 | msgstr "" 102 | 103 | #: src/rimeengine.cpp:542 104 | msgid "Rime has encountered an error. See log for details." 105 | msgstr "" 106 | 107 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 108 | msgid "Rime input method" 109 | msgstr "" 110 | 111 | #: src/rimeengine.cpp:529 112 | msgid "Rime is ready." 113 | msgstr "Rime sẵn sàng," 114 | 115 | #: src/rimeengine.cpp:526 116 | msgid "" 117 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 118 | "is finished..." 119 | msgstr "" 120 | 121 | #: src/rimeengine.h:84 122 | msgid "Shared Input State" 123 | msgstr "" 124 | 125 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 126 | msgid "Synchronize" 127 | msgstr "Đồng bộ hóa" 128 | 129 | #: src/rimeengine.h:100 130 | msgid "User data dir" 131 | msgstr "" 132 | -------------------------------------------------------------------------------- /po/zh_CN.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # rocka, 2023 7 | # csslayer , 2023 8 | # Yiyu Liu, 2024 9 | # 10 | msgid "" 11 | msgstr "" 12 | "Project-Id-Version: fcitx5-rime\n" 13 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 14 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 15 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 16 | "Last-Translator: Yiyu Liu, 2024\n" 17 | "Language-Team: Chinese (China) (https://app.transifex.com/fcitx/teams/12005/" 18 | "zh_CN/)\n" 19 | "Language: zh_CN\n" 20 | "MIME-Version: 1.0\n" 21 | "Content-Type: text/plain; charset=UTF-8\n" 22 | "Content-Transfer-Encoding: 8bit\n" 23 | "Plural-Forms: nplurals=1; plural=0;\n" 24 | 25 | #: src/rimeengine.h:97 26 | msgid "Action when switching input method" 27 | msgstr "切换输入法时的行为" 28 | 29 | #: src/rimeengine.h:58 30 | msgid "All" 31 | msgstr "全部" 32 | 33 | #: src/rimeengine.h:73 34 | msgid "Clear" 35 | msgstr "清空" 36 | 37 | #: src/rimeengine.h:76 38 | msgid "Commit commit preview" 39 | msgstr "提交预览" 40 | 41 | #: src/rimeengine.h:75 42 | msgid "Commit composing text" 43 | msgstr "提交编辑中文本" 44 | 45 | #: src/rimeengine.h:64 46 | msgid "Commit preview" 47 | msgstr "提交预览" 48 | 49 | #: src/rimeengine.h:74 50 | msgid "Commit raw input" 51 | msgstr "提交原始字符串" 52 | 53 | #: src/rimeengine.h:64 54 | msgid "Composing text" 55 | msgstr "编辑中文本" 56 | 57 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 58 | msgid "Deploy" 59 | msgstr "重新部署" 60 | 61 | #: src/rimeengine.h:63 62 | msgid "Do not show" 63 | msgstr "不显示" 64 | 65 | #: src/rimeengine.h:91 66 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 67 | msgstr "将嵌入式预编辑文本的光标固定在开头" 68 | 69 | #: src/rimeengine.h:58 70 | msgid "Follow Global Configuration" 71 | msgstr "跟随全局选项" 72 | 73 | #: src/rimecandidate.cpp:164 74 | msgid "Forget word" 75 | msgstr "忘记词汇" 76 | 77 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 78 | msgid "Latin Mode" 79 | msgstr "英文模式" 80 | 81 | #: src/rimeengine.h:59 82 | msgid "No" 83 | msgstr "否" 84 | 85 | #: src/rimeengine.h:81 86 | msgid "Preedit Mode" 87 | msgstr "预编辑模式" 88 | 89 | #: src/rimeengine.h:59 90 | msgid "Program" 91 | msgstr "程序" 92 | 93 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 94 | msgid "Rime" 95 | msgstr "中州韵" 96 | 97 | #: src/rime-addon.conf.in.in:3 98 | msgid "Rime Wrapper For Fcitx" 99 | msgstr "Fcitx 的中州韵封装" 100 | 101 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 102 | msgid "Rime for Fcitx 5" 103 | msgstr "Fcitx 5 的中州韵支持" 104 | 105 | #: src/rimeengine.cpp:542 106 | msgid "Rime has encountered an error. See log for details." 107 | msgstr "Rime 出现了一个错误。详细信息请参阅日志。" 108 | 109 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 110 | msgid "Rime input method" 111 | msgstr "中州韵输入法" 112 | 113 | #: src/rimeengine.cpp:529 114 | msgid "Rime is ready." 115 | msgstr "Rime 就绪。" 116 | 117 | #: src/rimeengine.cpp:526 118 | msgid "" 119 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 120 | "is finished..." 121 | msgstr "Rime 正在进行维护程序。可能需要几秒钟。请等待完成……" 122 | 123 | #: src/rimeengine.h:84 124 | msgid "Shared Input State" 125 | msgstr "共享输入状态" 126 | 127 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 128 | msgid "Synchronize" 129 | msgstr "同步" 130 | 131 | #: src/rimeengine.h:100 132 | msgid "User data dir" 133 | msgstr "用户数据目录" 134 | -------------------------------------------------------------------------------- /po/zh_TW.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the fcitx5-rime package. 4 | # 5 | # Translators: 6 | # csslayer , 2017 7 | # pan93412 , 2019 8 | # bruh, 2020 9 | # neko_0xff , 2022 10 | # Kisaragi Hiu , 2024 11 | # Yiyu Liu, 2024 12 | # 13 | msgid "" 14 | msgstr "" 15 | "Project-Id-Version: fcitx5-rime\n" 16 | "Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" 17 | "POT-Creation-Date: 2025-05-25 20:24+0000\n" 18 | "PO-Revision-Date: 2017-11-23 05:02+0000\n" 19 | "Last-Translator: Yiyu Liu, 2024\n" 20 | "Language-Team: Chinese (Taiwan) (https://app.transifex.com/fcitx/teams/12005/" 21 | "zh_TW/)\n" 22 | "Language: zh_TW\n" 23 | "MIME-Version: 1.0\n" 24 | "Content-Type: text/plain; charset=UTF-8\n" 25 | "Content-Transfer-Encoding: 8bit\n" 26 | "Plural-Forms: nplurals=1; plural=0;\n" 27 | 28 | #: src/rimeengine.h:97 29 | msgid "Action when switching input method" 30 | msgstr "切換輸入法時的行為" 31 | 32 | #: src/rimeengine.h:58 33 | msgid "All" 34 | msgstr "全部" 35 | 36 | #: src/rimeengine.h:73 37 | msgid "Clear" 38 | msgstr "清除" 39 | 40 | #: src/rimeengine.h:76 41 | msgid "Commit commit preview" 42 | msgstr "提交預覽" 43 | 44 | #: src/rimeengine.h:75 45 | msgid "Commit composing text" 46 | msgstr "提交編輯中文本" 47 | 48 | #: src/rimeengine.h:64 49 | msgid "Commit preview" 50 | msgstr "提交預覽" 51 | 52 | #: src/rimeengine.h:74 53 | msgid "Commit raw input" 54 | msgstr "提交原始字串" 55 | 56 | #: src/rimeengine.h:64 57 | msgid "Composing text" 58 | msgstr "編輯中文本" 59 | 60 | #: src/rimeengine.h:110 src/rimeengine.cpp:202 61 | msgid "Deploy" 62 | msgstr "重新部署" 63 | 64 | #: src/rimeengine.h:63 65 | msgid "Do not show" 66 | msgstr "不顯示" 67 | 68 | #: src/rimeengine.h:91 69 | msgid "Fix embedded preedit cursor at the beginning of the preedit" 70 | msgstr "將內嵌預編輯游標位置固定在預編輯的開頭" 71 | 72 | #: src/rimeengine.h:58 73 | msgid "Follow Global Configuration" 74 | msgstr "跟隨全域配置" 75 | 76 | #: src/rimecandidate.cpp:164 77 | msgid "Forget word" 78 | msgstr "忘記字詞" 79 | 80 | #: src/rimestate.cpp:91 src/rimeengine.cpp:689 81 | msgid "Latin Mode" 82 | msgstr "羅馬拼音模式" 83 | 84 | #: src/rimeengine.h:59 85 | msgid "No" 86 | msgstr "不跟隨" 87 | 88 | #: src/rimeengine.h:81 89 | msgid "Preedit Mode" 90 | msgstr "預編輯模式" 91 | 92 | #: src/rimeengine.h:59 93 | msgid "Program" 94 | msgstr "程式" 95 | 96 | #: src/rimeengine.cpp:560 src/rime.conf.in:2 src/rime-addon.conf.in.in:2 97 | msgid "Rime" 98 | msgstr "中州韻" 99 | 100 | #: src/rime-addon.conf.in.in:3 101 | msgid "Rime Wrapper For Fcitx" 102 | msgstr "Fcitx 的中州韻封裝" 103 | 104 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:7 105 | msgid "Rime for Fcitx 5" 106 | msgstr "Fcitx 5 的中州韻支援" 107 | 108 | #: src/rimeengine.cpp:542 109 | msgid "Rime has encountered an error. See log for details." 110 | msgstr "Rime 遇到了一個錯誤。詳細資訊請檢視日誌。" 111 | 112 | #: org.fcitx.Fcitx5.Addon.Rime.metainfo.xml.in:8 113 | msgid "Rime input method" 114 | msgstr "Rime 輸入法" 115 | 116 | #: src/rimeengine.cpp:529 117 | msgid "Rime is ready." 118 | msgstr "Rime 準備好了。" 119 | 120 | #: src/rimeengine.cpp:526 121 | msgid "" 122 | "Rime is under maintenance. It may take a few seconds. Please wait until it " 123 | "is finished..." 124 | msgstr "Rime 正在維護中。這可能需要幾秒鐘。請等待完成…" 125 | 126 | #: src/rimeengine.h:84 127 | msgid "Shared Input State" 128 | msgstr "共享輸入狀態" 129 | 130 | #: src/rimeengine.h:114 src/rimeengine.cpp:214 131 | msgid "Synchronize" 132 | msgstr "同步" 133 | 134 | #: src/rimeengine.h:100 135 | msgid "User data dir" 136 | msgstr "使用者資料夾" 137 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(RIME_SOURCES 2 | rimestate.cpp 3 | rimeengine.cpp 4 | rimecandidate.cpp 5 | rimesession.cpp 6 | rimeaction.cpp 7 | rimefactory.cpp 8 | ) 9 | 10 | set(RIME_LINK_LIBRARIES 11 | Fcitx5::Core 12 | Fcitx5::Config 13 | ${RIME_TARGET} 14 | Fcitx5::Module::Notifications 15 | Pthread::Pthread 16 | ) 17 | 18 | find_package(Fcitx5ModuleDBus QUIET) 19 | if (Fcitx5ModuleDBus_FOUND) 20 | list(APPEND RIME_SOURCES rimeservice.cpp) 21 | list(APPEND RIME_LINK_LIBRARIES Fcitx5::Module::DBus) 22 | else() 23 | add_definitions(-DFCITX_RIME_NO_DBUS) 24 | endif() 25 | 26 | add_fcitx5_addon(rime ${RIME_SOURCES}) 27 | target_link_libraries(rime ${RIME_LINK_LIBRARIES}) 28 | install(TARGETS rime DESTINATION "${CMAKE_INSTALL_LIBDIR}/fcitx5") 29 | fcitx5_translate_desktop_file(rime.conf.in rime.conf) 30 | configure_file(rime-addon.conf.in.in rime-addon.conf.in) 31 | fcitx5_translate_desktop_file("${CMAKE_CURRENT_BINARY_DIR}/rime-addon.conf.in" rime-addon.conf) 32 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/rime.conf" DESTINATION "${FCITX_INSTALL_PKGDATADIR}/inputmethod" COMPONENT config) 33 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/rime-addon.conf" RENAME rime.conf DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon" COMPONENT config) 34 | 35 | -------------------------------------------------------------------------------- /src/rime-addon.conf.in.in: -------------------------------------------------------------------------------- 1 | [Addon] 2 | Name=Rime 3 | Comment=Rime Wrapper For Fcitx 4 | Category=InputMethod 5 | Version=@PROJECT_VERSION@ 6 | Library=export:librime 7 | Type=@FCITX_ADDON_TYPE@ 8 | OnDemand=True 9 | Configurable=True 10 | 11 | [Addon/Dependencies] 12 | 0=core:@REQUIRED_FCITX_VERSION@ 13 | 14 | [Addon/OptionalDependencies] 15 | 0=notifications 16 | 1=dbus 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/rime.conf.in: -------------------------------------------------------------------------------- 1 | [InputMethod] 2 | Name=Rime 3 | Icon=fcitx-rime 4 | Label=ㄓ 5 | LangCode=zh 6 | Addon=rime 7 | Configurable=True 8 | -------------------------------------------------------------------------------- /src/rimeaction.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024~2024 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | #include "rimeaction.h" 7 | #include "rimeengine.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace fcitx::rime { 22 | 23 | namespace { 24 | 25 | std::optional optionValue(RimeEngine *engine, InputContext *ic, 26 | bool requestSession, 27 | const std::string &option) { 28 | auto *state = engine->state(ic); 29 | auto *api = engine->api(); 30 | if (!state) { 31 | return std::nullopt; 32 | } 33 | auto session = state->session(requestSession); 34 | if (!session) { 35 | return std::nullopt; 36 | } 37 | return bool(api->get_option(session, option.c_str())); 38 | } 39 | } // namespace 40 | 41 | ToggleAction::ToggleAction(RimeEngine *engine, std::string_view schema, 42 | std::string_view option, std::string disabledText, 43 | std::string enabledText) 44 | : engine_(engine), option_(option), disabledText_(std::move(disabledText)), 45 | enabledText_(std::move(enabledText)) { 46 | engine_->instance()->userInterfaceManager().registerAction( 47 | stringutils::concat("fcitx-rime-", schema, "-", option), this); 48 | } 49 | 50 | void ToggleAction::activate(InputContext *ic) { 51 | auto *state = engine_->state(ic); 52 | auto *api = engine_->api(); 53 | if (!state) { 54 | return; 55 | } 56 | auto session = state->session(); 57 | Bool oldValue = api->get_option(session, option_.c_str()); 58 | api->set_option(session, option_.c_str(), !oldValue); 59 | } 60 | 61 | std::string ToggleAction::shortText(InputContext *ic) const { 62 | auto value = optionValue(engine_, ic, /*requestSession=*/true, option_); 63 | if (!value.has_value()) { 64 | return ""; 65 | } 66 | if (*value) { 67 | return stringutils::concat(enabledText_, " → ", disabledText_); 68 | } 69 | return stringutils::concat(disabledText_, " → ", enabledText_); 70 | } 71 | 72 | std::optional ToggleAction::snapshotOption(InputContext *ic) { 73 | auto value = optionValue(engine_, ic, /*requestSession=*/false, option_); 74 | if (!value.has_value()) { 75 | return std::nullopt; 76 | } 77 | return *value ? option_ : stringutils::concat("!", option_); 78 | } 79 | 80 | bool ToggleAction::checkOptionName(std::string_view name) const { 81 | return name == option_; 82 | } 83 | 84 | std::string ToggleAction::optionLabel(InputContext *ic) { 85 | auto value = optionValue(engine_, ic, /*requestSession=*/true, option_); 86 | if (!value.has_value()) { 87 | return ""; 88 | } 89 | return *value ? enabledText_ : disabledText_; 90 | } 91 | 92 | SelectAction::SelectAction(RimeEngine *engine, std::string_view schema, 93 | std::vector options, 94 | std::vector texts) 95 | : engine_(engine), options_(options), texts_(std::move(texts)) { 96 | for (size_t i = 0; i < options.size(); ++i) { 97 | actions_.emplace_back(); 98 | actions_.back().setShortText(texts_[i]); 99 | actions_.back().connect( 100 | [this, i](InputContext *ic) { 101 | auto *state = engine_->state(ic); 102 | auto *api = engine_->api(); 103 | if (!state) { 104 | return; 105 | } 106 | auto session = state->session(); 107 | for (size_t j = 0; j < options_.size(); ++j) { 108 | api->set_option(session, options_[j].c_str(), i == j); 109 | } 110 | }); 111 | engine_->instance()->userInterfaceManager().registerAction( 112 | stringutils::concat("fcitx-rime-", schema, "-", options_[i]), 113 | &actions_.back()); 114 | menu_.addAction(&actions_.back()); 115 | } 116 | setMenu(&menu_); 117 | engine_->instance()->userInterfaceManager().registerAction( 118 | stringutils::concat("fcitx-rime-", schema, "-select-", options[0]), 119 | this); 120 | } 121 | 122 | std::string SelectAction::shortText(InputContext *ic) const { 123 | auto *state = engine_->state(ic); 124 | auto *api = engine_->api(); 125 | if (!state) { 126 | return ""; 127 | } 128 | auto session = state->session(); 129 | for (size_t i = 0; i < options_.size(); ++i) { 130 | if (api->get_option(session, options_[i].c_str())) { 131 | return texts_[i]; 132 | } 133 | } 134 | return ""; 135 | } 136 | 137 | std::optional SelectAction::snapshotOption(InputContext *ic) { 138 | auto *state = engine_->state(ic); 139 | auto *api = engine_->api(); 140 | if (!state) { 141 | return std::nullopt; 142 | } 143 | auto session = state->session(false); 144 | if (!session) { 145 | return std::nullopt; 146 | } 147 | for (auto &option : options_) { 148 | if (api->get_option(session, option.c_str())) { 149 | return option; 150 | } 151 | } 152 | return std::nullopt; 153 | } 154 | 155 | bool SelectAction::checkOptionName(std::string_view name) const { 156 | return std::find(options_.begin(), options_.end(), name) != options_.end(); 157 | } 158 | 159 | std::string SelectAction::optionLabel(InputContext *ic) { 160 | return shortText(ic); 161 | } 162 | } // namespace fcitx::rime 163 | -------------------------------------------------------------------------------- /src/rimeaction.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2017~2017 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | #ifndef _FCITX_RIMEACTION_H_ 7 | #define _FCITX_RIMEACTION_H_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace fcitx::rime { 19 | 20 | class RimeEngine; 21 | 22 | class RimeOptionAction : public Action { 23 | public: 24 | // This is used to save the option when we need to release the session. 25 | virtual std::optional snapshotOption(InputContext *ic) = 0; 26 | // Return the label of current option. 27 | virtual std::string optionLabel(InputContext *ic) = 0; 28 | // Check whether a option name belongs to the action. 29 | virtual bool checkOptionName(std::string_view name) const = 0; 30 | }; 31 | 32 | class ToggleAction : public RimeOptionAction { 33 | public: 34 | ToggleAction(RimeEngine *engine, std::string_view schema, 35 | std::string_view option, std::string disabledText, 36 | std::string enabledText); 37 | 38 | void activate(InputContext *ic) override; 39 | 40 | std::string shortText(InputContext *ic) const override; 41 | 42 | std::string icon(InputContext * /*unused*/) const override { return ""; } 43 | 44 | std::optional snapshotOption(InputContext *ic) override; 45 | 46 | const std::string &option() const { return option_; } 47 | 48 | std::string optionLabel(InputContext *ic) override; 49 | 50 | bool checkOptionName(std::string_view name) const override; 51 | 52 | private: 53 | RimeEngine *engine_; 54 | std::string option_; 55 | std::string disabledText_; 56 | std::string enabledText_; 57 | }; 58 | 59 | class SelectAction : public RimeOptionAction { 60 | public: 61 | SelectAction(RimeEngine *engine, std::string_view schema, 62 | std::vector options, 63 | std::vector texts); 64 | 65 | std::string shortText(InputContext *ic) const override; 66 | 67 | std::string icon(InputContext * /*unused*/) const override { return ""; } 68 | 69 | std::optional snapshotOption(InputContext *ic) override; 70 | 71 | const std::vector &options() const { return options_; } 72 | 73 | std::string optionLabel(InputContext *ic) override; 74 | 75 | bool checkOptionName(std::string_view name) const override; 76 | 77 | private: 78 | RimeEngine *engine_; 79 | std::vector options_; 80 | std::vector texts_; 81 | std::list actions_; 82 | Menu menu_; 83 | }; 84 | 85 | } // namespace fcitx::rime 86 | 87 | #endif // _FCITX_RIMEENGINE_H_ 88 | -------------------------------------------------------------------------------- /src/rimecandidate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2017~2017 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #include "rimecandidate.h" 8 | #include "rimeengine.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace fcitx::rime { 17 | 18 | RimeCandidateWord::RimeCandidateWord(RimeEngine *engine, 19 | const RimeCandidate &candidate, KeySym sym, 20 | int idx) 21 | : engine_(engine), sym_(sym), idx_(idx) { 22 | setText(Text{candidate.text}); 23 | if (candidate.comment && candidate.comment[0]) { 24 | setComment(Text{candidate.comment}); 25 | } 26 | } 27 | 28 | void RimeCandidateWord::select(InputContext *inputContext) const { 29 | if (auto *state = engine_->state(inputContext)) { 30 | state->selectCandidate(inputContext, idx_, /*global=*/false); 31 | } 32 | } 33 | 34 | void RimeCandidateWord::forget(RimeState *state) const { 35 | #ifndef FCITX_RIME_NO_DELETE_CANDIDATE 36 | state->deleteCandidate(idx_, /*global=*/false); 37 | #endif 38 | } 39 | 40 | RimeGlobalCandidateWord::RimeGlobalCandidateWord(RimeEngine *engine, 41 | const RimeCandidate &candidate, 42 | int idx) 43 | : engine_(engine), idx_(idx) { 44 | setText(Text{candidate.text}); 45 | if (candidate.comment && candidate.comment[0]) { 46 | setComment(Text{candidate.comment}); 47 | } 48 | } 49 | 50 | void RimeGlobalCandidateWord::select(InputContext *inputContext) const { 51 | if (auto *state = engine_->state(inputContext)) { 52 | state->selectCandidate(inputContext, idx_, /*global=*/true); 53 | } 54 | } 55 | 56 | void RimeGlobalCandidateWord::forget(RimeState *state) const { 57 | #ifndef FCITX_RIME_NO_DELETE_CANDIDATE 58 | state->deleteCandidate(idx_, /*global=*/true); 59 | #endif 60 | } 61 | 62 | RimeCandidateList::RimeCandidateList(RimeEngine *engine, InputContext *ic, 63 | const RimeContext &context) 64 | : engine_(engine), ic_(ic), hasPrev_(context.menu.page_no != 0), 65 | hasNext_(!context.menu.is_last_page) { 66 | setPageable(this); 67 | setBulk(this); 68 | setActionable(this); 69 | #ifndef FCITX_RIME_NO_HIGHLIGHT_CANDIDATE 70 | setBulkCursor(this); 71 | #endif 72 | 73 | const auto &menu = context.menu; 74 | 75 | int num_select_keys = menu.select_keys ? strlen(menu.select_keys) : 0; 76 | bool has_label = RIME_STRUCT_HAS_MEMBER(context, context.select_labels) && 77 | context.select_labels; 78 | 79 | int i; 80 | for (i = 0; i < menu.num_candidates; ++i) { 81 | KeySym sym = FcitxKey_None; 82 | std::string label; 83 | if (i < menu.page_size && has_label) { 84 | label = context.select_labels[i]; 85 | } else if (i < num_select_keys) { 86 | label = std::string(1, menu.select_keys[i]); 87 | } else { 88 | label = std::to_string((i + 1) % 10); 89 | } 90 | label.append(" "); 91 | labels_.emplace_back(label); 92 | 93 | if (i < num_select_keys) { 94 | sym = static_cast(menu.select_keys[i]); 95 | } else { 96 | sym = static_cast('0' + (i + 1) % 10); 97 | } 98 | candidateWords_.emplace_back(std::make_unique( 99 | engine, menu.candidates[i], sym, i)); 100 | 101 | if (i == menu.highlighted_candidate_index) { 102 | cursor_ = i; 103 | } 104 | } 105 | } 106 | 107 | const CandidateWord &RimeCandidateList::candidateFromAll(int idx) const { 108 | if (idx < 0 || empty()) { 109 | throw std::invalid_argument("Invalid global index"); 110 | } 111 | 112 | auto session = engine_->state(ic_)->session(false); 113 | if (!session) { 114 | throw std::invalid_argument("Invalid session"); 115 | } 116 | 117 | auto index = static_cast(idx); 118 | 119 | auto *api = engine_->api(); 120 | 121 | RimeCandidateListIterator iter; 122 | if (index >= globalCandidateWords_.size()) { 123 | if (index >= maxSize_) { 124 | throw std::invalid_argument("Invalid global index"); 125 | } 126 | } else { 127 | if (globalCandidateWords_[index]) { 128 | return *globalCandidateWords_[index]; 129 | } 130 | } 131 | 132 | if (!api->candidate_list_from_index(session, &iter, idx) || 133 | !api->candidate_list_next(&iter)) { 134 | maxSize_ = std::min(index, maxSize_); 135 | throw std::invalid_argument("Invalid global index"); 136 | } 137 | 138 | if (index >= globalCandidateWords_.size()) { 139 | globalCandidateWords_.resize(index + 1); 140 | } 141 | globalCandidateWords_[index] = 142 | std::make_unique(engine_, iter.candidate, idx); 143 | api->candidate_list_end(&iter); 144 | return *globalCandidateWords_[index]; 145 | } 146 | 147 | int RimeCandidateList::totalSize() const { return -1; } 148 | 149 | bool RimeCandidateList::hasAction(const CandidateWord & /*candidate*/) const { 150 | #ifndef FCITX_RIME_NO_DELETE_CANDIDATE 151 | // We can always reset rime candidate's frequency. 152 | return true; 153 | #else 154 | return false; 155 | #endif 156 | } 157 | 158 | std::vector 159 | RimeCandidateList::candidateActions(const CandidateWord & /*candidate*/) const { 160 | std::vector actions; 161 | #ifndef FCITX_RIME_NO_DELETE_CANDIDATE 162 | CandidateAction action; 163 | action.setId(0); 164 | action.setText(_("Forget word")); 165 | actions.push_back(std::move(action)); 166 | #endif 167 | return actions; 168 | } 169 | 170 | void RimeCandidateList::triggerAction(const CandidateWord &candidate, int id) { 171 | if (id != 0) { 172 | return; 173 | } 174 | if (auto state = engine_->state(ic_)) { 175 | if (const auto *rimeCandidate = 176 | dynamic_cast(&candidate)) { 177 | rimeCandidate->forget(state); 178 | } else if (const auto *rimeCandidate = 179 | dynamic_cast(&candidate)) { 180 | rimeCandidate->forget(state); 181 | } 182 | } 183 | } 184 | 185 | #ifndef FCITX_RIME_NO_HIGHLIGHT_CANDIDATE 186 | int RimeCandidateList::globalCursorIndex() const { 187 | return -1; // No API available. 188 | } 189 | 190 | void RimeCandidateList::setGlobalCursorIndex(int index) { 191 | auto session = engine_->state(ic_)->session(false); 192 | if (!session) { 193 | return; 194 | } 195 | auto *api = engine_->api(); 196 | api->highlight_candidate(session, index); 197 | } 198 | #endif 199 | } // namespace fcitx::rime 200 | -------------------------------------------------------------------------------- /src/rimecandidate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2017~2017 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | #ifndef _FCITX_RIMECANDIDATE_H_ 7 | #define _FCITX_RIMECANDIDATE_H_ 8 | 9 | #include "rimeengine.h" 10 | #include "rimestate.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace fcitx::rime { 18 | 19 | class RimeCandidateWord : public CandidateWord { 20 | public: 21 | RimeCandidateWord(RimeEngine *engine, const RimeCandidate &candidate, 22 | KeySym sym, int idx); 23 | 24 | void select(InputContext *inputContext) const override; 25 | void forget(RimeState *state) const; 26 | 27 | private: 28 | RimeEngine *engine_; 29 | KeySym sym_; 30 | int idx_; 31 | }; 32 | 33 | class RimeGlobalCandidateWord : public CandidateWord { 34 | public: 35 | RimeGlobalCandidateWord(RimeEngine *engine, const RimeCandidate &candidate, 36 | int idx); 37 | 38 | void select(InputContext *inputContext) const override; 39 | void forget(RimeState *state) const; 40 | 41 | private: 42 | RimeEngine *engine_; 43 | int idx_; 44 | }; 45 | 46 | class RimeCandidateList final : public CandidateList, 47 | public ActionableCandidateList, 48 | public PageableCandidateList, 49 | public BulkCandidateList 50 | #ifndef FCITX_RIME_NO_HIGHLIGHT_CANDIDATE 51 | , 52 | public BulkCursorCandidateList 53 | #endif 54 | { 55 | public: 56 | RimeCandidateList(RimeEngine *engine, InputContext *ic, 57 | const RimeContext &context); 58 | 59 | const Text &label(int idx) const override { 60 | checkIndex(idx); 61 | return labels_[idx]; 62 | } 63 | 64 | const CandidateWord &candidate(int idx) const override { 65 | checkIndex(idx); 66 | return *candidateWords_[idx]; 67 | } 68 | int size() const override { return candidateWords_.size(); } 69 | 70 | int cursorIndex() const override { return cursor_; } 71 | 72 | CandidateLayoutHint layoutHint() const override { return layout_; } 73 | 74 | bool hasPrev() const override { return hasPrev_; } 75 | bool hasNext() const override { return hasNext_; } 76 | void prev() override { 77 | KeyEvent event(ic_, Key(FcitxKey_Page_Up)); 78 | if (auto state = engine_->state(ic_)) { 79 | state->keyEvent(event); 80 | } 81 | } 82 | void next() override { 83 | KeyEvent event(ic_, Key(FcitxKey_Page_Down)); 84 | if (auto state = engine_->state(ic_)) { 85 | state->keyEvent(event); 86 | } 87 | } 88 | 89 | bool usedNextBefore() const override { return true; } 90 | 91 | const CandidateWord &candidateFromAll(int idx) const override; 92 | int totalSize() const override; 93 | 94 | #ifndef FCITX_RIME_NO_HIGHLIGHT_CANDIDATE 95 | int globalCursorIndex() const override; 96 | void setGlobalCursorIndex(int index) override; 97 | #endif 98 | 99 | bool hasAction(const CandidateWord &candidate) const override; 100 | std::vector 101 | candidateActions(const CandidateWord &candidate) const override; 102 | void triggerAction(const CandidateWord &candidate, int id) override; 103 | 104 | private: 105 | void checkIndex(int idx) const { 106 | if (idx < 0 && idx >= size()) { 107 | throw std::invalid_argument("invalid index"); 108 | } 109 | } 110 | 111 | RimeEngine *engine_; 112 | InputContext *ic_; 113 | std::vector labels_; 114 | bool hasPrev_ = false; 115 | bool hasNext_ = false; 116 | CandidateLayoutHint layout_ = CandidateLayoutHint::NotSet; 117 | int cursor_ = -1; 118 | 119 | std::vector> candidateWords_; 120 | 121 | mutable size_t maxSize_ = std::numeric_limits::max(); 122 | mutable std::vector> 123 | globalCandidateWords_; 124 | }; 125 | } // namespace fcitx::rime 126 | 127 | #endif // _FCITX_RIMECANDIDATE_H_ 128 | -------------------------------------------------------------------------------- /src/rimeengine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2017~2017 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #include "rimeengine.h" 8 | #include "notifications_public.h" 9 | #include "rimeaction.h" 10 | #include "rimestate.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | FCITX_DEFINE_LOG_CATEGORY(rime_log, "rime"); 52 | 53 | namespace fcitx::rime { 54 | 55 | namespace { 56 | 57 | // Allow notification for 60secs. 58 | constexpr uint64_t NotificationTimeout = 60000000; 59 | 60 | std::unordered_map> 61 | parseAppOptions(rime_api_t *api, RimeConfig *config) { 62 | std::unordered_map> 63 | appOptions; 64 | RimeConfigIterator appIter; 65 | RimeConfigIterator optionIter; 66 | if (api->config_begin_map(&appIter, config, "app_options")) { 67 | while (api->config_next(&appIter)) { 68 | auto &options = appOptions[appIter.key]; 69 | if (api->config_begin_map(&optionIter, config, appIter.path)) { 70 | while (api->config_next(&optionIter)) { 71 | Bool value = False; 72 | if (api->config_get_bool(config, optionIter.path, &value)) { 73 | options[optionIter.key] = !!value; 74 | } 75 | } 76 | api->config_end(&optionIter); 77 | } 78 | } 79 | api->config_end(&appIter); 80 | } 81 | return appOptions; 82 | } 83 | 84 | std::vector getListItemPath(rime_api_t *api, RimeConfig *config, 85 | const std::string &path) { 86 | std::vector paths; 87 | RimeConfigIterator iter; 88 | if (api->config_begin_list(&iter, config, path.c_str())) { 89 | while (api->config_next(&iter)) { 90 | paths.push_back(iter.path); 91 | } 92 | api->config_end(&iter); 93 | } 94 | return paths; 95 | } 96 | 97 | std::vector getListItemString(rime_api_t *api, RimeConfig *config, 98 | const std::string &path) { 99 | std::vector values; 100 | auto paths = getListItemPath(api, config, path); 101 | for (const auto &path : paths) { 102 | const auto *value = api->config_get_cstring(config, path.c_str()); 103 | if (!value) { 104 | return {}; 105 | } 106 | values.emplace_back(value); 107 | } 108 | return values; 109 | } 110 | 111 | rime_api_t *EnsureRimeApi() { 112 | auto *api = rime_get_api(); 113 | if (!api) { 114 | throw std::runtime_error("Failed to get Rime API"); 115 | } 116 | return api; 117 | } 118 | 119 | } // namespace 120 | 121 | class IMAction : public Action { 122 | public: 123 | IMAction(RimeEngine *engine) : engine_(engine) {} 124 | 125 | std::string shortText(InputContext *ic) const override { 126 | std::string result; 127 | auto *state = engine_->state(ic); 128 | if (state) { 129 | state->getStatus([&result](const RimeStatus &status) { 130 | result = status.schema_id ? status.schema_id : ""; 131 | if (status.is_disabled) { 132 | result = "\xe2\x8c\x9b"; 133 | } else if (status.is_ascii_mode) { 134 | result = "A"; 135 | } else if (status.schema_name && status.schema_name[0] != '.') { 136 | result = status.schema_name; 137 | } else { 138 | result = "中"; 139 | } 140 | }); 141 | } else { 142 | result = "\xe2\x8c\x9b"; 143 | } 144 | return result; 145 | } 146 | 147 | std::string longText(InputContext *ic) const override { 148 | std::string result; 149 | auto *state = engine_->state(ic); 150 | if (state) { 151 | state->getStatus([&result](const RimeStatus &status) { 152 | result = status.schema_name ? status.schema_name : ""; 153 | }); 154 | } 155 | return result; 156 | } 157 | 158 | std::string icon(InputContext *ic) const override { 159 | bool isDisabled = false; 160 | auto *state = engine_->state(ic); 161 | if (state) { 162 | state->getStatus([&isDisabled](const RimeStatus &status) { 163 | isDisabled = status.is_disabled; 164 | }); 165 | } 166 | if (isDisabled) { 167 | return "fcitx_rime_disabled"; 168 | } 169 | return "fcitx_rime_im"; 170 | } 171 | 172 | private: 173 | RimeEngine *engine_; 174 | }; 175 | 176 | bool RimeEngine::firstRun_ = true; 177 | 178 | RimeEngine::RimeEngine(Instance *instance) 179 | : instance_(instance), api_(EnsureRimeApi()), 180 | factory_([this](InputContext &ic) { return new RimeState(this, ic); }), 181 | sessionPool_(this, getSharedStatePolicy()) { 182 | if constexpr (isAndroid() || isApple()) { 183 | const auto &sp = StandardPaths::global(); 184 | std::string defaultYaml = 185 | sp.locate(StandardPathsType::Data, "rime-data/default.yaml"); 186 | if (defaultYaml.empty()) { 187 | throw std::runtime_error("Fail to locate shared data directory"); 188 | } 189 | sharedDataDir_ = fcitx::fs::dirName(defaultYaml); 190 | } else { 191 | sharedDataDir_ = RIME_DATA_DIR; 192 | } 193 | imAction_ = std::make_unique(this); 194 | instance_->userInterfaceManager().registerAction("fcitx-rime-im", 195 | imAction_.get()); 196 | imAction_->setMenu(&schemaMenu_); 197 | eventDispatcher_.attach(&instance_->eventLoop()); 198 | separatorAction_.setSeparator(true); 199 | instance_->userInterfaceManager().registerAction("fcitx-rime-separator", 200 | &separatorAction_); 201 | deployAction_.setIcon("fcitx_rime_deploy"); 202 | deployAction_.setShortText(_("Deploy")); 203 | deployAction_.connect([this](InputContext *ic) { 204 | deploy(); 205 | auto *state = this->state(ic); 206 | if (state && ic->hasFocus()) { 207 | state->updateUI(ic, false); 208 | } 209 | }); 210 | instance_->userInterfaceManager().registerAction("fcitx-rime-deploy", 211 | &deployAction_); 212 | 213 | syncAction_.setIcon("fcitx_rime_sync"); 214 | syncAction_.setShortText(_("Synchronize")); 215 | 216 | syncAction_.connect([this](InputContext *ic) { 217 | sync(/*userTriggered=*/true); 218 | auto *state = this->state(ic); 219 | if (state && ic->hasFocus()) { 220 | state->updateUI(ic, false); 221 | } 222 | }); 223 | instance_->userInterfaceManager().registerAction("fcitx-rime-sync", 224 | &syncAction_); 225 | schemaMenu_.addAction(&separatorAction_); 226 | schemaMenu_.addAction(&deployAction_); 227 | schemaMenu_.addAction(&syncAction_); 228 | globalConfigReloadHandle_ = instance_->watchEvent( 229 | EventType::GlobalConfigReloaded, EventWatcherPhase::Default, 230 | [this](Event &) { refreshSessionPoolPolicy(); }); 231 | 232 | allowNotification("failure"); 233 | reloadConfig(); 234 | constructed_ = true; 235 | } 236 | 237 | RimeEngine::~RimeEngine() { 238 | factory_.unregister(); 239 | try { 240 | api_->finalize(); 241 | } catch (const std::exception &e) { 242 | RIME_ERROR() << e.what(); 243 | } 244 | } 245 | 246 | void RimeEngine::rimeStart(bool fullcheck) { 247 | RIME_DEBUG() << "Rime Start (fullcheck: " << fullcheck << ")"; 248 | 249 | auto userDir = 250 | StandardPaths::global().userDirectory(StandardPathsType::PkgData) / 251 | "rime"; 252 | RIME_DEBUG() << "Rime data directory: " << userDir; 253 | if (!fs::makePath(userDir)) { 254 | if (!fs::isdir(userDir)) { 255 | RIME_ERROR() << "Failed to create user directory: " << userDir; 256 | } 257 | } 258 | 259 | RIME_STRUCT(RimeTraits, fcitx_rime_traits); 260 | fcitx_rime_traits.shared_data_dir = sharedDataDir_.c_str(); 261 | fcitx_rime_traits.app_name = "rime.fcitx-rime"; 262 | fcitx_rime_traits.user_data_dir = userDir.c_str(); 263 | fcitx_rime_traits.distribution_name = "Rime"; 264 | fcitx_rime_traits.distribution_code_name = "fcitx-rime"; 265 | fcitx_rime_traits.distribution_version = FCITX_RIME_VERSION; 266 | // make librime only log to stderr 267 | // https://github.com/rime/librime/commit/6d1b9b65de4e7784a68a17d10a3e5c900e4fd511 268 | fcitx_rime_traits.log_dir = ""; 269 | switch (rime_log().logLevel()) { 270 | case NoLog: 271 | fcitx_rime_traits.min_log_level = 4; 272 | break; 273 | case Fatal: 274 | fcitx_rime_traits.min_log_level = 3; 275 | break; 276 | case Error: 277 | case Warn: 278 | case Info: 279 | fcitx_rime_traits.min_log_level = 2; 280 | break; 281 | case Debug: 282 | default: 283 | // Rime info is too noisy. 284 | fcitx_rime_traits.min_log_level = 0; 285 | break; 286 | } 287 | 288 | fcitx_rime_traits.modules = nullptr; 289 | 290 | if (firstRun_) { 291 | api_->setup(&fcitx_rime_traits); 292 | firstRun_ = false; 293 | } 294 | api_->initialize(&fcitx_rime_traits); 295 | api_->set_notification_handler(&rimeNotificationHandler, this); 296 | api_->start_maintenance(fullcheck); 297 | 298 | if (!api_->is_maintenance_mode()) { 299 | updateAppOptions(); 300 | } else { 301 | needRefreshAppOption_ = true; 302 | } 303 | } 304 | 305 | void RimeEngine::updateAppOptions() { 306 | appOptions_.clear(); 307 | RimeConfig config = {nullptr}; 308 | if (api_->config_open("fcitx5", &config)) { 309 | appOptions_ = parseAppOptions(api_, &config); 310 | api_->config_close(&config); 311 | } 312 | RIME_DEBUG() << "App options are " << appOptions_; 313 | releaseAllSession(); 314 | } 315 | 316 | void RimeEngine::reloadConfig() { 317 | readAsIni(config_, "conf/rime.conf"); 318 | updateConfig(); 319 | } 320 | 321 | void RimeEngine::setSubConfig(const std::string &path, 322 | const RawConfig & /*unused*/) { 323 | if (path == "deploy") { 324 | deploy(); 325 | } else if (path == "sync") { 326 | sync(/*userTriggered=*/true); 327 | } 328 | } 329 | 330 | void RimeEngine::updateConfig() { 331 | RIME_DEBUG() << "Rime UpdateConfig"; 332 | if (constructed_ && factory_.registered()) { 333 | releaseAllSession(true); 334 | } 335 | try { 336 | api_->finalize(); 337 | } catch (const std::exception &e) { 338 | RIME_ERROR() << e.what(); 339 | } 340 | 341 | rimeStart(false); 342 | instance_->inputContextManager().registerProperty("rimeState", &factory_); 343 | updateSchemaMenu(); 344 | refreshSessionPoolPolicy(); 345 | 346 | deployAction_.setHotkey(config_.deploy.value()); 347 | syncAction_.setHotkey(config_.synchronize.value()); 348 | 349 | if (constructed_) { 350 | refreshStatusArea(0); 351 | } 352 | } 353 | 354 | void RimeEngine::refreshStatusArea(InputContext &ic) { 355 | // prevent modifying status area owned by other ime 356 | // e.g. keyboard-us when typing password 357 | if (instance_->inputMethod(&ic) != "rime") { 358 | return; 359 | } 360 | auto &statusArea = ic.statusArea(); 361 | statusArea.clearGroup(StatusGroup::InputMethod); 362 | statusArea.addAction(StatusGroup::InputMethod, imAction_.get()); 363 | 364 | auto *rimeState = state(&ic); 365 | std::string currentSchema; 366 | if (!rimeState) { 367 | return; 368 | } 369 | rimeState->getStatus([¤tSchema](const RimeStatus &status) { 370 | currentSchema = status.schema_id ? status.schema_id : ""; 371 | }); 372 | if (currentSchema.empty()) { 373 | return; 374 | } 375 | 376 | if (auto iter = optionActions_.find(currentSchema); 377 | iter != optionActions_.end()) { 378 | for (const auto &action : iter->second) { 379 | statusArea.addAction(StatusGroup::InputMethod, action.get()); 380 | } 381 | } 382 | } 383 | 384 | void RimeEngine::refreshStatusArea(RimeSessionId session) { 385 | instance_->inputContextManager().foreachFocused( 386 | [this, session](InputContext *ic) { 387 | if (auto *state = this->state(ic)) { 388 | // After a deployment, param is 0, refresh all 389 | if (!session || state->session(false) == session) { 390 | refreshStatusArea(*ic); 391 | } 392 | } 393 | return true; 394 | }); 395 | } 396 | 397 | void RimeEngine::updateStatusArea(RimeSessionId session) { 398 | instance_->inputContextManager().foreachFocused( 399 | [this, session](InputContext *ic) { 400 | if (instance_->inputMethod(ic) != "rime") { 401 | return true; 402 | } 403 | if (auto *state = this->state(ic)) { 404 | // After a deployment, param is 0, refresh all 405 | if (!session || state->session(false) == session) { 406 | // Re-read new option values. 407 | ic->updateUserInterface(UserInterfaceComponent::StatusArea); 408 | } 409 | } 410 | return true; 411 | }); 412 | } 413 | 414 | void RimeEngine::activate(const InputMethodEntry & /*entry*/, 415 | InputContextEvent &event) { 416 | auto *ic = event.inputContext(); 417 | refreshStatusArea(*ic); 418 | if (auto *state = this->state(ic)) { 419 | state->activate(); 420 | } 421 | } 422 | 423 | void RimeEngine::deactivate(const InputMethodEntry &entry, 424 | InputContextEvent &event) { 425 | if (event.type() == EventType::InputContextSwitchInputMethod) { 426 | auto *inputContext = event.inputContext(); 427 | auto *state = this->state(inputContext); 428 | switch (*config_.switchInputMethodBehavior) { 429 | case SwitchInputMethodBehavior::Clear: 430 | break; 431 | case SwitchInputMethodBehavior::CommitRawInput: 432 | state->commitInput(inputContext); 433 | break; 434 | case SwitchInputMethodBehavior::CommitComposingText: 435 | state->commitComposing(inputContext); 436 | break; 437 | case SwitchInputMethodBehavior::CommitCommitPreview: 438 | state->commitPreedit(inputContext); 439 | break; 440 | } 441 | } 442 | reset(entry, event); 443 | } 444 | 445 | void RimeEngine::keyEvent(const InputMethodEntry &entry, KeyEvent &event) { 446 | FCITX_UNUSED(entry); 447 | RIME_DEBUG() << "Rime receive key: " << event.rawKey() << " " 448 | << event.isRelease(); 449 | auto *inputContext = event.inputContext(); 450 | if (!event.isRelease()) { 451 | if (event.key().checkKeyList(*config_.deploy)) { 452 | deploy(); 453 | event.filterAndAccept(); 454 | return; 455 | } 456 | if (event.key().checkKeyList(*config_.synchronize)) { 457 | sync(/*userTriggered=*/true); 458 | event.filterAndAccept(); 459 | return; 460 | } 461 | } 462 | auto *state = this->state(inputContext); 463 | currentKeyEventState_ = state; 464 | state->keyEvent(event); 465 | currentKeyEventState_ = nullptr; 466 | } 467 | 468 | void RimeEngine::reset(const InputMethodEntry & /*entry*/, 469 | InputContextEvent &event) { 470 | auto *inputContext = event.inputContext(); 471 | auto *state = this->state(inputContext); 472 | state->clear(); 473 | instance_->resetCompose(inputContext); 474 | inputContext->inputPanel().reset(); 475 | inputContext->updatePreedit(); 476 | inputContext->updateUserInterface(UserInterfaceComponent::InputPanel); 477 | } 478 | 479 | void RimeEngine::allowNotification(std::string type) { 480 | allowNotificationUntil_ = now(CLOCK_MONOTONIC) + NotificationTimeout; 481 | allowNotificationType_ = std::move(type); 482 | } 483 | 484 | void RimeEngine::save() { sync(/*userTriggered=*/false); } 485 | 486 | void RimeEngine::rimeNotificationHandler(void *context, RimeSessionId session, 487 | const char *messageType, 488 | const char *messageValue) { 489 | RIME_DEBUG() << "Notification: " << session << " " << messageType << " " 490 | << messageValue; 491 | auto *that = static_cast(context); 492 | if (that->mainThreadId_ == std::this_thread::get_id()) { 493 | that->notifyImmediately(session, messageType, messageValue); 494 | } 495 | that->eventDispatcher_.schedule( 496 | [that, session, messageType = std::string(messageType), 497 | messageValue = std::string(messageValue)]() { 498 | that->notify(session, messageType, messageValue); 499 | }); 500 | } 501 | 502 | void RimeEngine::notifyImmediately(RimeSessionId session, 503 | std::string_view messageType, 504 | std::string_view messageValue) { 505 | if (messageType != "option") { 506 | return; 507 | } 508 | if (!currentKeyEventState_ || 509 | currentKeyEventState_->session(false) != session) { 510 | return; 511 | } 512 | currentKeyEventState_->addChangedOption(messageValue); 513 | } 514 | 515 | void RimeEngine::notify(RimeSessionId session, const std::string &messageType, 516 | const std::string &messageValue) { 517 | const char *message = nullptr; 518 | const char *icon = ""; 519 | const char *tipId = ""; 520 | const int timeout = 3000; 521 | bool blockMessage = false; 522 | if (messageType == "deploy") { 523 | tipId = "fcitx-rime-deploy"; 524 | icon = "fcitx_rime_deploy"; 525 | if (messageValue == "start") { 526 | message = _("Rime is under maintenance. It may take a few " 527 | "seconds. Please wait until it is finished..."); 528 | } else if (messageValue == "success") { 529 | message = _("Rime is ready."); 530 | if (!api_->is_maintenance_mode()) { 531 | if (needRefreshAppOption_) { 532 | api_->deploy_config_file("fcitx5.yaml", "config_version"); 533 | updateAppOptions(); 534 | needRefreshAppOption_ = false; 535 | } 536 | } 537 | updateSchemaMenu(); 538 | refreshStatusArea(0); 539 | blockMessage = true; 540 | } else if (messageValue == "failure") { 541 | needRefreshAppOption_ = false; 542 | message = _("Rime has encountered an error. " 543 | "See log for details."); 544 | blockMessage = true; 545 | } 546 | } else if (messageType == "option") { 547 | updateStatusArea(session); 548 | } else if (messageType == "schema") { 549 | // Schema is changed either via status area or shortcut 550 | refreshStatusArea(session); 551 | } 552 | 553 | auto *notifications = this->notifications(); 554 | const auto current = now(CLOCK_MONOTONIC); 555 | if (message && notifications && current > silenceNotificationUntil_ && 556 | (current < allowNotificationUntil_ && 557 | (allowNotificationType_.empty() || 558 | messageType == allowNotificationType_))) { 559 | notifications->call( 560 | tipId, _("Rime"), icon, _("Rime"), message, timeout); 561 | } 562 | // Block message after error / success. 563 | if (blockMessage) { 564 | silenceNotificationUntil_ = current + 30000; 565 | } 566 | } 567 | 568 | RimeState *RimeEngine::state(InputContext *ic) { 569 | if (!factory_.registered()) { 570 | return nullptr; 571 | } 572 | return ic->propertyFor(&factory_); 573 | } 574 | 575 | std::string RimeEngine::subMode(const InputMethodEntry & /*entry*/, 576 | InputContext &ic) { 577 | if (auto *rimeState = state(&ic)) { 578 | return rimeState->subMode(); 579 | } 580 | return ""; 581 | } 582 | 583 | std::string RimeEngine::subModeLabelImpl(const InputMethodEntry & /*unused*/, 584 | InputContext &ic) { 585 | if (auto *rimeState = state(&ic)) { 586 | return rimeState->subModeLabel(); 587 | } 588 | return ""; 589 | } 590 | 591 | std::string RimeEngine::subModeIconImpl(const InputMethodEntry & /*unused*/, 592 | InputContext &ic) { 593 | std::string result = "fcitx-rime"; 594 | if (!factory_.registered()) { 595 | return result; 596 | } 597 | auto *state = this->state(&ic); 598 | if (state) { 599 | state->getStatus([&result](const RimeStatus &status) { 600 | if (status.is_disabled) { 601 | result = "fcitx_rime_disable"; 602 | } else if (status.is_ascii_mode) { 603 | result = "fcitx_rime_latin"; 604 | } else { 605 | result = "fcitx-rime"; 606 | } 607 | }); 608 | } 609 | return result; 610 | } 611 | 612 | void RimeEngine::releaseAllSession(bool snapshot) { 613 | instance_->inputContextManager().foreach([&](InputContext *ic) { 614 | if (auto *state = this->state(ic)) { 615 | if (snapshot) { 616 | state->snapshot(); 617 | } 618 | state->release(); 619 | } 620 | return true; 621 | }); 622 | } 623 | 624 | void RimeEngine::deploy() { 625 | RIME_DEBUG() << "Rime Deploy"; 626 | releaseAllSession(true); 627 | api_->finalize(); 628 | allowNotification(); 629 | rimeStart(true); 630 | } 631 | 632 | void RimeEngine::sync(bool userTriggered) { 633 | RIME_DEBUG() << "Rime Sync user data"; 634 | releaseAllSession(true); 635 | if (userTriggered) { 636 | allowNotification(); 637 | } 638 | api_->sync_user_data(); 639 | } 640 | 641 | void RimeEngine::updateActionsForSchema(const std::string &schema) { 642 | RimeConfig config{}; 643 | 644 | if (!api_->schema_open(schema.c_str(), &config)) { 645 | return; 646 | } 647 | auto switchPaths = getListItemPath(api_, &config, "switches"); 648 | for (const auto &switchPath : switchPaths) { 649 | auto labels = getListItemString(api_, &config, switchPath + "/states"); 650 | if (labels.size() <= 1) { 651 | continue; 652 | } 653 | auto namePath = switchPath + "/name"; 654 | const auto *name = api_->config_get_cstring(&config, namePath.c_str()); 655 | if (name) { 656 | if (labels.size() != 2) { 657 | continue; 658 | } 659 | std::string optionName = name; 660 | if (optionName == RIME_ASCII_MODE) { 661 | // imAction_ has latin mode that does the same 662 | continue; 663 | } 664 | 665 | optionActions_[schema].emplace_back(std::make_unique( 666 | this, schema, optionName, labels[0], labels[1])); 667 | } else { 668 | auto options = 669 | getListItemString(api_, &config, switchPath + "/options"); 670 | if (labels.size() != options.size()) { 671 | continue; 672 | } 673 | optionActions_[schema].emplace_back( 674 | std::make_unique(this, schema, options, labels)); 675 | } 676 | } 677 | api_->config_close(&config); 678 | } 679 | 680 | void RimeEngine::updateSchemaMenu() { 681 | schemas_.clear(); 682 | schemActions_.clear(); 683 | optionActions_.clear(); 684 | RimeSchemaList list; 685 | list.size = 0; 686 | if (api_->get_schema_list(&list)) { 687 | schemActions_.emplace_back(); 688 | 689 | schemActions_.back().setShortText(_("Latin Mode")); 690 | schemActions_.back().connect( 691 | [this](InputContext *ic) { 692 | auto *state = this->state(ic); 693 | state->toggleLatinMode(); 694 | imAction_->update(ic); 695 | }); 696 | instance_->userInterfaceManager().registerAction(&schemActions_.back()); 697 | schemaMenu_.insertAction(&separatorAction_, &schemActions_.back()); 698 | for (size_t i = 0; i < list.size; i++) { 699 | schemActions_.emplace_back(); 700 | std::string schemaId = list.list[i].schema_id; 701 | auto &schemaAction = schemActions_.back(); 702 | schemaAction.setShortText(list.list[i].name); 703 | schemaAction.connect( 704 | [this, schemaId](InputContext *ic) { 705 | auto *state = this->state(ic); 706 | state->selectSchema(schemaId); 707 | imAction_->update(ic); 708 | }); 709 | instance_->userInterfaceManager().registerAction(&schemaAction); 710 | schemaMenu_.insertAction(&separatorAction_, &schemaAction); 711 | updateActionsForSchema(schemaId); 712 | schemas_.insert(schemaId); 713 | } 714 | api_->free_schema_list(&list); 715 | } 716 | } 717 | 718 | void RimeEngine::refreshSessionPoolPolicy() { 719 | auto newPolicy = getSharedStatePolicy(); 720 | if (sessionPool_.propertyPropagatePolicy() != newPolicy) { 721 | releaseAllSession(constructed_); 722 | sessionPool_.setPropertyPropagatePolicy(newPolicy); 723 | } 724 | } 725 | 726 | PropertyPropagatePolicy RimeEngine::getSharedStatePolicy() { 727 | switch (*config_.sharedStatePolicy) { 728 | case SharedStatePolicy::All: 729 | return PropertyPropagatePolicy::All; 730 | case SharedStatePolicy::Program: 731 | return PropertyPropagatePolicy::Program; 732 | case SharedStatePolicy::No: 733 | return PropertyPropagatePolicy::No; 734 | case SharedStatePolicy::FollowGlobalConfig: 735 | default: 736 | return instance_->globalConfig().shareInputState(); 737 | } 738 | } 739 | 740 | } // namespace fcitx::rime 741 | -------------------------------------------------------------------------------- /src/rimeengine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2017~2017 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | #ifndef _FCITX_RIMEENGINE_H_ 7 | #define _FCITX_RIMEENGINE_H_ 8 | 9 | #include "rimesession.h" 10 | #include "rimestate.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifndef FCITX_RIME_NO_DBUS 47 | #include "rimeservice.h" 48 | #endif 49 | 50 | namespace fcitx::rime { 51 | 52 | class RimeState; 53 | class RimeOptionAction; 54 | 55 | enum class SharedStatePolicy { FollowGlobalConfig, All, Program, No }; 56 | 57 | FCITX_CONFIG_ENUM_NAME_WITH_I18N(SharedStatePolicy, 58 | N_("Follow Global Configuration"), N_("All"), 59 | N_("Program"), N_("No")); 60 | 61 | enum class PreeditMode { No, ComposingText, CommitPreview }; 62 | 63 | FCITX_CONFIG_ENUM_NAME_WITH_I18N(PreeditMode, N_("Do not show"), 64 | N_("Composing text"), N_("Commit preview")) 65 | 66 | enum class SwitchInputMethodBehavior { 67 | Clear, 68 | CommitRawInput, 69 | CommitComposingText, 70 | CommitCommitPreview 71 | }; 72 | 73 | FCITX_CONFIG_ENUM_NAME_WITH_I18N(SwitchInputMethodBehavior, N_("Clear"), 74 | N_("Commit raw input"), 75 | N_("Commit composing text"), 76 | N_("Commit commit preview")) 77 | 78 | FCITX_CONFIGURATION( 79 | RimeEngineConfig, 80 | OptionWithAnnotation preeditMode{ 81 | this, "PreeditMode", _("Preedit Mode"), 82 | isAndroid() ? PreeditMode::No : PreeditMode::ComposingText}; 83 | OptionWithAnnotation 84 | sharedStatePolicy{this, "InputState", _("Shared Input State"), 85 | SharedStatePolicy::All}; 86 | // On Linux only cursor position is available so this pins candidate window 87 | // while typing. On macOS any position within embedded preedit is available 88 | // so this is unnecessary. On Android there is no candidate window yet. 89 | Option preeditCursorPositionAtBeginning{ 90 | this, "PreeditCursorPositionAtBeginning", 91 | _("Fix embedded preedit cursor at the beginning of the preedit"), 92 | !isAndroid() && !isApple() && !isEmscripten()}; 93 | OptionWithAnnotation 95 | switchInputMethodBehavior{ 96 | this, "SwitchInputMethodBehavior", 97 | _("Action when switching input method"), 98 | SwitchInputMethodBehavior::CommitCommitPreview}; 99 | ExternalOption userDataDir{ 100 | this, "UserDataDir", _("User data dir"), 101 | stringutils::concat( 102 | "xdg-open \"", 103 | stringutils::replaceAll((StandardPaths::global().userDirectory( 104 | StandardPathsType::PkgData) / 105 | "rime") 106 | .string(), 107 | "\"", "\"\"\""), 108 | "\"")}; 109 | fcitx::Option deploy{ 110 | this, "Deploy", _("Deploy"), 111 | isApple() ? fcitx::KeyList{fcitx::Key("Control+Alt+grave")} 112 | : fcitx::KeyList{}}; 113 | fcitx::Option synchronize{ 114 | this, "Synchronize", _("Synchronize"), {}};); 115 | 116 | class RimeEngine final : public InputMethodEngineV2 { 117 | public: 118 | RimeEngine(Instance *instance); 119 | ~RimeEngine(); 120 | Instance *instance() { return instance_; } 121 | void activate(const InputMethodEntry &entry, 122 | InputContextEvent &event) override; 123 | void deactivate(const InputMethodEntry &entry, 124 | InputContextEvent &event) override; 125 | void keyEvent(const InputMethodEntry &entry, KeyEvent &keyEvent) override; 126 | void reloadConfig() override; 127 | void reset(const InputMethodEntry &entry, 128 | InputContextEvent &event) override; 129 | void save() override; 130 | auto &factory() { return factory_; } 131 | 132 | const Configuration *getConfig() const override { return &config_; } 133 | void setConfig(const RawConfig &config) override { 134 | config_.load(config, true); 135 | safeSaveAsIni(config_, "conf/rime.conf"); 136 | updateConfig(); 137 | } 138 | void setSubConfig(const std::string &path, 139 | const RawConfig & /*unused*/) override; 140 | void updateConfig(); 141 | 142 | std::string subMode(const InputMethodEntry & /*entry*/, 143 | InputContext & /*inputContext*/) override; 144 | std::string subModeIconImpl(const InputMethodEntry & /*unused*/, 145 | InputContext & /*unused*/) override; 146 | std::string subModeLabelImpl(const InputMethodEntry & /*unused*/, 147 | InputContext & /*unused*/) override; 148 | const RimeEngineConfig &config() const { return config_; } 149 | 150 | rime_api_t *api() { return api_; } 151 | const auto &appOptions() const { return appOptions_; } 152 | 153 | void rimeStart(bool fullcheck); 154 | 155 | RimeState *state(InputContext *ic); 156 | RimeSessionPool &sessionPool() { return sessionPool_; } 157 | 158 | #ifndef FCITX_RIME_NO_DBUS 159 | FCITX_ADDON_DEPENDENCY_LOADER(dbus, instance_->addonManager()); 160 | #endif 161 | 162 | void allowNotification(std::string type = ""); 163 | const auto &schemas() const { return schemas_; } 164 | const auto &optionActions() const { return optionActions_; }; 165 | 166 | private: 167 | static void rimeNotificationHandler(void *context, RimeSessionId session, 168 | const char *messageTypee, 169 | const char *messageValue); 170 | 171 | void deploy(); 172 | void sync(bool userTriggered); 173 | void updateSchemaMenu(); 174 | void updateActionsForSchema(const std::string &schema); 175 | void notifyImmediately(RimeSessionId session, std::string_view type, 176 | std::string_view value); 177 | void notify(RimeSessionId session, const std::string &type, 178 | const std::string &value); 179 | void releaseAllSession(bool snapshot = false); 180 | void updateAppOptions(); 181 | void refreshStatusArea(InputContext &ic); 182 | void refreshStatusArea(RimeSessionId session); 183 | void updateStatusArea(RimeSessionId session); 184 | void refreshSessionPoolPolicy(); 185 | PropertyPropagatePolicy getSharedStatePolicy(); 186 | 187 | bool constructed_ = false; 188 | std::string sharedDataDir_; 189 | IconTheme theme_; 190 | Instance *instance_; 191 | EventDispatcher eventDispatcher_; 192 | rime_api_t *api_; 193 | static bool firstRun_; 194 | uint64_t silenceNotificationUntil_ = 0; 195 | uint64_t allowNotificationUntil_ = 0; 196 | std::string allowNotificationType_; 197 | FactoryFor factory_; 198 | bool needRefreshAppOption_ = false; 199 | 200 | std::unique_ptr imAction_; 201 | SimpleAction separatorAction_; 202 | SimpleAction deployAction_; 203 | SimpleAction syncAction_; 204 | 205 | RimeEngineConfig config_; 206 | std::unordered_map> 207 | appOptions_; 208 | 209 | FCITX_ADDON_DEPENDENCY_LOADER(notifications, instance_->addonManager()); 210 | 211 | std::unordered_set schemas_; 212 | std::list schemActions_; 213 | std::unordered_map>> 215 | optionActions_; 216 | Menu schemaMenu_; 217 | std::unique_ptr> globalConfigReloadHandle_; 218 | 219 | #ifndef FCITX_RIME_NO_DBUS 220 | RimeService service_{this}; 221 | #endif 222 | RimeSessionPool sessionPool_; 223 | std::thread::id mainThreadId_ = std::this_thread::get_id(); 224 | RimeState *currentKeyEventState_ = nullptr; 225 | }; 226 | } // namespace fcitx::rime 227 | 228 | FCITX_DECLARE_LOG_CATEGORY(rime_log); 229 | 230 | #define RIME_DEBUG() FCITX_LOGC(rime_log, Debug) 231 | #define RIME_ERROR() FCITX_LOGC(rime_log, Error) 232 | 233 | #endif // _FCITX_RIMEENGINE_H_ 234 | -------------------------------------------------------------------------------- /src/rimefactory.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024-2024 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #include "rimefactory.h" 8 | #include "rimeengine.h" 9 | #include 10 | 11 | namespace fcitx::rime { 12 | 13 | AddonInstance *RimeEngineFactory::create(AddonManager *manager) { 14 | registerDomain("fcitx5-rime", FCITX_INSTALL_LOCALEDIR); 15 | return new RimeEngine(manager->instance()); 16 | } 17 | 18 | } // namespace fcitx::rime 19 | 20 | FCITX_ADDON_FACTORY_V2(rime, fcitx::rime::RimeEngineFactory) 21 | -------------------------------------------------------------------------------- /src/rimefactory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2024-2024 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | #ifndef _FCITX_RIMEFACTORY_H_ 7 | #define _FCITX_RIMEFACTORY_H_ 8 | 9 | #include 10 | 11 | namespace fcitx::rime { 12 | 13 | class RimeEngineFactory : public AddonFactory { 14 | public: 15 | AddonInstance *create(AddonManager *manager) override; 16 | }; 17 | 18 | } // namespace fcitx::rime 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/rimeservice.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021~2021 CSSlayer 3 | * 4 | * SPDX-License-Identifier: GPL-2.0-or-later 5 | * 6 | */ 7 | #include "rimeservice.h" 8 | #include "dbus_public.h" 9 | #include "rimeengine.h" 10 | #include "rimestate.h" 11 | 12 | namespace fcitx::rime { 13 | 14 | RimeService::RimeService(RimeEngine *engine) : engine_(engine) { 15 | auto dbus = engine->dbus(); 16 | if (!dbus) { 17 | return; 18 | } 19 | auto bus = dbus->call(); 20 | bus->addObjectVTable("/rime", "org.fcitx.Fcitx.Rime1", *this); 21 | } 22 | 23 | RimeState *RimeService::currentState() { 24 | auto ic = engine_->instance()->mostRecentInputContext(); 25 | if (!ic) { 26 | return nullptr; 27 | } 28 | return engine_->state(ic); 29 | } 30 | 31 | void RimeService::setAsciiMode(bool ascii) { 32 | if (auto *state = currentState()) { 33 | state->setLatinMode(ascii); 34 | if (auto *ic = engine_->instance()->mostRecentInputContext(); 35 | ic && ic->hasFocus()) { 36 | engine_->instance()->showInputMethodInformation(ic); 37 | } 38 | } 39 | } 40 | 41 | bool RimeService::isAsciiMode() { 42 | bool isAscii = false; 43 | if (auto *state = currentState()) { 44 | state->getStatus([&isAscii](const RimeStatus &status) { 45 | isAscii = status.is_ascii_mode; 46 | }); 47 | } 48 | return isAscii; 49 | } 50 | 51 | void RimeService::setSchema(const std::string &schema) { 52 | if (auto state = currentState()) { 53 | state->selectSchema(schema); 54 | if (auto ic = engine_->instance()->mostRecentInputContext(); 55 | ic && ic->hasFocus()) { 56 | engine_->instance()->showInputMethodInformation(ic); 57 | } 58 | } 59 | } 60 | 61 | std::string RimeService::currentSchema() { 62 | std::string result; 63 | auto state = currentState(); 64 | if (state) { 65 | state->getStatus([&result](const RimeStatus &status) { 66 | result = status.schema_id ? status.schema_id : ""; 67 | }); 68 | } 69 | return result; 70 | } 71 | 72 | std::vector RimeService::listAllSchemas() { 73 | std::vector schemas; 74 | if (auto api = engine_->api()) { 75 | RimeSchemaList list; 76 | list.size = 0; 77 | if (api->get_schema_list(&list)) { 78 | for (size_t i = 0; i < list.size; i++) { 79 | schemas.emplace_back(list.list[i].schema_id); 80 | } 81 | api->free_schema_list(&list); 82 | } 83 | } 84 | return schemas; 85 | } 86 | 87 | } // namespace fcitx::rime 88 | -------------------------------------------------------------------------------- /src/rimeservice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021~2021 CSSlayer 3 | * 4 | * SPDX-License-Identifier: GPL-2.0-or-later 5 | * 6 | */ 7 | #ifndef _FCITX5_RIME_RIMESERVICE_H_ 8 | #define _FCITX5_RIME_RIMESERVICE_H_ 9 | 10 | #include 11 | #include 12 | 13 | namespace fcitx::rime { 14 | 15 | class RimeEngine; 16 | class RimeState; 17 | 18 | class RimeService : public dbus::ObjectVTable { 19 | public: 20 | RimeService(RimeEngine *engine); 21 | 22 | void setAsciiMode(bool asciiMode); 23 | bool isAsciiMode(); 24 | void setSchema(const std::string &schema); 25 | std::string currentSchema(); 26 | std::vector listAllSchemas(); 27 | 28 | private: 29 | RimeState *currentState(); 30 | FCITX_OBJECT_VTABLE_METHOD(setAsciiMode, "SetAsciiMode", "b", ""); 31 | FCITX_OBJECT_VTABLE_METHOD(isAsciiMode, "IsAsciiMode", "", "b"); 32 | FCITX_OBJECT_VTABLE_METHOD(setSchema, "SetSchema", "s", ""); 33 | FCITX_OBJECT_VTABLE_METHOD(currentSchema, "GetCurrentSchema", "", "s"); 34 | FCITX_OBJECT_VTABLE_METHOD(listAllSchemas, "ListAllSchemas", "", "as"); 35 | 36 | RimeEngine *engine_; 37 | }; 38 | 39 | } // namespace fcitx::rime 40 | 41 | #endif // _FCITX5_RIME_RIMESERVICE_H_ 42 | -------------------------------------------------------------------------------- /src/rimesession.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021~2021 CSSlayer 3 | * 4 | * SPDX-License-Identifier: GPL-2.0-or-later 5 | * 6 | */ 7 | #include "rimesession.h" 8 | #include "rimeengine.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace fcitx::rime { 24 | 25 | RimeSessionHolder::RimeSessionHolder(RimeSessionPool *pool, 26 | const std::string &program) 27 | : pool_(pool) { 28 | auto *api = pool_->engine()->api(); 29 | id_ = api->create_session(); 30 | 31 | if (!id_) { 32 | throw std::runtime_error("Failed to create session."); 33 | } 34 | 35 | setProgramName(program); 36 | 37 | if (program.empty()) { 38 | return; 39 | } 40 | 41 | const auto &appOptions = pool_->engine()->appOptions(); 42 | if (auto iter = appOptions.find(program); iter != appOptions.end()) { 43 | RIME_DEBUG() << "Apply app options to " << program << ": " 44 | << iter->second; 45 | for (const auto &[key, value] : iter->second) { 46 | api->set_option(id_, key.data(), value); 47 | } 48 | } 49 | } 50 | 51 | RimeSessionHolder::~RimeSessionHolder() { 52 | if (id_) { 53 | pool_->engine()->api()->destroy_session(id_); 54 | } 55 | if (!key_.empty()) { 56 | pool_->unregisterSession(key_); 57 | } 58 | } 59 | 60 | void RimeSessionHolder::setProgramName(const std::string &program) { 61 | // set_property will trigger property change notification, which is a little 62 | // bit annoying, so don't set it, if the value is not changed. 63 | if (program == currentProgram_) { 64 | return; 65 | } 66 | 67 | currentProgram_ = program; 68 | pool_->engine()->api()->set_property(id_, "client_app", program.data()); 69 | } 70 | 71 | #if 0 72 | LogMessageBuilder &operator<<(LogMessageBuilder &log, const std::weak_ptr &session) { 73 | auto sessionPtr = session.lock(); 74 | log << "RimeSession("<< (sessionPtr ? std::to_string(sessionPtr->id()) : "null") << ")"; 75 | return log; 76 | } 77 | #endif 78 | 79 | RimeSessionPool::RimeSessionPool(RimeEngine *engine, 80 | PropertyPropagatePolicy initialPolicy) 81 | : engine_(engine), policy_(initialPolicy) {} 82 | 83 | void RimeSessionPool::setPropertyPropagatePolicy( 84 | PropertyPropagatePolicy policy) { 85 | if (policy_ == policy) { 86 | return; 87 | } 88 | 89 | assert(sessions_.empty()); 90 | policy_ = policy; 91 | } 92 | 93 | std::string uuidKey(InputContext *ic) { 94 | std::string key = "u:"; 95 | for (auto v : ic->uuid()) { 96 | auto lower = v % 16; 97 | auto upper = v / 16; 98 | key.push_back(charutils::toHex(upper)); 99 | key.push_back(charutils::toHex(lower)); 100 | } 101 | return key; 102 | } 103 | 104 | std::tuple, bool> 105 | RimeSessionPool::requestSession(InputContext *ic) { 106 | std::string key; 107 | switch (policy_) { 108 | case PropertyPropagatePolicy::No: 109 | key = uuidKey(ic); 110 | break; 111 | case PropertyPropagatePolicy::Program: 112 | if (!ic->program().empty()) { 113 | key = stringutils::concat("p:", ic->program()); 114 | } else { 115 | key = uuidKey(ic); 116 | } 117 | break; 118 | case PropertyPropagatePolicy::All: 119 | key = "g:"; 120 | break; 121 | } 122 | auto iter = sessions_.find(key); 123 | if (iter != sessions_.end()) { 124 | return {iter->second.lock(), false}; 125 | } 126 | try { 127 | auto newSession = 128 | std::make_shared(this, ic->program()); 129 | registerSession(key, newSession); 130 | return {newSession, true}; 131 | } catch (...) { 132 | } 133 | return {nullptr, false}; 134 | } 135 | 136 | void RimeSessionPool::registerSession( 137 | const std::string &key, std::shared_ptr session) { 138 | assert(!key.empty()); 139 | session->key_ = key; 140 | auto [_, success] = sessions_.emplace(key, session); 141 | FCITX_UNUSED(success); 142 | assert(success); 143 | } 144 | 145 | void RimeSessionPool::unregisterSession(const std::string &key) { 146 | auto count = sessions_.erase(key); 147 | FCITX_UNUSED(count); 148 | assert(count > 0); 149 | } 150 | 151 | } // namespace fcitx::rime 152 | -------------------------------------------------------------------------------- /src/rimesession.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2021~2021 CSSlayer 3 | * 4 | * SPDX-License-Identifier: GPL-2.0-or-later 5 | * 6 | */ 7 | #ifndef _FCITX5_RIME_RIMESESSION_H_ 8 | #define _FCITX5_RIME_RIMESESSION_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace fcitx::rime { 21 | 22 | class RimeEngine; 23 | class RimeSessionPool; 24 | 25 | class RimeSessionHolder { 26 | friend class RimeSessionPool; 27 | 28 | public: 29 | RimeSessionHolder(RimeSessionPool *pool, const std::string &program); 30 | 31 | RimeSessionHolder(RimeSessionHolder &&) = delete; 32 | 33 | ~RimeSessionHolder(); 34 | 35 | RimeSessionId id() const { return id_; } 36 | 37 | void setProgramName(const std::string &program); 38 | 39 | private: 40 | RimeSessionPool *pool_; 41 | RimeSessionId id_ = 0; 42 | std::string key_; 43 | std::string currentProgram_; 44 | }; 45 | 46 | class RimeSessionPool { 47 | friend class RimeSessionHolder; 48 | 49 | public: 50 | RimeSessionPool(RimeEngine *engine, PropertyPropagatePolicy initialPolicy); 51 | 52 | PropertyPropagatePolicy propertyPropagatePolicy() const { return policy_; } 53 | void setPropertyPropagatePolicy(PropertyPropagatePolicy policy); 54 | 55 | std::tuple, bool> 56 | requestSession(InputContext *ic); 57 | 58 | RimeEngine *engine() const { return engine_; } 59 | 60 | private: 61 | void registerSession(const std::string &key, 62 | std::shared_ptr session); 63 | void unregisterSession(const std::string &key); 64 | RimeEngine *engine_; 65 | PropertyPropagatePolicy policy_; 66 | std::unordered_map> sessions_; 67 | }; 68 | 69 | } // namespace fcitx::rime 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/rimestate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2017~2017 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | 7 | #include "rimestate.h" 8 | #include "rimeaction.h" 9 | #include "rimecandidate.h" 10 | #include "rimeengine.h" 11 | #include "rimesession.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | namespace fcitx::rime { 42 | 43 | namespace { 44 | 45 | bool emptyExceptAux(const InputPanel &inputPanel) { 46 | 47 | return inputPanel.preedit().empty() && inputPanel.preedit().empty() && 48 | (!inputPanel.candidateList() || inputPanel.candidateList()->empty()); 49 | } 50 | 51 | } // namespace 52 | 53 | RimeState::RimeState(RimeEngine *engine, InputContext &ic) 54 | : engine_(engine), ic_(ic) {} 55 | 56 | RimeState::~RimeState() {} 57 | 58 | RimeSessionId RimeState::session(bool requestNewSession) { 59 | if (!session_ && requestNewSession) { 60 | auto [sessionHolder, isNewSession] = 61 | engine_->sessionPool().requestSession(&ic_); 62 | session_ = sessionHolder; 63 | if (isNewSession) { 64 | restore(); 65 | } else { 66 | savedCurrentSchema_.clear(); 67 | savedOptions_.clear(); 68 | } 69 | } 70 | if (!session_) { 71 | return 0; 72 | } 73 | 74 | return session_->id(); 75 | } 76 | 77 | void RimeState::clear() { 78 | if (auto session = this->session()) { 79 | engine_->api()->clear_composition(session); 80 | } 81 | } 82 | 83 | void RimeState::activate() { maybeSyncProgramNameToSession(); } 84 | 85 | std::string RimeState::subMode() { 86 | std::string result; 87 | getStatus([&result](const RimeStatus &status) { 88 | if (status.is_disabled) { 89 | result = "\xe2\x8c\x9b"; 90 | } else if (status.is_ascii_mode) { 91 | result = _("Latin Mode"); 92 | } else if (status.schema_name && status.schema_name[0] != '.') { 93 | result = status.schema_name; 94 | } 95 | }); 96 | return result; 97 | } 98 | 99 | std::string RimeState::subModeLabel() { 100 | std::string result; 101 | getStatus([&result](const RimeStatus &status) { 102 | if (status.is_disabled) { 103 | result = ""; 104 | } else if (status.is_ascii_mode) { 105 | result = "A"; 106 | } else if (status.schema_name && status.schema_name[0] != '.') { 107 | result = status.schema_name; 108 | if (!result.empty() && 109 | utf8::lengthValidated(result) != utf8::INVALID_LENGTH) { 110 | result = result.substr( 111 | 0, std::distance(result.begin(), 112 | utf8::nextChar(result.begin()))); 113 | } 114 | } 115 | }); 116 | return result; 117 | } 118 | 119 | std::string RimeState::currentSchema() { 120 | std::string schema; 121 | getStatus([&schema](const RimeStatus &status) { 122 | if (status.schema_id) { 123 | schema = status.schema_id; 124 | } 125 | }); 126 | return schema; 127 | } 128 | 129 | void RimeState::toggleLatinMode() { 130 | auto *api = engine_->api(); 131 | if (api->is_maintenance_mode()) { 132 | return; 133 | } 134 | 135 | Bool oldValue = api->get_option(session(), RIME_ASCII_MODE); 136 | api->set_option(session(), RIME_ASCII_MODE, !oldValue); 137 | } 138 | 139 | void RimeState::setLatinMode(bool latin) { 140 | auto *api = engine_->api(); 141 | if (api->is_maintenance_mode()) { 142 | return; 143 | } 144 | api->set_option(session(), RIME_ASCII_MODE, latin); 145 | } 146 | 147 | void RimeState::selectSchema(const std::string &schema) { 148 | auto *api = engine_->api(); 149 | if (api->is_maintenance_mode()) { 150 | return; 151 | } 152 | api->set_option(session(), RIME_ASCII_MODE, false); 153 | api->select_schema(session(), schema.data()); 154 | } 155 | 156 | void RimeState::keyEvent(KeyEvent &event) { 157 | changedOptions_.clear(); 158 | auto *ic = event.inputContext(); 159 | // For key-release, composeResult will always be empty string, which feed 160 | // into engine directly. 161 | std::string composeResult; 162 | if (!event.key().states().testAny( 163 | KeyStates{KeyState::Ctrl, KeyState::Super}) && 164 | !event.isRelease()) { 165 | auto compose = 166 | engine_->instance()->processComposeString(&ic_, event.key().sym()); 167 | if (!compose) { 168 | event.filterAndAccept(); 169 | return; 170 | } 171 | composeResult = *compose; 172 | } 173 | 174 | auto *api = engine_->api(); 175 | if (api->is_maintenance_mode()) { 176 | return; 177 | } 178 | auto session = this->session(); 179 | if (!session) { 180 | return; 181 | } 182 | 183 | maybeSyncProgramNameToSession(); 184 | lastMode_ = subMode(); 185 | 186 | std::string lastSchema = currentSchema(); 187 | auto states = event.rawKey().states() & 188 | KeyStates{KeyState::Mod1, KeyState::CapsLock, KeyState::Shift, 189 | KeyState::Ctrl, KeyState::Super}; 190 | if (states.test(KeyState::Super)) { 191 | // IBus uses virtual super mask. 192 | states |= KeyState::Super2; 193 | } 194 | uint32_t intStates = states; 195 | if (event.isRelease()) { 196 | // IBUS_RELEASE_MASK 197 | intStates |= (1 << 30); 198 | } 199 | if (!composeResult.empty()) { 200 | event.filterAndAccept(); 201 | auto length = utf8::lengthValidated(composeResult); 202 | bool result = false; 203 | if (length == 1) { 204 | auto c = utf8::getChar(composeResult); 205 | auto sym = Key::keySymFromUnicode(c); 206 | if (sym != FcitxKey_None) { 207 | result = api->process_key(session, sym, intStates); 208 | } 209 | } 210 | if (!result) { 211 | commitPreedit(ic); 212 | ic->commitString(composeResult); 213 | clear(); 214 | } 215 | } else { 216 | auto result = 217 | api->process_key(session, event.rawKey().sym(), intStates); 218 | if (result) { 219 | event.filterAndAccept(); 220 | } 221 | } 222 | 223 | RIME_STRUCT(RimeCommit, commit); 224 | if (api->get_commit(session, &commit)) { 225 | ic->commitString(commit.text); 226 | api->free_commit(&commit); 227 | engine_->instance()->resetCompose(ic); 228 | } 229 | 230 | updateUI(ic, event.isRelease()); 231 | if (!event.isRelease() && !lastSchema.empty() && 232 | lastSchema == currentSchema() && ic->inputPanel().empty() && 233 | !changedOptions_.empty()) { 234 | showChangedOptions(); 235 | } 236 | } 237 | 238 | void RimeState::selectCandidate(InputContext *inputContext, int idx, 239 | bool global) { 240 | auto *api = engine_->api(); 241 | if (api->is_maintenance_mode()) { 242 | return; 243 | } 244 | auto session = this->session(); 245 | if (!session) { 246 | return; 247 | } 248 | if (global) { 249 | api->select_candidate(session, idx); 250 | } else { 251 | api->select_candidate_on_current_page(session, idx); 252 | } 253 | RIME_STRUCT(RimeCommit, commit); 254 | if (api->get_commit(session, &commit)) { 255 | inputContext->commitString(commit.text); 256 | api->free_commit(&commit); 257 | } 258 | updateUI(inputContext, false); 259 | } 260 | 261 | #ifndef FCITX_RIME_NO_DELETE_CANDIDATE 262 | void RimeState::deleteCandidate(int idx, bool global) { 263 | auto *api = engine_->api(); 264 | if (api->is_maintenance_mode()) { 265 | return; 266 | } 267 | auto session = this->session(); 268 | if (!session) { 269 | return; 270 | } 271 | if (global) { 272 | api->delete_candidate(session, idx); 273 | } else { 274 | api->delete_candidate_on_current_page(session, idx); 275 | } 276 | updateUI(&ic_, false); 277 | } 278 | #endif 279 | 280 | bool RimeState::getStatus( 281 | const std::function &callback) { 282 | auto *api = engine_->api(); 283 | auto session = this->session(); 284 | if (!session) { 285 | return false; 286 | } 287 | RIME_STRUCT(RimeStatus, status); 288 | if (!api->get_status(session, &status)) { 289 | return false; 290 | } 291 | callback(status); 292 | api->free_status(&status); 293 | return true; 294 | } 295 | 296 | Text preeditFromRimeContext(const RimeContext &context, TextFormatFlags flag, 297 | TextFormatFlags highlightFlag) { 298 | Text preedit; 299 | 300 | do { 301 | if (context.composition.length == 0) { 302 | break; 303 | } 304 | 305 | // validation. 306 | if (!(context.composition.sel_start >= 0 && 307 | context.composition.sel_start <= context.composition.sel_end && 308 | context.composition.sel_end <= context.composition.length)) { 309 | break; 310 | } 311 | 312 | /* converted text */ 313 | if (context.composition.sel_start > 0) { 314 | preedit.append(std::string(context.composition.preedit, 315 | context.composition.sel_start), 316 | flag); 317 | } 318 | 319 | /* converting candidate */ 320 | if (context.composition.sel_start < context.composition.sel_end) { 321 | preedit.append( 322 | std::string( 323 | &context.composition.preedit[context.composition.sel_start], 324 | &context.composition.preedit[context.composition.sel_end]), 325 | flag | highlightFlag); 326 | } 327 | 328 | /* remaining input to convert */ 329 | if (context.composition.sel_end < context.composition.length) { 330 | preedit.append( 331 | std::string( 332 | &context.composition.preedit[context.composition.sel_end], 333 | &context.composition.preedit[context.composition.length]), 334 | flag); 335 | } 336 | 337 | preedit.setCursor(context.composition.cursor_pos); 338 | } while (0); 339 | 340 | return preedit; 341 | } 342 | 343 | void RimeState::updatePreedit(InputContext *ic, const RimeContext &context) { 344 | PreeditMode mode = ic->capabilityFlags().test(CapabilityFlag::Preedit) 345 | ? *engine_->config().preeditMode 346 | : PreeditMode::No; 347 | 348 | switch (mode) { 349 | case PreeditMode::No: 350 | ic->inputPanel().setPreedit(preeditFromRimeContext( 351 | context, TextFormatFlag::NoFlag, TextFormatFlag::NoFlag)); 352 | ic->inputPanel().setClientPreedit(Text()); 353 | break; 354 | case PreeditMode::CommitPreview: { 355 | ic->inputPanel().setPreedit(preeditFromRimeContext( 356 | context, TextFormatFlag::NoFlag, TextFormatFlag::NoFlag)); 357 | if (context.composition.length > 0 && context.commit_text_preview) { 358 | Text clientPreedit; 359 | clientPreedit.append(context.commit_text_preview, 360 | TextFormatFlag::Underline); 361 | if (*engine_->config().preeditCursorPositionAtBeginning) { 362 | clientPreedit.setCursor(0); 363 | } else { 364 | clientPreedit.setCursor(clientPreedit.textLength()); 365 | } 366 | ic->inputPanel().setClientPreedit(clientPreedit); 367 | } else { 368 | ic->inputPanel().setClientPreedit(Text()); 369 | } 370 | } break; 371 | case PreeditMode::ComposingText: { 372 | const TextFormatFlag highlightFlag = 373 | *engine_->config().preeditCursorPositionAtBeginning 374 | ? TextFormatFlag::HighLight 375 | : TextFormatFlag::NoFlag; 376 | Text clientPreedit = preeditFromRimeContext( 377 | context, TextFormatFlag::Underline, highlightFlag); 378 | if (*engine_->config().preeditCursorPositionAtBeginning) { 379 | clientPreedit.setCursor(0); 380 | } 381 | ic->inputPanel().setClientPreedit(clientPreedit); 382 | } break; 383 | } 384 | } 385 | 386 | void RimeState::updateUI(InputContext *ic, bool keyRelease) { 387 | auto &inputPanel = ic->inputPanel(); 388 | if (!keyRelease) { 389 | inputPanel.reset(); 390 | } 391 | bool oldEmptyExceptAux = emptyExceptAux(inputPanel); 392 | 393 | do { 394 | auto *api = engine_->api(); 395 | if (api->is_maintenance_mode()) { 396 | return; 397 | } 398 | auto session = this->session(); 399 | if (!api->find_session(session)) { 400 | return; 401 | } 402 | 403 | RIME_STRUCT(RimeContext, context); 404 | if (!api->get_context(session, &context)) { 405 | break; 406 | } 407 | 408 | updatePreedit(ic, context); 409 | 410 | if (context.menu.num_candidates) { 411 | ic->inputPanel().setCandidateList( 412 | std::make_unique(engine_, ic, context)); 413 | } else { 414 | ic->inputPanel().setCandidateList(nullptr); 415 | } 416 | 417 | api->free_context(&context); 418 | } while (false); 419 | 420 | ic->updatePreedit(); 421 | // HACK: for show input method information. 422 | // Since we don't use aux, which is great for this hack. 423 | bool newEmptyExceptAux = emptyExceptAux(inputPanel); 424 | // If it's key release and old information is not "empty", do the rest of 425 | // "reset". 426 | if (keyRelease && !newEmptyExceptAux) { 427 | inputPanel.setAuxUp(Text()); 428 | inputPanel.setAuxDown(Text()); 429 | } 430 | if (newEmptyExceptAux && lastMode_ != subMode()) { 431 | engine_->instance()->showInputMethodInformation(ic); 432 | ic->updateUserInterface(UserInterfaceComponent::StatusArea); 433 | } 434 | 435 | if (!keyRelease || !oldEmptyExceptAux || !newEmptyExceptAux) { 436 | ic->updateUserInterface(UserInterfaceComponent::InputPanel); 437 | } 438 | } 439 | 440 | void RimeState::release() { session_.reset(); } 441 | 442 | void RimeState::commitInput(InputContext *ic) { 443 | if (auto *api = engine_->api()) { 444 | if (const char *input = api->get_input(this->session())) { 445 | if (std::strlen(input) > 0) { 446 | ic->commitString(input); 447 | } 448 | } 449 | } 450 | } 451 | 452 | void RimeState::commitComposing(InputContext *ic) { 453 | if (auto *api = engine_->api()) { 454 | RIME_STRUCT(RimeContext, context); 455 | auto session = this->session(); 456 | if (!api->get_context(session, &context)) { 457 | return; 458 | } 459 | if (context.composition.length > 0) { 460 | ic->commitString(context.composition.preedit); 461 | } 462 | api->free_context(&context); 463 | } 464 | } 465 | 466 | void RimeState::commitPreedit(InputContext *ic) { 467 | if (auto *api = engine_->api()) { 468 | RIME_STRUCT(RimeContext, context); 469 | auto session = this->session(); 470 | if (!api->get_context(session, &context)) { 471 | return; 472 | } 473 | if (context.composition.length > 0 && context.commit_text_preview) { 474 | ic->commitString(context.commit_text_preview); 475 | } 476 | api->free_context(&context); 477 | } 478 | } 479 | 480 | void RimeState::snapshot() { 481 | if (!session(false)) { 482 | return; 483 | } 484 | getStatus([this](const RimeStatus &status) { 485 | if (!status.schema_id) { 486 | return; 487 | } 488 | savedCurrentSchema_ = status.schema_id; 489 | savedOptions_.clear(); 490 | if (savedCurrentSchema_.empty()) { 491 | return; 492 | } 493 | 494 | savedOptions_ = snapshotOptions(savedCurrentSchema_); 495 | }); 496 | } 497 | 498 | std::vector RimeState::snapshotOptions(const std::string &schema) { 499 | if (schema.empty()) { 500 | return {}; 501 | } 502 | std::vector savedOptions; 503 | const auto &optionActions = engine_->optionActions(); 504 | auto iter = optionActions.find(schema); 505 | if (iter == optionActions.end()) { 506 | return {}; 507 | } 508 | for (const auto &option : iter->second) { 509 | if (auto savedOption = option->snapshotOption(&ic_)) { 510 | savedOptions.push_back(std::move(*savedOption)); 511 | } 512 | } 513 | return savedOptions; 514 | } 515 | 516 | void RimeState::restore() { 517 | if (savedCurrentSchema_.empty()) { 518 | return; 519 | } 520 | if (!engine_->schemas().count(savedCurrentSchema_)) { 521 | return; 522 | } 523 | 524 | selectSchema(savedCurrentSchema_); 525 | for (const auto &option : savedOptions_) { 526 | if (option.starts_with("!")) { 527 | engine_->api()->set_option(session(), option.c_str() + 1, false); 528 | } else { 529 | engine_->api()->set_option(session(), option.c_str(), true); 530 | } 531 | } 532 | } 533 | 534 | void RimeState::maybeSyncProgramNameToSession() { 535 | // The program name is guranteed to be const through the Input Context 536 | // lifetime. There is no need to update it if the policy is not "All". 537 | if (engine_->sessionPool().propertyPropagatePolicy() != 538 | PropertyPropagatePolicy::All) { 539 | return; 540 | } 541 | 542 | if (session_) { 543 | session_->setProgramName(ic_.program()); 544 | } 545 | } 546 | 547 | void RimeState::addChangedOption(std::string_view option) { 548 | changedOptions_.push_back(std::string(option)); 549 | } 550 | void RimeState::showChangedOptions() { 551 | 552 | std::string schema = currentSchema(); 553 | if (schema.empty()) { 554 | return; 555 | } 556 | const auto &optionActions = engine_->optionActions(); 557 | auto iter = optionActions.find(schema); 558 | if (iter == optionActions.end()) { 559 | return; 560 | } 561 | const auto &actions = iter->second; 562 | 563 | std::string labels; 564 | std::unordered_set actionSet; 565 | std::vector actionList; 566 | 567 | auto extractOptionName = [](std::string_view &option) { 568 | const bool state = (option.front() != '!'); 569 | if (!state) { 570 | option.remove_prefix(1); 571 | } 572 | return state; 573 | }; 574 | 575 | for (std::string_view option : changedOptions_) { 576 | if (option.empty()) { 577 | continue; 578 | } 579 | extractOptionName(option); 580 | // Skip internal options. 581 | if (option.starts_with("_")) { 582 | continue; 583 | } 584 | 585 | // This is hard coded latin-mode. 586 | if (option == "ascii_mode") { 587 | continue; 588 | } 589 | 590 | // Filter by action, so we know this option belongs to current schema. 591 | auto actionIter = std::find_if( 592 | actions.begin(), actions.end(), 593 | [option](const std::unique_ptr &action) { 594 | return action->checkOptionName(option); 595 | }); 596 | if (actionIter == actions.end()) { 597 | continue; 598 | } 599 | if (actionSet.count(actionIter->get())) { 600 | continue; 601 | } 602 | actionSet.insert(actionIter->get()); 603 | actionList.push_back(actionIter->get()); 604 | } 605 | 606 | for (auto *action : actionList) { 607 | // Snapshot again, so SelectAction will return the current active value. 608 | auto label = action->optionLabel(&ic_); 609 | if (label.empty()) { 610 | continue; 611 | } 612 | if (!labels.empty()) { 613 | labels.append("|"); 614 | } 615 | labels.append(label); 616 | } 617 | if (!labels.empty()) { 618 | engine_->instance()->showCustomInputMethodInformation(&ic_, labels); 619 | } 620 | } 621 | } // namespace fcitx::rime 622 | -------------------------------------------------------------------------------- /src/rimestate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2017~2017 CSSlayer 3 | * 4 | * SPDX-License-Identifier: LGPL-2.1-or-later 5 | */ 6 | #ifndef _FCITX_RIMESTATE_H_ 7 | #define _FCITX_RIMESTATE_H_ 8 | 9 | #include "rimesession.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define RIME_ASCII_MODE "ascii_mode" 22 | 23 | namespace fcitx::rime { 24 | 25 | class RimeEngine; 26 | 27 | class RimeState : public InputContextProperty { 28 | public: 29 | RimeState(RimeEngine *engine, InputContext &ic); 30 | 31 | virtual ~RimeState(); 32 | 33 | void clear(); 34 | void activate(); 35 | void keyEvent(KeyEvent &event); 36 | void selectCandidate(InputContext *inputContext, int idx, bool global); 37 | #ifndef FCITX_RIME_NO_DELETE_CANDIDATE 38 | void deleteCandidate(int idx, bool global); 39 | #endif 40 | bool getStatus(const std::function &); 41 | void updatePreedit(InputContext *ic, const RimeContext &context); 42 | void updateUI(InputContext *ic, bool keyRelease); 43 | void release(); 44 | void commitInput(InputContext *ic); 45 | void commitComposing(InputContext *ic); 46 | void commitPreedit(InputContext *ic); 47 | std::string subMode(); 48 | std::string subModeLabel(); 49 | void toggleLatinMode(); 50 | void setLatinMode(bool latin); 51 | void selectSchema(const std::string &schemaId); 52 | RimeSessionId session(bool requestNewSession = true); 53 | 54 | void snapshot(); 55 | void restore(); 56 | std::string currentSchema(); 57 | void addChangedOption(std::string_view option); 58 | void showChangedOptions(); 59 | 60 | private: 61 | void maybeSyncProgramNameToSession(); 62 | std::vector snapshotOptions(const std::string &schema); 63 | 64 | std::string lastMode_; 65 | RimeEngine *engine_; 66 | InputContext &ic_; 67 | std::shared_ptr session_; 68 | 69 | std::string savedCurrentSchema_; 70 | std::vector savedOptions_; 71 | std::vector changedOptions_; 72 | }; 73 | } // namespace fcitx::rime 74 | 75 | #endif // _FCITX_RIMESTATE_H_ 76 | --------------------------------------------------------------------------------