19 #include <unordered_map> 20 #include <unordered_set> 23 #include "fcitx-config/iniparser.h" 24 #include "fcitx-config/rawconfig.h" 26 #include "fcitx-utils/handlertable.h" 27 #include "fcitx-utils/i18n.h" 29 #include "fcitx-utils/macros.h" 30 #include "fcitx-utils/misc_p.h" 33 #include "addoninfo.h" 35 #include "inputmethodconfig_p.h" 36 #include "inputmethodengine.h" 37 #include "inputmethodgroup.h" 46 :
QPtrHolder(q), addonManager_(addonManager_) {}
49 &buildDefaultGroupCallback);
51 &buildDefaultGroupCallback);
52 void setGroupOrder(
const std::vector<std::string> &groupOrder);
55 void loadStaticEntries(
const std::unordered_set<std::string> &addonNames);
57 void loadDynamicEntries(
const std::unordered_set<std::string> &addonNames);
65 std::list<std::string> groupOrder_;
66 bool buildingGroup_ =
false;
67 std::unordered_map<std::string, InputMethodGroup> groups_;
68 std::unordered_map<std::string, InputMethodEntry> entries_;
70 std::unique_ptr<HandlerTableEntry<EventHandler>> eventWatcher_;
71 int64_t timestamp_ = 0;
75 const std::unordered_set<std::string> &inputMethods) {
76 return !(entry.name().empty() || entry.uniqueName().empty() ||
77 entry.addon().empty() || inputMethods.count(entry.addon()) == 0);
80 void InputMethodManagerPrivate::loadConfig(
82 &buildDefaultGroupCallback) {
84 auto file = path.open(StandardPathsType::PkgConfig,
"profile");
87 readFromIni(config, file.fd());
89 InputMethodConfig imConfig;
90 imConfig.load(config);
93 std::vector<std::string> tempOrder;
94 if (!imConfig.groups.value().empty()) {
95 const auto &groupsConfig = imConfig.groups.value();
96 for (
const auto &groupConfig : groupsConfig) {
98 if (groupConfig.name.value().empty() ||
99 groupConfig.defaultLayout.value().empty()) {
103 groups_.emplace(groupConfig.name.value(),
105 tempOrder.push_back(groupConfig.name.value());
106 auto &group = result.first->second;
107 group.setDefaultLayout(groupConfig.defaultLayout.value());
108 const auto &items = groupConfig.items.value();
109 for (
const auto &item : items) {
110 if (!entries_.count(item.name.value())) {
111 FCITX_WARN() <<
"Group Item " << item.name.value()
112 <<
" in group " << groupConfig.name.value()
113 <<
" is not valid. Removed.";
116 group.inputMethodList().emplace_back(
118 .setLayout(item.layout.value())));
121 if (group.inputMethodList().empty()) {
122 FCITX_WARN() <<
"Group " << groupConfig.name.value()
123 <<
" is empty. Removed.";
124 groups_.erase(groupConfig.name.value());
127 group.setDefaultInputMethod(groupConfig.defaultInputMethod.value());
131 if (groups_.empty()) {
132 FCITX_INFO() <<
"No valid input method group in configuration. " 133 <<
"Building a default one";
134 buildDefaultGroup(buildDefaultGroupCallback);
136 if (!imConfig.groupOrder.value().empty()) {
137 setGroupOrder(imConfig.groupOrder.value());
139 setGroupOrder(tempOrder);
144 void InputMethodManagerPrivate::buildDefaultGroup(
146 &buildDefaultGroupCallback) {
148 if (buildDefaultGroupCallback) {
149 buildingGroup_ =
true;
150 buildDefaultGroupCallback(*q_func());
151 buildingGroup_ =
false;
153 std::string name = _(
"Default");
155 auto &group = result.first->second;
156 group.inputMethodList().emplace_back(
158 group.setDefaultInputMethod(
"");
159 group.setDefaultLayout(
"us");
160 setGroupOrder({std::move(name)});
162 assert(!groups_.empty());
163 assert(!groupOrder_.empty());
166 void InputMethodManagerPrivate::loadStaticEntries(
167 const std::unordered_set<std::string> &addonNames) {
169 timestamp_ = path.timestamp(StandardPathsType::PkgData,
"inputmethod");
170 auto filesMap = path.locate(StandardPathsType::PkgData,
"inputmethod",
171 pathfilter::extension(
".conf"));
172 for (
const auto &[fileName, fullName] : filesMap) {
173 const auto u8name = fileName.stem().u8string();
174 std::string name(u8name.begin(), u8name.end());
175 if (entries_.count(name) != 0) {
180 readFromIni(config, fd.
fd());
182 InputMethodInfo imInfo;
184 if (!*imInfo.im->enable) {
189 if (checkEntry(entry, addonNames)) {
190 entries_.emplace(std::string(entry.uniqueName()), std::move(entry));
195 void InputMethodManagerPrivate::loadDynamicEntries(
196 const std::unordered_set<std::string> &addonNames) {
197 for (
const auto &addonName : addonNames) {
198 const auto *addonInfo = addonManager_->
addonInfo(addonName);
200 if (!addonInfo || addonInfo->onDemand()) {
206 FCITX_WARN() <<
"Failed to load input method addon: " << addonName;
209 auto newEntries = engine->listInputMethods();
210 FCITX_INFO() <<
"Found " << newEntries.size() <<
" input method(s) " 211 <<
"in addon " << addonName;
212 for (
auto &newEntry : newEntries) {
214 if (checkEntry(newEntry, addonNames) &&
215 newEntry.addon() == addonName &&
216 entries_.count(newEntry.uniqueName()) == 0) {
217 entries_.emplace(std::string(newEntry.uniqueName()),
218 std::move(newEntry));
224 InputMethodManager::InputMethodManager(
AddonManager *addonManager)
225 : d_ptr(std::make_unique<InputMethodManagerPrivate>(addonManager,
this)) {}
227 InputMethodManager::~InputMethodManager() {}
230 &buildDefaultGroupCallback) {
232 emit<InputMethodManager::CurrentGroupAboutToChange>(
233 d->groupOrder_.empty() ?
"" : d->groupOrder_.front());
235 auto addonNames = d->addonManager_->addonNames(AddonCategory::InputMethod);
236 d->loadStaticEntries(addonNames);
237 d->loadDynamicEntries(addonNames);
239 d->loadConfig(buildDefaultGroupCallback);
241 emit<InputMethodManager::CurrentGroupChanged>(d->groupOrder_.front());
245 &buildDefaultGroupCallback) {
247 emit<InputMethodManager::CurrentGroupAboutToChange>(
248 d->groupOrder_.empty() ?
"" : d->groupOrder_.front());
249 d->buildDefaultGroup(buildDefaultGroupCallback);
251 emit<InputMethodManager::CurrentGroupChanged>(d->groupOrder_.front());
256 auto addonNames = d->addonManager_->addonNames(AddonCategory::InputMethod);
257 d->loadStaticEntries(addonNames);
258 d->loadDynamicEntries(addonNames);
263 return {d->groupOrder_.begin(), d->groupOrder_.end()};
268 return d->groups_.size();
273 if (groupName == d->groupOrder_.front()) {
277 std::find(d->groupOrder_.begin(), d->groupOrder_.end(), groupName);
278 if (iter != d->groupOrder_.end()) {
279 emit<InputMethodManager::CurrentGroupAboutToChange>(
280 d->groupOrder_.front());
281 d->groupOrder_.splice(d->groupOrder_.begin(), d->groupOrder_, iter);
282 emit<InputMethodManager::CurrentGroupChanged>(groupName);
288 if (groupName == d->groupOrder_.front()) {
292 std::find(d->groupOrder_.begin(), d->groupOrder_.end(), groupName);
293 if (iter != d->groupOrder_.end()) {
294 emit<InputMethodManager::CurrentGroupAboutToChange>(
295 d->groupOrder_.front());
296 d->groupOrder_.splice(d->groupOrder_.begin(), d->groupOrder_, iter,
297 d->groupOrder_.end());
298 emit<InputMethodManager::CurrentGroupChanged>(groupName);
304 return d->groups_.find(d->groupOrder_.front())->second;
309 if (groupCount() < 2) {
312 emit<InputMethodManager::CurrentGroupAboutToChange>(d->groupOrder_.front());
314 d->groupOrder_.splice(d->groupOrder_.end(), d->groupOrder_,
315 d->groupOrder_.begin());
317 d->groupOrder_.splice(d->groupOrder_.begin(), d->groupOrder_,
318 std::prev(d->groupOrder_.end()));
320 emit<InputMethodManager::CurrentGroupChanged>(d->groupOrder_.front());
325 auto ¤tGroup = d->groups_.find(d->groupOrder_.front())->second;
326 currentGroup.setDefaultInputMethod(name);
332 return findValue(d->groups_, name);
337 auto *group = findValue(d->groups_, newGroupInfo.name());
341 bool isCurrent =
false;
342 if (!d->buildingGroup_) {
343 isCurrent = (group == ¤tGroup());
345 emit<InputMethodManager::CurrentGroupAboutToChange>(
346 d->groupOrder_.front());
349 auto &list = newGroupInfo.inputMethodList();
350 auto iter = std::remove_if(list.begin(), list.end(),
352 return !d->entries_.count(item.name());
354 list.erase(iter, list.end());
355 auto defaultInputMethod = newGroupInfo.defaultInputMethod();
356 if (defaultInputMethod.empty()) {
357 defaultInputMethod = group->defaultInputMethod();
359 newGroupInfo.setDefaultInputMethod(defaultInputMethod);
360 *group = std::move(newGroupInfo);
361 if (!d->buildingGroup_ && isCurrent) {
362 emit<InputMethodManager::CurrentGroupChanged>(d->groupOrder_.front());
366 void InputMethodManagerPrivate::setGroupOrder(
367 const std::vector<std::string> &groupOrder) {
369 std::unordered_set<std::string> added;
370 for (
const auto &groupName : groupOrder) {
371 if (groups_.count(groupName)) {
372 groupOrder_.push_back(groupName);
373 added.insert(groupName);
376 for (
auto &p : groups_) {
377 if (!added.count(p.first)) {
378 groupOrder_.push_back(p.first);
381 assert(groupOrder_.size() == groups_.size());
391 newGroup.setDefaultLayout(currentGroup().defaultLayout());
393 if (newGroup.defaultLayout().empty()) {
394 newGroup.setDefaultLayout(
"us");
396 d->groups_.emplace(name, std::move(newGroup));
397 d->groupOrder_.push_back(name);
398 if (!d->buildingGroup_) {
399 emit<InputMethodManager::GroupAdded>(name);
404 if (groupCount() == 1) {
408 bool isCurrent = d->groupOrder_.front() == name;
409 auto iter = d->groups_.find(name);
410 if (iter != d->groups_.end()) {
412 emit<InputMethodManager::CurrentGroupAboutToChange>(
413 d->groupOrder_.front());
415 d->groups_.erase(iter);
416 d->groupOrder_.remove(name);
419 emit<InputMethodManager::CurrentGroupChanged>(
420 d->groupOrder_.front());
422 if (!d->buildingGroup_) {
423 emit<InputMethodManager::GroupRemoved>(name);
430 InputMethodConfig config;
431 std::vector<InputMethodGroupConfig> groups;
432 config.groupOrder.setValue(
433 std::vector<std::string>{d->groupOrder_.begin(), d->groupOrder_.end()});
435 for (
auto &p : d->groups_) {
436 auto &group = p.second;
437 groups.emplace_back();
438 auto &groupConfig = groups.back();
439 groupConfig.name.setValue(group.name());
440 groupConfig.defaultLayout.setValue(group.defaultLayout());
441 groupConfig.defaultInputMethod.setValue(group.defaultInputMethod());
442 std::vector<InputMethodGroupItemConfig> itemsConfig;
443 for (
auto &item : group.inputMethodList()) {
444 itemsConfig.emplace_back();
445 auto &itemConfig = itemsConfig.back();
446 itemConfig.name.setValue(item.name());
447 itemConfig.layout.setValue(item.layout());
450 groupConfig.items.setValue(std::move(itemsConfig));
452 config.groups.setValue(std::move(groups));
454 safeSaveAsIni(config,
"profile");
460 return findValue(d->entries_, name);
465 for (
auto &p : d->entries_) {
466 if (!callback(p.second)) {
475 if (!d->buildingGroup_) {
476 throw std::runtime_error(
"Called not within building group");
478 d->setGroupOrder(groups);
484 StandardPathsType::PkgData,
"inputmethod");
485 return timestamp > d->timestamp_;
void addEmptyGroup(const std::string &name)
Create a new empty group with given name.
Class wrap around the unix fd.
std::vector< std::string > groups() const
Return all the names of group by order.
static const StandardPaths & global()
Return the global instance of StandardPath.
const InputMethodEntry * entry(const std::string &name) const
Return a given input method entry by name.
void setCurrentGroup(const std::string &group)
Set the name of current group, rest of the group order will be adjusted accordingly.
void setGroupOrder(const std::vector< std::string > &groups)
Update the initial order of groups.
bool checkUpdate() const
Check if there is new entries could be loaded.
void load(const std::function< void(InputMethodManager &)> &buildDefaultGroupCallback={})
Load the input method information from disk.
AddonInstance * addon(const std::string &name, bool load=false)
Get the loaded addon instance.
An instance represents a standalone Fcitx instance.
void removeGroup(const std::string &name)
Remove an existing group by name.
A raw configuration tree that stores key-value pairs in a hierarchical structure. ...
void enumerateGroup(bool forward)
Simply enumerate input method groups.
Utilities to enable use object with signal.
Utility class to handle unix file descriptor.
New Utility classes to handle application specific path.
void setGroup(InputMethodGroup newGroupInfo)
Update the information of an existing group.
int fd() const noexcept
Get the internal fd.
void reset(const std::function< void(InputMethodManager &)> &buildDefaultGroupCallback={})
Reset all the group information to initial state.
void setDefaultInputMethod(const std::string &name)
Set default input method for current group.
static UnixFD openPath(const std::filesystem::path &path, std::optional< int > flags=std::nullopt, std::optional< mode_t > mode=std::nullopt)
Open the path.
void save()
Save the input method information to disk.
void enumerateGroupTo(const std::string &groupName)
enumerate group to a certain group.
const InputMethodGroup * group(const std::string &name) const
Return the input method group of given name.
const AddonInfo * addonInfo(const std::string &name) const
Get addon information for given addon.
bool foreachEntries(const std::function< bool(const InputMethodEntry &entry)> &callback)
Enumerate all the input method entries.
const InputMethodGroup & currentGroup() const
Return the current group.
int groupCount() const
Return the number of groups.
void refresh()
Load new input method configuration file from disk.