File indexing completed on 2024-11-24 05:00:24

0001 /*
0002     SPDX-FileCopyrightText: 2011-2012 Ni Hui <shuizhuyuanluo@126.com>
0003     SPDX-FileCopyrightText: 2013-2014 Weng Xuetian <wengxt@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "panel.h"
0009 #include "app.h"
0010 #include "enginemanager.h"
0011 #include "gtkaccelparse_p.h"
0012 #include "propertymanager.h"
0013 #include "xkblayoutmanager.h"
0014 #include <QByteArray>
0015 #include <QDebug>
0016 #include <QPair>
0017 #include <QStringList>
0018 #include <locale.h>
0019 #include <stdlib.h>
0020 #include <string.h>
0021 #include <string>
0022 
0023 #ifndef DBUS_ERROR_FAILED
0024 #define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed"
0025 #endif /* DBUS_ERROR_FAILED */
0026 
0027 #define IBUS_SCHEMA_GENERAL "org.freedesktop.ibus.general"
0028 #define IBUS_SCHEMA_HOTKEY "org.freedesktop.ibus.general.hotkey"
0029 #define IBUS_SCHEMA_PANEL "org.freedesktop.ibus.panel"
0030 
0031 typedef struct _IBusPanelImpanelClass IBusPanelImpanelClass;
0032 
0033 struct _IBusPanelImpanel {
0034     IBusPanelService parent;
0035     IBusBus *bus;
0036     GDBusConnection *conn;
0037     PropertyManager *propManager;
0038     EngineManager *engineManager;
0039     XkbLayoutManager *xkbLayoutManager;
0040     App *app;
0041     gboolean useSystemKeyboardLayout;
0042     int selected;
0043     GSettings *settings_general;
0044     GSettings *settings_hotkey;
0045 };
0046 
0047 struct _IBusPanelImpanelClass {
0048     IBusPanelServiceClass parent;
0049 };
0050 
0051 /* functions prototype */
0052 static void ibus_panel_impanel_class_init(IBusPanelImpanelClass *klass);
0053 static void ibus_panel_impanel_init(IBusPanelImpanel *impanel);
0054 static void ibus_panel_impanel_destroy(IBusPanelImpanel *impanel);
0055 
0056 static void ibus_panel_impanel_focus_in(IBusPanelService *panel, const gchar *input_context_path);
0057 static void ibus_panel_impanel_focus_out(IBusPanelService *panel, const gchar *input_context_path);
0058 static void ibus_panel_impanel_register_properties(IBusPanelService *panel, IBusPropList *prop_list);
0059 static void ibus_panel_impanel_real_register_properties(IBusPanelImpanel *impanel);
0060 static void ibus_panel_impanel_set_cursor_location(IBusPanelService *panel, gint x, gint y, gint w, gint h);
0061 static void ibus_panel_impanel_update_auxiliary_text(IBusPanelService *panel, IBusText *text, gboolean visible);
0062 static void ibus_panel_impanel_update_lookup_table(IBusPanelService *panel, IBusLookupTable *lookup_table, gboolean visible);
0063 static void ibus_panel_impanel_update_preedit_text(IBusPanelService *panel, IBusText *text, guint cursor_pos, gboolean visible);
0064 static void ibus_panel_impanel_update_property(IBusPanelService *panel, IBusProperty *prop);
0065 static void ibus_panel_impanel_cursor_down_lookup_table(IBusPanelService *panel);
0066 static void ibus_panel_impanel_cursor_up_lookup_table(IBusPanelService *panel);
0067 static void ibus_panel_impanel_hide_auxiliary_text(IBusPanelService *panel);
0068 static void ibus_panel_impanel_hide_language_bar(IBusPanelService *panel);
0069 static void ibus_panel_impanel_hide_lookup_table(IBusPanelService *panel);
0070 static void ibus_panel_impanel_hide_preedit_text(IBusPanelService *panel);
0071 static void ibus_panel_impanel_page_down_lookup_table(IBusPanelService *panel);
0072 static void ibus_panel_impanel_page_up_lookup_table(IBusPanelService *panel);
0073 static void ibus_panel_impanel_reset(IBusPanelService *panel);
0074 static void ibus_panel_impanel_show_auxiliary_text(IBusPanelService *panel);
0075 static void ibus_panel_impanel_show_language_bar(IBusPanelService *panel);
0076 static void ibus_panel_impanel_show_lookup_table(IBusPanelService *panel);
0077 static void ibus_panel_impanel_show_preedit_text(IBusPanelService *panel);
0078 static void ibus_panel_impanel_start_setup(IBusPanelService *panel);
0079 static void ibus_panel_impanel_state_changed(IBusPanelService *panel);
0080 
0081 /* impanel signal handler function */
0082 static void ibus_panel_impanel_exec_im_menu(IBusPanelImpanel *impanel);
0083 static void ibus_panel_impanel_exec_menu(IBusPanelImpanel *impanel, IBusPropList *prop_list);
0084 
0085 static void impanel_set_engine(IBusPanelImpanel *impanel, const char *name);
0086 
0087 static QByteArray ibus_property_to_propstr(IBusProperty *property, gboolean useSymbol = FALSE);
0088 
0089 static QByteArray ibus_engine_desc_to_logo_propstr(IBusEngineDesc *engine);
0090 
0091 void impanel_update_logo_by_engine(IBusPanelImpanel *impanel, IBusEngineDesc *engine_desc)
0092 {
0093     if (!impanel->conn) {
0094         return;
0095     }
0096 
0097     QByteArray propstr = ibus_engine_desc_to_logo_propstr(engine_desc);
0098 
0099     g_dbus_connection_emit_signal(impanel->conn,
0100                                   nullptr,
0101                                   "/kimpanel",
0102                                   "org.kde.kimpanel.inputmethod",
0103                                   "UpdateProperty",
0104                                   (g_variant_new("(s)", propstr.constData())),
0105                                   nullptr);
0106 }
0107 
0108 void ibus_panel_impanel_set_bus(IBusPanelImpanel *impanel, IBusBus *bus)
0109 {
0110     impanel->bus = bus;
0111 }
0112 
0113 void ibus_panel_impanel_set_app(IBusPanelImpanel *impanel, App *app)
0114 {
0115     impanel->app = app;
0116 }
0117 
0118 void ibus_panel_impanel_accept(IBusPanelImpanel *impanel)
0119 {
0120     if (impanel->selected >= 0 && static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
0121         impanel_set_engine(impanel, ibus_engine_desc_get_name(impanel->engineManager->engines()[impanel->selected]));
0122         impanel->selected = -1;
0123     }
0124 }
0125 
0126 void ibus_panel_impanel_navigate(IBusPanelImpanel *impanel, gboolean start, gboolean forward)
0127 {
0128     if (start) {
0129         impanel->selected = -1;
0130     }
0131 
0132     if (impanel->engineManager->length() < 2) {
0133         return;
0134     }
0135 
0136     IBusEngineDesc *engine_desc = nullptr;
0137     if (impanel->selected < 0) {
0138         engine_desc = ibus_bus_get_global_engine(impanel->bus);
0139     } else if (static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
0140         engine_desc = impanel->engineManager->engines()[impanel->selected];
0141         g_object_ref(engine_desc);
0142     }
0143 
0144     if (!engine_desc) {
0145         engine_desc = impanel->engineManager->engines()[0];
0146         g_object_ref(engine_desc);
0147     }
0148 
0149     if (engine_desc) {
0150         const char *name = impanel->engineManager->navigate(engine_desc, forward);
0151         impanel->selected = impanel->engineManager->getIndexByName(name);
0152         g_object_unref(engine_desc);
0153     } else {
0154         return;
0155     }
0156 
0157     if (impanel->selected >= 0 && static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
0158         ibus_panel_impanel_real_register_properties(impanel);
0159     }
0160 }
0161 
0162 void ibus_panel_impanel_move_next(IBusPanelImpanel *impanel)
0163 {
0164     if (impanel->engineManager->length() >= 2) {
0165         impanel_set_engine(impanel, ibus_engine_desc_get_name(impanel->engineManager->engines()[1]));
0166     }
0167 }
0168 
0169 static GDBusNodeInfo *introspection_data = nullptr;
0170 
0171 static guint owner_id;
0172 
0173 static const gchar introspection_xml[] =
0174     "<node>"
0175     "  <interface name='org.kde.kimpanel.inputmethod'>"
0176     "    <signal name='Enable'>"
0177     "      <arg type='b' name='enable'/>"
0178     "    </signal>"
0179     "    <signal name='RegisterProperties'>"
0180     "      <arg type='as' name='prop'/>"
0181     "    </signal>"
0182     "    <signal name='UpdateProperty'>"
0183     "      <arg type='s' name='prop'/>"
0184     "    </signal>"
0185     "    <signal name='RemoveProperty'>"
0186     "      <arg type='s' name='prop'/>"
0187     "    </signal>"
0188     "    <signal name='ShowAux'>"
0189     "      <arg type='b' name='toshow'/>"
0190     "    </signal>"
0191     "    <signal name='ShowLookupTable'>"
0192     "      <arg type='b' name='toshow'/>"
0193     "    </signal>"
0194     "    <signal name='ShowPreedit'>"
0195     "      <arg type='b' name='toshow'/>"
0196     "    </signal>"
0197     "    <signal name='UpdateAux'>"
0198     "      <arg type='s' name='text'/>"
0199     "      <arg type='s' name='attr'/>"
0200     "    </signal>"
0201     "    <signal name='UpdateLookupTableCursor'>"
0202     "      <arg type='i' name='pos'/>"
0203     "    </signal>"
0204     "    <signal name='UpdateLookupTable'>"
0205     "      <arg type='as' name='labels'/>"
0206     "      <arg type='as' name='candidates'/>"
0207     "      <arg type='as' name='attrs'/>"
0208     "      <arg type='b' name='hasprev'/>"
0209     "      <arg type='b' name='hasnext'/>"
0210     "    </signal>"
0211     "    <signal name='UpdatePreeditCaret'>"
0212     "      <arg type='i' name='pos'/>"
0213     "    </signal>"
0214     "    <signal name='UpdatePreeditText'>"
0215     "      <arg type='s' name='text'/>"
0216     "      <arg type='s' name='attr'/>"
0217     "    </signal>"
0218     "    <signal name='UpdateSpotLocation'>"
0219     "      <arg type='i' name='x'/>"
0220     "      <arg type='i' name='y'/>"
0221     "    </signal>"
0222     "    <signal name='ExecMenu'>"
0223     "      <arg type='as' name='actions'/>"
0224     "    </signal>"
0225     "  </interface>"
0226     "</node>";
0227 
0228 static const char prop_sep[] = ":";
0229 
0230 static QByteArray ibus_property_args_to_propstr(const char *key, const char *label, const char *icon, const char *tooltip, const char *hint = "")
0231 {
0232     QByteArray propstr("/IBus/");
0233     QByteArray str(key);
0234     str.replace(':', '!');
0235 
0236     App *app = static_cast<App *>(qApp);
0237 
0238     propstr += str;
0239     propstr += prop_sep;
0240     propstr += QByteArray(label).replace(':', '-').constData();
0241     propstr += prop_sep;
0242     propstr += app->normalizeIconName(QByteArray(icon).replace(':', '-'));
0243     propstr += prop_sep;
0244     propstr += QByteArray(tooltip).replace(':', '-').constData();
0245     propstr += prop_sep;
0246     propstr += QByteArray(hint).replace(':', '-').constData();
0247 
0248     return propstr;
0249 }
0250 
0251 static QByteArray ibus_engine_desc_to_logo_propstr(IBusEngineDesc *engine)
0252 {
0253     const gchar *label = "IBus";
0254     const gchar *tooltip = "";
0255     const gchar *icon = "input-keyboard";
0256 
0257     gchar xkbLabel[3];
0258     if (engine) {
0259         const gchar *iconname = ibus_engine_desc_get_icon(engine);
0260         if (iconname && iconname[0]) {
0261             icon = iconname;
0262         }
0263 
0264         if (strncmp("xkb:", ibus_engine_desc_get_name(engine), 4) == 0) {
0265             strncpy(xkbLabel, ibus_engine_desc_get_name(engine) + 4, 2);
0266             xkbLabel[2] = 0;
0267             int i = 0;
0268             while (xkbLabel[i]) {
0269                 if (xkbLabel[i] == ':') {
0270                     xkbLabel[i] = 0;
0271                 }
0272                 i++;
0273             }
0274             label = xkbLabel;
0275             icon = "";
0276         }
0277 
0278         const gchar *longname = ibus_engine_desc_get_longname(engine);
0279         if (longname && longname[0]) {
0280             tooltip = longname;
0281         }
0282     }
0283 
0284     return ibus_property_args_to_propstr("Logo", label, icon, tooltip);
0285 }
0286 
0287 static QByteArray ibus_property_to_propstr(IBusProperty *property, gboolean useSymbol)
0288 {
0289     const gchar *label = nullptr;
0290     const gchar *tooltip = ibus_text_get_text(ibus_property_get_tooltip(property));
0291     const gchar *icon = ibus_property_get_icon(property);
0292 
0293     if (useSymbol) {
0294         label = ibus_text_get_text(ibus_property_get_symbol(property));
0295         if (!label || label[0] == '\0') {
0296             label = ibus_text_get_text(ibus_property_get_label(property));
0297         }
0298     } else {
0299         label = ibus_text_get_text(ibus_property_get_label(property));
0300     }
0301 
0302     const char *hint = "";
0303     if (ibus_property_get_prop_type(property) == PROP_TYPE_TOGGLE) {
0304         if (ibus_property_get_state(property) != PROP_STATE_CHECKED) {
0305             hint = "disable";
0306         }
0307     } else if (ibus_property_get_prop_type(property) == PROP_TYPE_RADIO) {
0308         if (ibus_property_get_state(property) == PROP_STATE_CHECKED) {
0309             hint = "checked";
0310         }
0311     }
0312 
0313     return ibus_property_args_to_propstr(ibus_property_get_key(property), label, icon, tooltip, hint);
0314 }
0315 
0316 static QByteArray ibus_engine_desc_args_to_propstr(const char *name, const char *language, const char *longname, const char *icon, const char *description)
0317 {
0318     QByteArray propstr("/IBus/Engine/");
0319     QByteArray data(name);
0320     data.replace(':', '!');
0321     propstr += data;
0322     propstr += prop_sep;
0323     if (language) {
0324         propstr += language;
0325         propstr += " - ";
0326     }
0327     propstr += longname;
0328     propstr += prop_sep;
0329     propstr += icon;
0330     propstr += prop_sep;
0331     propstr += description;
0332     return propstr;
0333 }
0334 
0335 static QByteArray ibus_engine_desc_to_propstr(IBusEngineDesc *engine_desc)
0336 {
0337     return ibus_engine_desc_args_to_propstr(ibus_engine_desc_get_name(engine_desc),
0338                                             ibus_engine_desc_get_language(engine_desc),
0339                                             ibus_engine_desc_get_longname(engine_desc),
0340                                             ibus_engine_desc_get_icon(engine_desc),
0341                                             ibus_engine_desc_get_description(engine_desc));
0342 }
0343 
0344 static void impanel_get_default_engine(IBusPanelImpanel *impanel, char ***pengine_names, gsize *plen)
0345 {
0346     GList *engines = ibus_bus_list_engines(impanel->bus);
0347     if (!engines) {
0348         *pengine_names = g_new0(gchar *, 2);
0349         *plen = 1;
0350         (*pengine_names)[0] = g_strdup("xkb:us::eng");
0351         return;
0352     }
0353 
0354     QList<QByteArray> engineList;
0355     impanel->xkbLayoutManager->getLayout();
0356     QStringList layouts = impanel->xkbLayoutManager->defaultLayout().split(QLatin1Char{','});
0357     QStringList variants = impanel->xkbLayoutManager->defaultVariant().split(QLatin1Char{','});
0358 
0359     for (int i = 0; i < layouts.size(); i++) {
0360         QString variant;
0361         if (i < variants.size()) {
0362             variant = variants[i];
0363         }
0364 
0365         for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
0366             IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
0367             QByteArray name = ibus_engine_desc_get_name(desc);
0368             if (!name.startsWith("xkb:")) {
0369                 continue;
0370             }
0371 
0372             if (QLatin1String(ibus_engine_desc_get_layout(desc)) == layouts[i] && QLatin1String(ibus_engine_desc_get_layout_variant(desc)) == variant) {
0373                 engineList << name;
0374             }
0375         }
0376     }
0377     const char *locale = setlocale(LC_CTYPE, nullptr);
0378     if (!locale) {
0379         locale = "C";
0380     }
0381 
0382     QStringList localeList = QString::fromLocal8Bit(locale).split(QLatin1Char{'.'});
0383     const QString lang = localeList.size() > 0 ? localeList.at(0) : QString{};
0384 
0385     bool added = false;
0386     for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
0387         IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
0388         QByteArray name = ibus_engine_desc_get_name(desc);
0389         if (name.startsWith("xkb:")) {
0390             continue;
0391         }
0392 
0393         if (QLatin1String(ibus_engine_desc_get_language(desc)) == lang && ibus_engine_desc_get_rank(desc) > 0) {
0394             engineList << name;
0395             added = true;
0396         }
0397     }
0398 
0399     if (!added) {
0400         localeList = QString(lang).split(QLatin1Char{'_'});
0401         QString _lang = localeList.size() > 0 ? localeList.at(0) : QString{};
0402 
0403         for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
0404             IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
0405             QByteArray name = ibus_engine_desc_get_name(desc);
0406             if (name.startsWith("xkb:")) {
0407                 continue;
0408             }
0409 
0410             if (QLatin1String(ibus_engine_desc_get_language(desc)) == _lang && ibus_engine_desc_get_rank(desc) > 0) {
0411                 engineList << name;
0412             }
0413         }
0414     }
0415 
0416     for (GList *engine = g_list_first(engines); engine != nullptr; engine = g_list_next(engine)) {
0417         IBusEngineDesc *desc = IBUS_ENGINE_DESC(engine->data);
0418         g_object_unref(desc);
0419     }
0420 
0421     g_list_free(engines);
0422 
0423     if (engineList.size() == 0) {
0424         *pengine_names = g_new0(gchar *, 2);
0425         *plen = 1;
0426         (*pengine_names)[0] = g_strdup("xkb:us::eng");
0427         return;
0428     } else {
0429         *pengine_names = g_new0(gchar *, engineList.size() + 1);
0430         *plen = engineList.size();
0431         size_t i = 0;
0432         for (const QByteArray &name : std::as_const(engineList)) {
0433             (*pengine_names)[i] = g_strdup(name.constData());
0434             i++;
0435         }
0436     }
0437 }
0438 
0439 bool contains(gchar **strlist, const gchar *str)
0440 {
0441     for (; strlist; ++strlist) {
0442         if (g_strcmp0(*strlist, str) == 0)
0443             return true;
0444     }
0445     return false;
0446 }
0447 
0448 static void impanel_update_engines(IBusPanelImpanel *impanel, GVariant *var_engines)
0449 {
0450     gchar **engine_names = nullptr;
0451     size_t len = 0;
0452     if (var_engines) {
0453         engine_names = g_variant_dup_strv(var_engines, &len);
0454     }
0455     if (len == 0) {
0456         g_strfreev(engine_names);
0457         engine_names = nullptr;
0458     }
0459 
0460     if (!engine_names) {
0461         impanel_get_default_engine(impanel, &engine_names, &len);
0462         GVariant *var = g_variant_new_strv(engine_names, len);
0463         g_settings_set_value(impanel->settings_general, "preload-engines", var);
0464     }
0465 
0466     IBusEngineDesc **engines = ibus_bus_get_engines_by_names(impanel->bus, engine_names);
0467 
0468     impanel->engineManager->setEngines(engines);
0469     if (engines && engines[0]
0470         && (!ibus_bus_get_global_engine(impanel->bus) || !contains(engine_names, ibus_engine_desc_get_name(ibus_bus_get_global_engine(impanel->bus))))) {
0471         ibus_bus_set_global_engine(impanel->bus, ibus_engine_desc_get_name(engines[0]));
0472     }
0473     g_strfreev(engine_names);
0474 
0475     impanel->app->setDoGrab(len > 1);
0476 }
0477 
0478 static void impanel_update_engines_order(IBusPanelImpanel *impanel, GVariant *var_engines)
0479 {
0480     const gchar **engine_names = nullptr;
0481     size_t len = 0;
0482     engine_names = g_variant_get_strv(var_engines, &len);
0483     if (len) {
0484         impanel->engineManager->setOrder(engine_names, len);
0485 
0486         if (impanel->engineManager->engines()) {
0487             ibus_bus_set_global_engine(impanel->bus, ibus_engine_desc_get_name(impanel->engineManager->engines()[0]));
0488         }
0489     }
0490     g_free(engine_names);
0491 }
0492 
0493 static void impanel_update_triggers(IBusPanelImpanel *impanel, GVariant *variant)
0494 {
0495     gchar **triggers = nullptr;
0496     size_t len = 0;
0497     if (variant) {
0498         triggers = g_variant_dup_strv(variant, &len);
0499     }
0500     if (len == 0) {
0501         g_strfreev(triggers);
0502         triggers = nullptr;
0503     }
0504     if (!triggers) {
0505         triggers = g_new0(gchar *, 2);
0506         len = 1;
0507         triggers[0] = g_strdup("<Super>space");
0508     }
0509 
0510     QList<QPair<uint, uint>> triggersList;
0511     for (size_t i = 0; i < len; i++) {
0512         guint key = 0;
0513         GdkModifierType mod = (GdkModifierType)0;
0514         _gtk_accelerator_parse(triggers[i], &key, &mod);
0515         if (key) {
0516             triggersList << std::make_pair(key, (uint)mod);
0517         }
0518     }
0519     impanel->app->setTriggerKeys(triggersList);
0520 }
0521 
0522 static void impanel_update_use_system_keyboard_layout(IBusPanelImpanel *impanel, GVariant *variant)
0523 {
0524     impanel->useSystemKeyboardLayout = g_variant_get_boolean(variant);
0525 }
0526 static void impanel_update_use_global_engine(IBusPanelImpanel *impanel, GVariant *variant)
0527 {
0528     impanel->engineManager->setUseGlobalEngine(g_variant_get_boolean(variant));
0529 }
0530 
0531 static void impanel_update_latin_layouts(IBusPanelImpanel *impanel, GVariant *variant)
0532 {
0533     if (!variant) {
0534         return;
0535     }
0536     gsize length;
0537     const gchar **variants = g_variant_get_strv(variant, &length);
0538 
0539     impanel->xkbLayoutManager->setLatinLayouts(variants, length);
0540     g_free(variants);
0541 }
0542 
0543 static void impanel_settings_changed_callback(GSettings *settings, const gchar *key, gpointer user_data)
0544 {
0545     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
0546     gchar *schema = nullptr;
0547     GVariant *value = g_settings_get_value(settings, key);
0548 
0549     g_object_get(G_OBJECT(settings), "schema", &schema, NULL);
0550 
0551     if (g_strcmp0(schema, IBUS_SCHEMA_GENERAL) == 0 && g_strcmp0(key, "preload-engines") == 0) {
0552         impanel_update_engines(impanel, value);
0553     } else if (g_strcmp0(schema, IBUS_SCHEMA_HOTKEY) == 0 && g_strcmp0(key, "triggers") == 0) {
0554         impanel_update_triggers(impanel, value);
0555     } else if (g_strcmp0(schema, IBUS_SCHEMA_GENERAL) == 0 && g_strcmp0(key, "use-system-keyboard-layout") == 0) {
0556         impanel_update_use_system_keyboard_layout(impanel, value);
0557     } else if (g_strcmp0(schema, IBUS_SCHEMA_GENERAL) == 0 && g_strcmp0(key, "use-global-engine") == 0) {
0558         impanel_update_use_global_engine(impanel, value);
0559     }
0560     g_free(schema);
0561 }
0562 
0563 static void impanel_exit_callback(GDBusConnection *connection,
0564                                   const gchar *sender_name,
0565                                   const gchar *object_path,
0566                                   const gchar *interface_name,
0567                                   const gchar *signal_name,
0568                                   GVariant *parameters,
0569                                   gpointer user_data)
0570 {
0571     Q_UNUSED(connection);
0572     Q_UNUSED(sender_name);
0573     Q_UNUSED(object_path);
0574     Q_UNUSED(interface_name);
0575     Q_UNUSED(signal_name);
0576     Q_UNUSED(parameters);
0577     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
0578     if (impanel->bus) {
0579         ibus_bus_exit(impanel->bus, FALSE);
0580     }
0581 }
0582 
0583 static void impanel_panel_created_callback(GDBusConnection *connection,
0584                                            const gchar *sender_name,
0585                                            const gchar *object_path,
0586                                            const gchar *interface_name,
0587                                            const gchar *signal_name,
0588                                            GVariant *parameters,
0589                                            gpointer user_data)
0590 {
0591     Q_UNUSED(connection);
0592     Q_UNUSED(sender_name);
0593     Q_UNUSED(object_path);
0594     Q_UNUSED(interface_name);
0595     Q_UNUSED(signal_name);
0596     Q_UNUSED(parameters);
0597     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
0598     ibus_panel_impanel_real_register_properties(impanel);
0599 }
0600 
0601 static void impanel_set_engine(IBusPanelImpanel *impanel, const char *name)
0602 {
0603     if (!name || !name[0]) {
0604         return;
0605     }
0606     if (ibus_bus_set_global_engine(impanel->bus, name)) {
0607         if (!impanel->useSystemKeyboardLayout) {
0608             IBusEngineDesc *engine_desc = ibus_bus_get_global_engine(impanel->bus);
0609             if (engine_desc) {
0610                 impanel->xkbLayoutManager->setLayout(engine_desc);
0611             }
0612             g_object_unref(engine_desc);
0613         }
0614         impanel->engineManager->setCurrentEngine(name);
0615     } else {
0616         qDebug() << "set engine failed.";
0617     }
0618 }
0619 
0620 static void impanel_trigger_property_callback(GDBusConnection *connection,
0621                                               const gchar *sender_name,
0622                                               const gchar *object_path,
0623                                               const gchar *interface_name,
0624                                               const gchar *signal_name,
0625                                               GVariant *parameters,
0626                                               gpointer user_data)
0627 {
0628     Q_UNUSED(connection);
0629     Q_UNUSED(sender_name);
0630     Q_UNUSED(object_path);
0631     Q_UNUSED(interface_name);
0632     Q_UNUSED(signal_name);
0633     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
0634     gchar *s0 = nullptr;
0635     g_variant_get(parameters, "(s)", &s0);
0636     if (!s0 || strlen(s0) <= 6)
0637         return;
0638     QByteArray prop_key(s0 + 6); // +6 to skip "/IBus/"
0639     prop_key.replace('!', ':');
0640     if (g_ascii_strncasecmp(prop_key.constData(), "Logo", 4) == 0)
0641         ibus_panel_impanel_exec_im_menu(impanel);
0642     else if (g_ascii_strncasecmp(prop_key.constData(), "Engine/", 7) == 0) {
0643         impanel_set_engine(impanel, prop_key.constData() + 7);
0644     } else {
0645         IBusProperty *property = impanel->propManager->property(prop_key.constData());
0646         if (property) {
0647             IBusPropState newstate = ibus_property_get_state(property);
0648             switch (ibus_property_get_prop_type(property)) {
0649             case PROP_TYPE_RADIO:
0650             case PROP_TYPE_TOGGLE:
0651                 if (ibus_property_get_prop_type(property) == PROP_TYPE_TOGGLE) {
0652                     if (newstate == PROP_STATE_CHECKED)
0653                         newstate = PROP_STATE_UNCHECKED;
0654                     else if (newstate == PROP_STATE_UNCHECKED)
0655                         newstate = PROP_STATE_CHECKED;
0656                 } else if (ibus_property_get_prop_type(property) == PROP_TYPE_RADIO) {
0657                     newstate = PROP_STATE_CHECKED;
0658                 }
0659                 Q_FALLTHROUGH();
0660             case PROP_TYPE_NORMAL:
0661                 ibus_property_set_state(property, newstate);
0662                 ibus_panel_service_property_activate((IBusPanelService *)impanel, prop_key.constData(), newstate);
0663                 break;
0664             case PROP_TYPE_MENU:
0665                 ibus_panel_impanel_exec_menu(impanel, ibus_property_get_sub_props(property));
0666             case PROP_TYPE_SEPARATOR:
0667                 break;
0668             default:
0669                 break;
0670             }
0671         } else {
0672             ibus_panel_service_property_activate((IBusPanelService *)impanel, prop_key.constData(), PROP_STATE_CHECKED);
0673         }
0674     }
0675     g_free(s0);
0676 }
0677 
0678 static void impanel_select_candidate_callback(GDBusConnection *connection,
0679                                               const gchar *sender_name,
0680                                               const gchar *object_path,
0681                                               const gchar *interface_name,
0682                                               const gchar *signal_name,
0683                                               GVariant *parameters,
0684                                               gpointer user_data)
0685 {
0686     Q_UNUSED(connection);
0687     Q_UNUSED(sender_name);
0688     Q_UNUSED(object_path);
0689     Q_UNUSED(interface_name);
0690     Q_UNUSED(signal_name);
0691 
0692     gint i;
0693     g_variant_get(parameters, "(i)", &i);
0694     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
0695     ibus_panel_service_candidate_clicked((IBusPanelService *)impanel, i, 0, 0);
0696 }
0697 
0698 static void impanel_prev_page_callback(GDBusConnection *connection,
0699                                        const gchar *sender_name,
0700                                        const gchar *object_path,
0701                                        const gchar *interface_name,
0702                                        const gchar *signal_name,
0703                                        GVariant *parameters,
0704                                        gpointer user_data)
0705 {
0706     Q_UNUSED(connection);
0707     Q_UNUSED(sender_name);
0708     Q_UNUSED(object_path);
0709     Q_UNUSED(interface_name);
0710     Q_UNUSED(signal_name);
0711     Q_UNUSED(parameters);
0712 
0713     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
0714     ibus_panel_service_page_up((IBusPanelService *)impanel);
0715 }
0716 
0717 static void impanel_next_page_callback(GDBusConnection *connection,
0718                                        const gchar *sender_name,
0719                                        const gchar *object_path,
0720                                        const gchar *interface_name,
0721                                        const gchar *signal_name,
0722                                        GVariant *parameters,
0723                                        gpointer user_data)
0724 {
0725     Q_UNUSED(connection);
0726     Q_UNUSED(sender_name);
0727     Q_UNUSED(object_path);
0728     Q_UNUSED(interface_name);
0729     Q_UNUSED(signal_name);
0730     Q_UNUSED(parameters);
0731 
0732     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(user_data);
0733     ibus_panel_service_page_down((IBusPanelService *)impanel);
0734 }
0735 
0736 static void impanel_configure_callback(GDBusConnection *connection,
0737                                        const gchar *sender_name,
0738                                        const gchar *object_path,
0739                                        const gchar *interface_name,
0740                                        const gchar *signal_name,
0741                                        GVariant *parameters,
0742                                        gpointer user_data)
0743 {
0744     Q_UNUSED(connection);
0745     Q_UNUSED(sender_name);
0746     Q_UNUSED(object_path);
0747     Q_UNUSED(interface_name);
0748     Q_UNUSED(signal_name);
0749     Q_UNUSED(parameters);
0750     Q_UNUSED(user_data);
0751     pid_t pid = fork();
0752     if (pid == 0) {
0753         execlp("ibus-setup", "ibus-setup", (char *)nullptr);
0754         exit(0);
0755     }
0756 }
0757 
0758 static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
0759 {
0760     Q_UNUSED(name);
0761     IBusPanelImpanel *impanel = ((IBusPanelImpanel *)user_data);
0762     impanel->conn = connection;
0763 
0764     g_dbus_connection_register_object(connection,
0765                                       "/kimpanel",
0766                                       introspection_data->interfaces[0],
0767                                       nullptr, /*&interface_vtable*/
0768                                       nullptr, /* user_data */
0769                                       nullptr, /* user_data_free_func */
0770                                       nullptr); /* GError** */
0771 
0772     g_dbus_connection_signal_subscribe(connection,
0773                                        "org.kde.impanel",
0774                                        "org.kde.impanel",
0775                                        "TriggerProperty",
0776                                        "/org/kde/impanel",
0777                                        nullptr,
0778                                        G_DBUS_SIGNAL_FLAGS_NONE,
0779                                        impanel_trigger_property_callback,
0780                                        user_data,
0781                                        nullptr);
0782     g_dbus_connection_signal_subscribe(connection,
0783                                        "org.kde.impanel",
0784                                        "org.kde.impanel",
0785                                        "SelectCandidate",
0786                                        "/org/kde/impanel",
0787                                        nullptr,
0788                                        G_DBUS_SIGNAL_FLAGS_NONE,
0789                                        impanel_select_candidate_callback,
0790                                        user_data,
0791                                        nullptr);
0792     g_dbus_connection_signal_subscribe(connection,
0793                                        "org.kde.impanel",
0794                                        "org.kde.impanel",
0795                                        "LookupTablePageUp",
0796                                        "/org/kde/impanel",
0797                                        nullptr,
0798                                        G_DBUS_SIGNAL_FLAGS_NONE,
0799                                        impanel_prev_page_callback,
0800                                        user_data,
0801                                        nullptr);
0802     g_dbus_connection_signal_subscribe(connection,
0803                                        "org.kde.impanel",
0804                                        "org.kde.impanel",
0805                                        "LookupTablePageDown",
0806                                        "/org/kde/impanel",
0807                                        nullptr,
0808                                        G_DBUS_SIGNAL_FLAGS_NONE,
0809                                        impanel_next_page_callback,
0810                                        user_data,
0811                                        nullptr);
0812     g_dbus_connection_signal_subscribe(connection,
0813                                        "org.kde.impanel",
0814                                        "org.kde.impanel",
0815                                        "PanelCreated",
0816                                        "/org/kde/impanel",
0817                                        nullptr,
0818                                        G_DBUS_SIGNAL_FLAGS_NONE,
0819                                        impanel_panel_created_callback,
0820                                        user_data,
0821                                        nullptr);
0822     g_dbus_connection_signal_subscribe(connection,
0823                                        "org.kde.impanel",
0824                                        "org.kde.impanel",
0825                                        "Exit",
0826                                        "/org/kde/impanel",
0827                                        nullptr,
0828                                        G_DBUS_SIGNAL_FLAGS_NONE,
0829                                        impanel_exit_callback,
0830                                        user_data,
0831                                        nullptr);
0832     g_dbus_connection_signal_subscribe(connection,
0833                                        "org.kde.impanel",
0834                                        "org.kde.impanel",
0835                                        "Configure",
0836                                        "/org/kde/impanel",
0837                                        nullptr,
0838                                        G_DBUS_SIGNAL_FLAGS_NONE,
0839                                        impanel_configure_callback,
0840                                        user_data,
0841                                        nullptr);
0842 
0843     GVariant *var_engines = g_settings_get_value(impanel->settings_general, "preload-engines");
0844     impanel_update_engines(impanel, var_engines);
0845     if (var_engines) {
0846         g_variant_unref(var_engines);
0847     }
0848 
0849     var_engines = g_settings_get_value(impanel->settings_general, "engines-order");
0850     if (var_engines) {
0851         impanel_update_engines_order(impanel, var_engines);
0852         g_variant_unref(var_engines);
0853     }
0854 
0855     GVariant *var_triggers = g_settings_get_value(impanel->settings_hotkey, "triggers");
0856     impanel_update_triggers(impanel, var_triggers);
0857     if (var_triggers) {
0858         g_variant_unref(var_triggers);
0859     }
0860 
0861     GVariant *var_layouts = g_settings_get_value(impanel->settings_general, "xkb-latin-layouts");
0862     if (var_layouts) {
0863         impanel_update_latin_layouts(impanel, var_layouts);
0864         g_variant_unref(var_layouts);
0865     }
0866 
0867     GVariant *var = g_settings_get_value(impanel->settings_general, "use-system-keyboard-layout");
0868     if (var) {
0869         impanel_update_use_system_keyboard_layout(impanel, var);
0870         g_variant_unref(var);
0871     }
0872 
0873     var = g_settings_get_value(impanel->settings_general, "use-global-engine");
0874     if (var) {
0875         impanel_update_use_global_engine(impanel, var);
0876         g_variant_unref(var);
0877     }
0878 
0879     ibus_panel_impanel_real_register_properties(impanel);
0880 }
0881 
0882 static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
0883 {
0884     Q_UNUSED(connection);
0885     Q_UNUSED(name);
0886     Q_UNUSED(user_data);
0887 }
0888 
0889 static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data)
0890 {
0891     Q_UNUSED(connection);
0892     Q_UNUSED(name);
0893     Q_UNUSED(user_data);
0894     exit(1);
0895 }
0896 
0897 G_DEFINE_TYPE(IBusPanelImpanel, ibus_panel_impanel, IBUS_TYPE_PANEL_SERVICE)
0898 
0899 static void ibus_panel_impanel_class_init(IBusPanelImpanelClass *klass)
0900 {
0901     GObjectClass *object_class = G_OBJECT_CLASS(klass);
0902 
0903     // clang-format off
0904     IBUS_OBJECT_CLASS (object_class)->destroy = (IBusObjectDestroyFunc) ibus_panel_impanel_destroy;
0905     IBUS_PANEL_SERVICE_CLASS (object_class)->focus_in                   = ibus_panel_impanel_focus_in;
0906     IBUS_PANEL_SERVICE_CLASS (object_class)->focus_out                  = ibus_panel_impanel_focus_out;
0907     IBUS_PANEL_SERVICE_CLASS (object_class)->register_properties        = ibus_panel_impanel_register_properties;
0908     IBUS_PANEL_SERVICE_CLASS (object_class)->set_cursor_location        = ibus_panel_impanel_set_cursor_location;
0909     IBUS_PANEL_SERVICE_CLASS (object_class)->update_auxiliary_text      = ibus_panel_impanel_update_auxiliary_text;
0910     IBUS_PANEL_SERVICE_CLASS (object_class)->update_lookup_table        = ibus_panel_impanel_update_lookup_table;
0911     IBUS_PANEL_SERVICE_CLASS (object_class)->update_preedit_text        = ibus_panel_impanel_update_preedit_text;
0912     IBUS_PANEL_SERVICE_CLASS (object_class)->update_property            = ibus_panel_impanel_update_property;
0913     IBUS_PANEL_SERVICE_CLASS (object_class)->cursor_down_lookup_table   = ibus_panel_impanel_cursor_down_lookup_table;
0914     IBUS_PANEL_SERVICE_CLASS (object_class)->cursor_up_lookup_table     = ibus_panel_impanel_cursor_up_lookup_table;
0915     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_auxiliary_text        = ibus_panel_impanel_hide_auxiliary_text;
0916     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_language_bar          = ibus_panel_impanel_hide_language_bar;
0917     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_lookup_table          = ibus_panel_impanel_hide_lookup_table;
0918     IBUS_PANEL_SERVICE_CLASS (object_class)->hide_preedit_text          = ibus_panel_impanel_hide_preedit_text;
0919     IBUS_PANEL_SERVICE_CLASS (object_class)->page_down_lookup_table     = ibus_panel_impanel_page_down_lookup_table;
0920     IBUS_PANEL_SERVICE_CLASS (object_class)->page_up_lookup_table       = ibus_panel_impanel_page_up_lookup_table;
0921     IBUS_PANEL_SERVICE_CLASS (object_class)->reset                      = ibus_panel_impanel_reset;
0922     IBUS_PANEL_SERVICE_CLASS (object_class)->show_auxiliary_text        = ibus_panel_impanel_show_auxiliary_text;
0923     IBUS_PANEL_SERVICE_CLASS (object_class)->show_language_bar          = ibus_panel_impanel_show_language_bar;
0924     IBUS_PANEL_SERVICE_CLASS (object_class)->show_lookup_table          = ibus_panel_impanel_show_lookup_table;
0925     IBUS_PANEL_SERVICE_CLASS (object_class)->show_preedit_text          = ibus_panel_impanel_show_preedit_text;
0926     IBUS_PANEL_SERVICE_CLASS (object_class)->start_setup                = ibus_panel_impanel_start_setup;
0927     IBUS_PANEL_SERVICE_CLASS (object_class)->state_changed              = ibus_panel_impanel_state_changed;
0928     // clang-format on
0929 }
0930 
0931 static void ibus_panel_impanel_init(IBusPanelImpanel *impanel)
0932 {
0933     impanel->bus = nullptr;
0934     impanel->app = nullptr;
0935     impanel->useSystemKeyboardLayout = false;
0936     impanel->selected = -1;
0937 
0938     introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, nullptr);
0939     owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
0940                               "org.kde.kimpanel.inputmethod",
0941                               G_BUS_NAME_OWNER_FLAGS_REPLACE,
0942                               on_bus_acquired,
0943                               on_name_acquired,
0944                               on_name_lost,
0945                               impanel,
0946                               nullptr);
0947 
0948     impanel->propManager = new PropertyManager;
0949     impanel->engineManager = new EngineManager;
0950     impanel->xkbLayoutManager = new XkbLayoutManager;
0951     impanel->settings_general = g_settings_new(IBUS_SCHEMA_GENERAL);
0952     impanel->settings_hotkey = g_settings_new(IBUS_SCHEMA_HOTKEY);
0953     g_signal_connect(impanel->settings_general, "changed", G_CALLBACK(impanel_settings_changed_callback), impanel);
0954     g_signal_connect(impanel->settings_hotkey, "changed", G_CALLBACK(impanel_settings_changed_callback), impanel);
0955 }
0956 
0957 static void ibus_panel_impanel_destroy(IBusPanelImpanel *impanel)
0958 {
0959     delete impanel->propManager;
0960     impanel->propManager = nullptr;
0961     delete impanel->engineManager;
0962     impanel->engineManager = nullptr;
0963     delete impanel->xkbLayoutManager;
0964     impanel->xkbLayoutManager = nullptr;
0965 
0966     g_signal_handlers_disconnect_by_func(impanel->settings_general, (gpointer)impanel_settings_changed_callback, impanel);
0967     g_signal_handlers_disconnect_by_func(impanel->settings_hotkey, (gpointer)impanel_settings_changed_callback, impanel);
0968     g_clear_object(&impanel->settings_general);
0969     g_clear_object(&impanel->settings_hotkey);
0970 
0971     g_bus_unown_name(owner_id);
0972     g_dbus_node_info_unref(introspection_data);
0973 
0974     IBUS_OBJECT_CLASS(ibus_panel_impanel_parent_class)->destroy((IBusObject *)impanel);
0975 }
0976 
0977 static void ibus_panel_impanel_focus_in(IBusPanelService *panel, const gchar *input_context_path)
0978 {
0979     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
0980     if (impanel->app->keyboardGrabbed()) {
0981         return;
0982     }
0983 
0984     auto engine_desc = ibus_bus_get_global_engine(impanel->bus);
0985     if (engine_desc) {
0986         impanel_update_logo_by_engine(impanel, engine_desc);
0987         g_object_unref(engine_desc);
0988     }
0989 
0990     impanel->engineManager->setCurrentContext(input_context_path);
0991     if (!impanel->engineManager->useGlobalEngine()) {
0992         impanel_set_engine(impanel, impanel->engineManager->currentEngine().toUtf8().constData());
0993     }
0994 }
0995 
0996 static void ibus_panel_impanel_focus_out(IBusPanelService *panel, const gchar *input_context_path)
0997 {
0998     Q_UNUSED(panel);
0999     Q_UNUSED(input_context_path);
1000     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1001 
1002     if (impanel->app->keyboardGrabbed()) {
1003         return;
1004     }
1005 
1006     if (impanel->engineManager->useGlobalEngine()) {
1007         return;
1008     }
1009     impanel->engineManager->setCurrentContext("");
1010 }
1011 
1012 static void ibus_panel_impanel_register_properties(IBusPanelService *panel, IBusPropList *prop_list)
1013 {
1014     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1015     impanel->propManager->setProperties(prop_list);
1016     ibus_panel_impanel_real_register_properties(impanel);
1017 }
1018 
1019 static void ibus_panel_impanel_real_register_properties(IBusPanelImpanel *impanel)
1020 {
1021     if (!impanel->conn)
1022         return;
1023 
1024     IBusProperty *property = nullptr;
1025     guint i = 0;
1026 
1027     GVariantBuilder builder;
1028     g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1029 
1030     if (impanel->selected >= 0 && static_cast<size_t>(impanel->selected) < impanel->engineManager->length()) {
1031         auto engine_desc = impanel->engineManager->engines()[impanel->selected];
1032         QByteArray propstr = ibus_engine_desc_to_logo_propstr(engine_desc);
1033         g_variant_builder_add(&builder, "s", propstr.constData());
1034     } else {
1035         QByteArray propstr;
1036         auto engine_desc = ibus_bus_get_global_engine(impanel->bus);
1037         if (engine_desc) {
1038             propstr = ibus_engine_desc_to_logo_propstr(engine_desc);
1039             g_variant_builder_add(&builder, "s", propstr.constData());
1040             g_object_unref(engine_desc);
1041         }
1042 
1043         IBusPropList *prop_list = impanel->propManager->properties();
1044         if (prop_list) {
1045             while ((property = ibus_prop_list_get(prop_list, i)) != nullptr) {
1046                 propstr = ibus_property_to_propstr(property, TRUE);
1047                 g_variant_builder_add(&builder, "s", propstr.constData());
1048                 ++i;
1049             }
1050         }
1051     }
1052 
1053     g_dbus_connection_emit_signal(impanel->conn,
1054                                   nullptr,
1055                                   "/kimpanel",
1056                                   "org.kde.kimpanel.inputmethod",
1057                                   "RegisterProperties",
1058                                   (g_variant_new("(as)", &builder)),
1059                                   nullptr);
1060 }
1061 
1062 static void ibus_panel_impanel_set_cursor_location(IBusPanelService *panel, gint x, gint y, gint w, gint h)
1063 {
1064     g_dbus_connection_call(IBUS_PANEL_IMPANEL(panel)->conn,
1065                            "org.kde.impanel",
1066                            "/org/kde/impanel",
1067                            "org.kde.impanel2",
1068                            "SetSpotRect",
1069                            (g_variant_new("(iiii)", x, y, w, h)),
1070                            nullptr,
1071                            G_DBUS_CALL_FLAGS_NONE,
1072                            -1, /* timeout */
1073                            nullptr,
1074                            nullptr,
1075                            nullptr);
1076 }
1077 
1078 static void ibus_panel_impanel_update_auxiliary_text(IBusPanelService *panel, IBusText *text, gboolean visible)
1079 {
1080     const gchar *t = ibus_text_get_text(text);
1081     const gchar *attr = "";
1082     IBusPanelImpanel *impanel = (IBusPanelImpanel *)panel;
1083 
1084     if (!impanel->conn)
1085         return;
1086 
1087     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "UpdateAux", (g_variant_new("(ss)", t, attr)), nullptr);
1088 
1089     if (visible == 0)
1090         ibus_panel_impanel_hide_auxiliary_text(panel);
1091     else
1092         ibus_panel_impanel_show_auxiliary_text(panel);
1093 }
1094 
1095 static void ibus_panel_impanel_update_lookup_table(IBusPanelService *panel, IBusLookupTable *lookup_table, gboolean visible)
1096 {
1097     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1098     if (!impanel->conn)
1099         return;
1100 
1101     guint page_size = ibus_lookup_table_get_page_size(lookup_table);
1102     guint cursor_pos = ibus_lookup_table_get_cursor_pos(lookup_table);
1103     guint page = cursor_pos / page_size;
1104     guint start = page * page_size;
1105     guint end = start + page_size;
1106     guint num = ibus_lookup_table_get_number_of_candidates(lookup_table);
1107     if (end > num) {
1108         end = num;
1109     }
1110 
1111     //     fprintf(stderr, "%d ~ %d pgsize %d num %d\n", start, end, page_size, num);
1112 
1113     guint i;
1114 
1115     gchar label[16][4]; // WARNING large enough I think --- nihui
1116 
1117     GVariantBuilder builder_labels;
1118     GVariantBuilder builder_candidates;
1119     GVariantBuilder builder_attrs;
1120     g_variant_builder_init(&builder_labels, G_VARIANT_TYPE("as"));
1121     g_variant_builder_init(&builder_candidates, G_VARIANT_TYPE("as"));
1122     g_variant_builder_init(&builder_attrs, G_VARIANT_TYPE("as"));
1123 
1124     const gchar *attr = "";
1125     for (i = start; i < end; i++) {
1126         g_snprintf(label[i - start], 4, "%d", (i - start + 1) % 10);
1127         // NOTE ibus always return NULL for ibus_lookup_table_get_label
1128         //         label = ibus_lookup_table_get_label(lookup_table, i)->text;
1129         g_variant_builder_add(&builder_labels, "s", label[i - start]);
1130 
1131         const gchar *candidate = ibus_text_get_text(ibus_lookup_table_get_candidate(lookup_table, i));
1132         g_variant_builder_add(&builder_candidates, "s", candidate);
1133 
1134         g_variant_builder_add(&builder_attrs, "s", attr);
1135     }
1136 
1137     gboolean has_prev = 1;
1138     gboolean has_next = 1;
1139 
1140     guint cursor_pos_in_page;
1141     if (ibus_lookup_table_is_cursor_visible(lookup_table))
1142         cursor_pos_in_page = cursor_pos % page_size;
1143     else
1144         cursor_pos_in_page = -1;
1145 
1146     gint orientation = ibus_lookup_table_get_orientation(lookup_table);
1147     if (orientation == IBUS_ORIENTATION_HORIZONTAL) {
1148         orientation = 2;
1149     } else if (orientation == IBUS_ORIENTATION_VERTICAL) {
1150         orientation = 1;
1151     } else {
1152         orientation = 0;
1153     }
1154 
1155     g_dbus_connection_call(
1156         impanel->conn,
1157         "org.kde.impanel",
1158         "/org/kde/impanel",
1159         "org.kde.impanel2",
1160         "SetLookupTable",
1161         (g_variant_new("(asasasbbii)", &builder_labels, &builder_candidates, &builder_attrs, has_prev, has_next, cursor_pos_in_page, orientation)),
1162         nullptr,
1163         G_DBUS_CALL_FLAGS_NONE,
1164         -1,
1165         nullptr,
1166         nullptr,
1167         nullptr);
1168 
1169     if (visible == 0)
1170         ibus_panel_impanel_hide_lookup_table(panel);
1171     else
1172         ibus_panel_impanel_show_lookup_table(panel);
1173 }
1174 
1175 static void ibus_panel_impanel_update_preedit_text(IBusPanelService *panel, IBusText *text, guint cursor_pos, gboolean visible)
1176 {
1177     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1178     if (!impanel->conn)
1179         return;
1180 
1181     const gchar *t = ibus_text_get_text(text);
1182     const gchar *attr = "";
1183 
1184     g_dbus_connection_emit_signal(impanel->conn,
1185                                   nullptr,
1186                                   "/kimpanel",
1187                                   "org.kde.kimpanel.inputmethod",
1188                                   "UpdatePreeditText",
1189                                   (g_variant_new("(ss)", t, attr)),
1190                                   nullptr);
1191 
1192     g_dbus_connection_emit_signal(impanel->conn,
1193                                   nullptr,
1194                                   "/kimpanel",
1195                                   "org.kde.kimpanel.inputmethod",
1196                                   "UpdatePreeditCaret",
1197                                   (g_variant_new("(i)", cursor_pos)),
1198                                   nullptr);
1199 
1200     if (visible == 0)
1201         ibus_panel_impanel_hide_preedit_text(panel);
1202     else
1203         ibus_panel_impanel_show_preedit_text(panel);
1204 }
1205 
1206 static void ibus_panel_impanel_update_property(IBusPanelService *panel, IBusProperty *prop)
1207 {
1208     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1209     if (!impanel->conn)
1210         return;
1211 
1212     impanel->propManager->updateProperty(prop);
1213 
1214     QByteArray propstr = ibus_property_to_propstr(prop, TRUE);
1215 
1216     g_dbus_connection_emit_signal(impanel->conn,
1217                                   nullptr,
1218                                   "/kimpanel",
1219                                   "org.kde.kimpanel.inputmethod",
1220                                   "UpdateProperty",
1221                                   (g_variant_new("(s)", propstr.constData())),
1222                                   nullptr);
1223 }
1224 
1225 static void ibus_panel_impanel_cursor_down_lookup_table(IBusPanelService *panel)
1226 {
1227     Q_UNUSED(panel);
1228 }
1229 
1230 static void ibus_panel_impanel_cursor_up_lookup_table(IBusPanelService *panel)
1231 {
1232     Q_UNUSED(panel);
1233 }
1234 
1235 static void ibus_panel_impanel_hide_auxiliary_text(IBusPanelService *panel)
1236 {
1237     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1238     if (!impanel->conn)
1239         return;
1240     gboolean toShow = 0;
1241 
1242     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowAux", (g_variant_new("(b)", toShow)), nullptr);
1243 }
1244 
1245 static void ibus_panel_impanel_hide_language_bar(IBusPanelService *panel)
1246 {
1247     Q_UNUSED(panel);
1248 }
1249 
1250 static void ibus_panel_impanel_hide_lookup_table(IBusPanelService *panel)
1251 {
1252     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1253     if (!impanel->conn)
1254         return;
1255     gboolean toShow = 0;
1256 
1257     g_dbus_connection_emit_signal(impanel->conn,
1258                                   nullptr,
1259                                   "/kimpanel",
1260                                   "org.kde.kimpanel.inputmethod",
1261                                   "ShowLookupTable",
1262                                   (g_variant_new("(b)", toShow)),
1263                                   nullptr);
1264 }
1265 
1266 static void ibus_panel_impanel_hide_preedit_text(IBusPanelService *panel)
1267 {
1268     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1269     if (!impanel->conn)
1270         return;
1271     gboolean toShow = 0;
1272 
1273     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowPreedit", (g_variant_new("(b)", toShow)), nullptr);
1274 }
1275 
1276 static void ibus_panel_impanel_page_down_lookup_table(IBusPanelService *panel)
1277 {
1278     Q_UNUSED(panel);
1279 }
1280 
1281 static void ibus_panel_impanel_page_up_lookup_table(IBusPanelService *panel)
1282 {
1283     Q_UNUSED(panel);
1284 }
1285 
1286 static void ibus_panel_impanel_reset(IBusPanelService *panel)
1287 {
1288     Q_UNUSED(panel);
1289 }
1290 
1291 static void ibus_panel_impanel_show_auxiliary_text(IBusPanelService *panel)
1292 {
1293     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1294     if (!impanel->conn)
1295         return;
1296     gboolean toShow = 1;
1297 
1298     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowAux", (g_variant_new("(b)", toShow)), nullptr);
1299 }
1300 
1301 static void ibus_panel_impanel_show_language_bar(IBusPanelService *panel)
1302 {
1303     Q_UNUSED(panel);
1304 }
1305 
1306 static void ibus_panel_impanel_show_lookup_table(IBusPanelService *panel)
1307 {
1308     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1309     if (!impanel->conn)
1310         return;
1311     gboolean toShow = 1;
1312 
1313     g_dbus_connection_emit_signal(impanel->conn,
1314                                   nullptr,
1315                                   "/kimpanel",
1316                                   "org.kde.kimpanel.inputmethod",
1317                                   "ShowLookupTable",
1318                                   (g_variant_new("(b)", toShow)),
1319                                   nullptr);
1320 }
1321 
1322 static void ibus_panel_impanel_show_preedit_text(IBusPanelService *panel)
1323 {
1324     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1325     if (!impanel->conn)
1326         return;
1327     gboolean toShow = 1;
1328 
1329     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ShowPreedit", (g_variant_new("(b)", toShow)), nullptr);
1330 }
1331 
1332 static void ibus_panel_impanel_start_setup(IBusPanelService *panel)
1333 {
1334     Q_UNUSED(panel);
1335 }
1336 
1337 static void ibus_panel_impanel_state_changed(IBusPanelService *panel)
1338 {
1339     IBusPanelImpanel *impanel = IBUS_PANEL_IMPANEL(panel);
1340     if (!impanel->conn)
1341         return;
1342 
1343     if (impanel->app->keyboardGrabbed()) {
1344         return;
1345     }
1346 
1347     IBusEngineDesc *engine_desc = ibus_bus_get_global_engine(impanel->bus);
1348     if (!engine_desc) {
1349         return;
1350     }
1351 
1352     impanel_update_logo_by_engine(impanel, engine_desc);
1353 
1354     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "Enable", (g_variant_new("(b)", TRUE)), nullptr);
1355 
1356     impanel->engineManager->moveToFirst(engine_desc);
1357     QStringList engineList = impanel->engineManager->engineOrder();
1358 
1359     gchar **engine_names = g_new0(gchar *, engineList.size() + 1);
1360     size_t i = 0;
1361     for (const QString &name : std::as_const(engineList)) {
1362         engine_names[i] = g_strdup(name.toUtf8().constData());
1363         i++;
1364     }
1365 
1366     GVariant *var = g_variant_new_strv(engine_names, engineList.size());
1367     g_settings_set_value(impanel->settings_general, "engines-order", var);
1368     g_strfreev(engine_names);
1369     g_object_unref(engine_desc);
1370 }
1371 
1372 static void ibus_panel_impanel_exec_menu(IBusPanelImpanel *impanel, IBusPropList *prop_list)
1373 {
1374     if (!impanel->conn)
1375         return;
1376 
1377     GVariantBuilder builder;
1378     g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1379 
1380     int i = 0;
1381     while (true) {
1382         IBusProperty *prop = ibus_prop_list_get(prop_list, i);
1383         if (!prop)
1384             break;
1385         QByteArray propstr = ibus_property_to_propstr(prop);
1386         g_variant_builder_add(&builder, "s", propstr.constData());
1387         i++;
1388     }
1389 
1390     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ExecMenu", (g_variant_new("(as)", &builder)), nullptr);
1391 }
1392 
1393 static void ibus_panel_impanel_exec_im_menu(IBusPanelImpanel *impanel)
1394 {
1395     if (!impanel->conn)
1396         return;
1397 
1398     GVariantBuilder builder;
1399     g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1400 
1401     IBusEngineDesc **engines = impanel->engineManager->engines();
1402     if (engines) {
1403         int i = 0;
1404         while (engines[i]) {
1405             QByteArray propstr = ibus_engine_desc_to_propstr(engines[i]);
1406             g_variant_builder_add(&builder, "s", propstr.constData());
1407             i++;
1408         }
1409     }
1410 
1411     g_dbus_connection_emit_signal(impanel->conn, nullptr, "/kimpanel", "org.kde.kimpanel.inputmethod", "ExecMenu", (g_variant_new("(as)", &builder)), nullptr);
1412 }
1413 
1414 IBusPanelImpanel *ibus_panel_impanel_new(GDBusConnection *connection)
1415 {
1416     IBusPanelImpanel *panel;
1417     panel = (IBusPanelImpanel *)g_object_new(IBUS_TYPE_PANEL_IMPANEL, "object-path", IBUS_PATH_PANEL, "connection", connection, NULL);
1418     return panel;
1419 }