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 newGroupInfo.setDefaultInputMethod(newGroupInfo.defaultInputMethod());
356 *group = std::move(newGroupInfo);
357 if (!d->buildingGroup_ && isCurrent) {
358 emit<InputMethodManager::CurrentGroupChanged>(d->groupOrder_.front());
362 void InputMethodManagerPrivate::setGroupOrder(
363 const std::vector<std::string> &groupOrder) {
365 std::unordered_set<std::string> added;
366 for (
const auto &groupName : groupOrder) {
367 if (groups_.count(groupName)) {
368 groupOrder_.push_back(groupName);
369 added.insert(groupName);
372 for (
auto &p : groups_) {
373 if (!added.count(p.first)) {
374 groupOrder_.push_back(p.first);
377 assert(groupOrder_.size() == groups_.size());
387 newGroup.setDefaultLayout(currentGroup().defaultLayout());
389 if (newGroup.defaultLayout().empty()) {
390 newGroup.setDefaultLayout(
"us");
392 d->groups_.emplace(name, std::move(newGroup));
393 d->groupOrder_.push_back(name);
394 if (!d->buildingGroup_) {
395 emit<InputMethodManager::GroupAdded>(name);
400 if (groupCount() == 1) {
404 bool isCurrent = d->groupOrder_.front() == name;
405 auto iter = d->groups_.find(name);
406 if (iter != d->groups_.end()) {
408 emit<InputMethodManager::CurrentGroupAboutToChange>(
409 d->groupOrder_.front());
411 d->groups_.erase(iter);
412 d->groupOrder_.remove(name);
415 emit<InputMethodManager::CurrentGroupChanged>(
416 d->groupOrder_.front());
418 if (!d->buildingGroup_) {
419 emit<InputMethodManager::GroupRemoved>(name);
426 InputMethodConfig config;
427 std::vector<InputMethodGroupConfig> groups;
428 config.groupOrder.setValue(
429 std::vector<std::string>{d->groupOrder_.begin(), d->groupOrder_.end()});
431 for (
auto &p : d->groups_) {
432 auto &group = p.second;
433 groups.emplace_back();
434 auto &groupConfig = groups.back();
435 groupConfig.name.setValue(group.name());
436 groupConfig.defaultLayout.setValue(group.defaultLayout());
437 groupConfig.defaultInputMethod.setValue(group.defaultInputMethod());
438 std::vector<InputMethodGroupItemConfig> itemsConfig;
439 for (
auto &item : group.inputMethodList()) {
440 itemsConfig.emplace_back();
441 auto &itemConfig = itemsConfig.back();
442 itemConfig.name.setValue(item.name());
443 itemConfig.layout.setValue(item.layout());
446 groupConfig.items.setValue(std::move(itemsConfig));
448 config.groups.setValue(std::move(groups));
450 safeSaveAsIni(config,
"profile");
456 return findValue(d->entries_, name);
461 for (
auto &p : d->entries_) {
462 if (!callback(p.second)) {
471 if (!d->buildingGroup_) {
472 throw std::runtime_error(
"Called not within building group");
474 d->setGroupOrder(groups);
480 StandardPathsType::PkgData,
"inputmethod");
481 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.
void enumerateGroup(bool forward)
Simply enumerate input method groups.
Utilities to enable use object with signal.
Utility class to handle unix file decriptor.
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 methdo 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.