You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

677 lines
19 KiB

  1. #include <config.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <math.h>
  6. #include <glib/gstdio.h>
  7. #include <gdk/gdk.h>
  8. #include <gtk/gtk.h>
  9. #include <libgnomekbd/gkbd-configuration.h>
  10. #include "xapp-kbd-layout-controller.h"
  11. enum
  12. {
  13. PROP_0,
  14. PROP_ENABLED
  15. };
  16. enum
  17. {
  18. KBD_LAYOUT_CHANGED,
  19. KBD_CONFIG_CHANGED,
  20. LAST_SIGNAL
  21. };
  22. static guint signals[LAST_SIGNAL] = { 0, };
  23. typedef struct
  24. {
  25. gchar *group_name;
  26. gchar *variant_name;
  27. gchar *group_label;
  28. gint group_dupe_id;
  29. gchar *variant_label;
  30. gint variant_dupe_id;
  31. } GroupData;
  32. #define GROUP_DATA(ptr, idx) ((GroupData *) g_ptr_array_index (ptr, idx))
  33. static void
  34. group_data_free (GroupData *data)
  35. {
  36. g_clear_pointer (&data->group_name, g_free);
  37. g_clear_pointer (&data->group_label, g_free);
  38. g_clear_pointer (&data->variant_label, g_free);
  39. data->group_dupe_id = 0;
  40. data->variant_dupe_id = 0;
  41. g_slice_free (GroupData, data);
  42. }
  43. struct _XAppKbdLayoutControllerPrivate
  44. {
  45. GkbdConfiguration *config;
  46. gint num_groups;
  47. GPtrArray *group_data;
  48. gulong changed_id;
  49. gulong group_changed_id;
  50. guint idle_changed_id;
  51. gboolean enabled;
  52. };
  53. G_DEFINE_TYPE (XAppKbdLayoutController, xapp_kbd_layout_controller, G_TYPE_OBJECT);
  54. static void
  55. clear_stores (XAppKbdLayoutController *controller)
  56. {
  57. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  58. g_clear_pointer (&priv->group_data, g_ptr_array_unref);
  59. }
  60. static gchar *
  61. create_label (XAppKbdLayoutController *controller,
  62. const gchar *name,
  63. gint dupe_id)
  64. {
  65. if (g_utf8_validate (name, -1, NULL))
  66. {
  67. gchar utf8[20];
  68. gchar *utf8_cased = NULL;
  69. g_utf8_strncpy (utf8, name, 2);
  70. utf8_cased = g_utf8_strdown (utf8, -1);
  71. GString *string = g_string_new (utf8_cased);
  72. g_free (utf8_cased);
  73. if (dupe_id > 0)
  74. {
  75. string = g_string_append_unichar (string, 0x2080 + dupe_id);
  76. }
  77. return g_string_free (string, FALSE);
  78. }
  79. return NULL;
  80. }
  81. static void
  82. load_stores (XAppKbdLayoutController *controller)
  83. {
  84. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  85. gchar **group_names = gkbd_configuration_get_group_names (priv->config);
  86. priv->num_groups = g_strv_length (group_names);
  87. /* We do nothing if there's only one keyboard layout enabled */
  88. if (priv->num_groups == 1)
  89. {
  90. priv->enabled = FALSE;
  91. return;
  92. }
  93. priv->enabled = TRUE;
  94. /* Populate the GroupData pointer array */
  95. gint i, j, group_dupe_id, variant_dupe_id;
  96. GPtrArray *list = g_ptr_array_new_with_free_func ((GDestroyNotify) group_data_free);
  97. for (i = 0; i < priv->num_groups; i++)
  98. {
  99. GroupData *data = g_slice_new0 (GroupData);
  100. /* Iterate through group names, figure out subscript values for duplicates */
  101. gchar *group_name = gkbd_configuration_get_group_name (priv->config, i);
  102. group_dupe_id = 0;
  103. for (j = 0; j < list->len; j++)
  104. {
  105. GroupData *iter = g_ptr_array_index (list, j);
  106. if (g_strcmp0 (group_name, iter->group_name) == 0)
  107. {
  108. group_dupe_id++;
  109. iter->group_dupe_id = group_dupe_id;
  110. }
  111. }
  112. if (group_dupe_id > 0)
  113. {
  114. group_dupe_id++;
  115. }
  116. data->group_name = group_name;
  117. data->group_dupe_id = group_dupe_id;
  118. /* Iterate through layout/variant names, figure out subscript values for duplicates */
  119. gchar *variant_name = gkbd_configuration_extract_layout_name (priv->config, i);
  120. variant_dupe_id = 0;
  121. for (j = 0; j < list->len; j++)
  122. {
  123. GroupData *iter = g_ptr_array_index (list, j);
  124. if (g_strcmp0 (variant_name, iter->variant_name) == 0)
  125. {
  126. variant_dupe_id++;
  127. iter->variant_dupe_id = variant_dupe_id;
  128. }
  129. }
  130. if (variant_dupe_id > 0)
  131. {
  132. variant_dupe_id++;
  133. }
  134. data->variant_name = variant_name;
  135. data->variant_dupe_id = variant_dupe_id;
  136. /* Finally, add the GroupData slice to the array */
  137. g_ptr_array_add (list, data);
  138. }
  139. for (i = 0; i < priv->num_groups; i++)
  140. {
  141. GroupData *data = g_ptr_array_index (list, i);
  142. /* Now generate labels for group names and for variant names. This is done
  143. * in its own loop after the initial run, because previous dupe ids could have
  144. * changed later in the loop.
  145. */
  146. data->group_label = create_label (controller, data->group_name, data->group_dupe_id);
  147. data->variant_label = create_label (controller, data->variant_name, data->variant_dupe_id);
  148. }
  149. priv->group_data = list;
  150. }
  151. static gboolean
  152. idle_config_changed (XAppKbdLayoutController *controller)
  153. {
  154. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  155. clear_stores (controller);
  156. load_stores (controller);
  157. if (gkbd_configuration_get_current_group (priv->config) >= priv->num_groups)
  158. {
  159. xapp_kbd_layout_controller_set_current_group (controller, 0);
  160. }
  161. g_signal_emit (controller, signals[KBD_CONFIG_CHANGED], 0);
  162. priv->idle_changed_id = 0;
  163. return FALSE;
  164. }
  165. static void
  166. on_configuration_changed (GkbdConfiguration *config,
  167. XAppKbdLayoutController *controller)
  168. {
  169. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  170. if (priv->idle_changed_id != 0)
  171. {
  172. g_source_remove (priv->idle_changed_id);
  173. priv->idle_changed_id = 0;
  174. }
  175. priv->idle_changed_id = g_idle_add ((GSourceFunc) idle_config_changed, controller);
  176. }
  177. static void
  178. on_configuration_group_changed (GkbdConfiguration *config,
  179. gint group,
  180. XAppKbdLayoutController *controller)
  181. {
  182. g_signal_emit (controller, signals[KBD_LAYOUT_CHANGED], 0, (guint) group);
  183. }
  184. static void
  185. xapp_kbd_layout_controller_init (XAppKbdLayoutController *controller)
  186. {
  187. controller->priv = G_TYPE_INSTANCE_GET_PRIVATE (controller, XAPP_TYPE_KBD_LAYOUT_CONTROLLER, XAppKbdLayoutControllerPrivate);
  188. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  189. priv->config = gkbd_configuration_get ();
  190. priv->enabled = FALSE;
  191. priv->idle_changed_id = 0;
  192. priv->group_data = NULL;
  193. }
  194. static void
  195. xapp_kbd_layout_controller_constructed (GObject *object)
  196. {
  197. G_OBJECT_CLASS (xapp_kbd_layout_controller_parent_class)->constructed (object);
  198. XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (object);
  199. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  200. gkbd_configuration_start_listen (priv->config);
  201. priv->changed_id = g_signal_connect_object (priv->config,
  202. "changed",
  203. G_CALLBACK (on_configuration_changed),
  204. controller, 0);
  205. priv->group_changed_id = g_signal_connect_object (priv->config,
  206. "group-changed",
  207. G_CALLBACK (on_configuration_group_changed),
  208. controller, 0);
  209. clear_stores (controller);
  210. load_stores (controller);
  211. }
  212. static void
  213. xapp_kbd_layout_controller_get_property (GObject *gobject,
  214. guint prop_id,
  215. GValue *value,
  216. GParamSpec *pspec)
  217. {
  218. XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (gobject);
  219. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  220. switch (prop_id)
  221. {
  222. case PROP_ENABLED:
  223. g_value_set_boolean (value, priv->enabled);
  224. break;
  225. default:
  226. G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
  227. break;
  228. }
  229. }
  230. static void
  231. xapp_kbd_layout_controller_dispose (GObject *object)
  232. {
  233. XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (object);
  234. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  235. gkbd_configuration_stop_listen (priv->config);
  236. clear_stores (controller);
  237. if (priv->changed_id > 0)
  238. {
  239. g_signal_handler_disconnect (priv->config, priv->changed_id);
  240. priv->changed_id = 0;
  241. }
  242. if (priv->group_changed_id > 0)
  243. {
  244. g_signal_handler_disconnect (priv->config, priv->group_changed_id);
  245. priv->group_changed_id = 0;
  246. }
  247. if (priv->idle_changed_id != 0)
  248. {
  249. g_source_remove (priv->idle_changed_id);
  250. priv->idle_changed_id = 0;
  251. }
  252. G_OBJECT_CLASS (xapp_kbd_layout_controller_parent_class)->dispose (object);
  253. }
  254. static void
  255. xapp_kbd_layout_controller_finalize (GObject *object)
  256. {
  257. XAppKbdLayoutController *controller = XAPP_KBD_LAYOUT_CONTROLLER (object);
  258. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  259. g_clear_object (&priv->config);
  260. G_OBJECT_CLASS (xapp_kbd_layout_controller_parent_class)->finalize (object);
  261. }
  262. static void
  263. xapp_kbd_layout_controller_class_init (XAppKbdLayoutControllerClass *klass)
  264. {
  265. GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  266. gobject_class->dispose = xapp_kbd_layout_controller_dispose;
  267. gobject_class->finalize = xapp_kbd_layout_controller_finalize;
  268. gobject_class->get_property = xapp_kbd_layout_controller_get_property;
  269. gobject_class->constructed = xapp_kbd_layout_controller_constructed;
  270. g_type_class_add_private (gobject_class, sizeof (XAppKbdLayoutControllerPrivate));
  271. g_object_class_install_property (gobject_class, PROP_ENABLED,
  272. g_param_spec_boolean ("enabled",
  273. "Enabled",
  274. "Whether we're enabled (more than one keyboard layout is installed)",
  275. FALSE,
  276. G_PARAM_READABLE)
  277. );
  278. signals[KBD_LAYOUT_CHANGED] = g_signal_new ("layout-changed",
  279. G_TYPE_FROM_CLASS (gobject_class),
  280. G_SIGNAL_RUN_LAST,
  281. 0,
  282. NULL, NULL,
  283. g_cclosure_marshal_VOID__UINT,
  284. G_TYPE_NONE,
  285. 1, G_TYPE_UINT);
  286. signals[KBD_CONFIG_CHANGED] = g_signal_new ("config-changed",
  287. G_TYPE_FROM_CLASS (gobject_class),
  288. G_SIGNAL_RUN_LAST,
  289. 0,
  290. NULL, NULL,
  291. g_cclosure_marshal_VOID__VOID,
  292. G_TYPE_NONE,
  293. 0, G_TYPE_NONE);
  294. }
  295. XAppKbdLayoutController *
  296. xapp_kbd_layout_controller_new (void)
  297. {
  298. return g_object_new (XAPP_TYPE_KBD_LAYOUT_CONTROLLER, NULL);
  299. }
  300. gboolean
  301. xapp_kbd_layout_controller_get_enabled (XAppKbdLayoutController *controller)
  302. {
  303. return controller->priv->enabled;
  304. }
  305. guint
  306. xapp_kbd_layout_controller_get_current_group (XAppKbdLayoutController *controller)
  307. {
  308. g_return_val_if_fail (controller->priv->enabled, 0);
  309. return gkbd_configuration_get_current_group (controller->priv->config);
  310. }
  311. void
  312. xapp_kbd_layout_controller_set_current_group (XAppKbdLayoutController *controller,
  313. guint group)
  314. {
  315. g_return_if_fail (controller->priv->enabled);
  316. g_return_if_fail (group <= controller->priv->num_groups);
  317. guint current = gkbd_configuration_get_current_group (controller->priv->config);
  318. if (current != group)
  319. {
  320. gkbd_configuration_lock_group (controller->priv->config, group);
  321. }
  322. }
  323. void
  324. xapp_kbd_layout_controller_next_group (XAppKbdLayoutController *controller)
  325. {
  326. g_return_if_fail (controller->priv->enabled);
  327. gkbd_configuration_lock_next_group (controller->priv->config);
  328. }
  329. void
  330. xapp_kbd_layout_controller_previous_group (XAppKbdLayoutController *controller)
  331. {
  332. g_return_if_fail (controller->priv->enabled);
  333. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  334. gint current = gkbd_configuration_get_current_group (priv->config);
  335. if (current > 0)
  336. {
  337. current--;
  338. }
  339. else
  340. {
  341. current = priv->num_groups - 1;
  342. }
  343. gkbd_configuration_lock_group (controller->priv->config, current);
  344. }
  345. /**
  346. * xapp_kbd_layout_controller_get_current_name:
  347. *
  348. * Returns the full name of the current keyboard layout.
  349. *
  350. * Returns: (transfer full): the newly created string or NULL
  351. * if something went wrong.
  352. */
  353. gchar *
  354. xapp_kbd_layout_controller_get_current_name (XAppKbdLayoutController *controller)
  355. {
  356. g_return_val_if_fail (controller->priv->enabled, NULL);
  357. return gkbd_configuration_get_current_tooltip (controller->priv->config);
  358. }
  359. /**
  360. * xapp_kbd_layout_controller_get_all_names:
  361. *
  362. * Returns an array of all full layout names
  363. *
  364. * Returns: (transfer none) (array zero-terminated=1): array of names
  365. */
  366. gchar **
  367. xapp_kbd_layout_controller_get_all_names (XAppKbdLayoutController *controller)
  368. {
  369. g_return_val_if_fail (controller->priv->enabled, NULL);
  370. return gkbd_configuration_get_group_names (controller->priv->config);
  371. }
  372. /**
  373. * xapp_kbd_layout_controller_get_current_icon_name:
  374. *
  375. * Returns the icon file name (no path or extension) to use for the current layout
  376. *
  377. * Returns: (transfer full): a new string with the icon name.
  378. */
  379. gchar *
  380. xapp_kbd_layout_controller_get_current_icon_name (XAppKbdLayoutController *controller)
  381. {
  382. g_return_val_if_fail (controller->priv->enabled, NULL);
  383. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  384. guint current = gkbd_configuration_get_current_group (priv->config);
  385. return g_strdup (GROUP_DATA (priv->group_data, current)->group_name);
  386. }
  387. /**
  388. * xapp_kbd_layout_controller_get_icon_name_for_group:
  389. *
  390. * Returns the icon file name (no path or extension) to use for the specified layout.
  391. *
  392. * Returns: (transfer full): a new string with the icon name.
  393. */
  394. gchar *
  395. xapp_kbd_layout_controller_get_icon_name_for_group (XAppKbdLayoutController *controller,
  396. guint group)
  397. {
  398. g_return_val_if_fail (controller->priv->enabled, NULL);
  399. g_return_val_if_fail (group <= controller->priv->num_groups, NULL);
  400. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  401. return g_strdup (GROUP_DATA (priv->group_data, group)->group_name);
  402. }
  403. /**
  404. * xapp_kbd_layout_controller_get_current_flag_id:
  405. *
  406. * Returns the duplicate id for the current layout
  407. *
  408. * Returns: the id
  409. */
  410. gint
  411. xapp_kbd_layout_controller_get_current_flag_id (XAppKbdLayoutController *controller)
  412. {
  413. g_return_val_if_fail (controller->priv->enabled, 0);
  414. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  415. guint current = gkbd_configuration_get_current_group (priv->config);
  416. return GROUP_DATA (priv->group_data, current)->group_dupe_id;
  417. }
  418. /**
  419. * xapp_kbd_layout_controller_flag_id_for_group:
  420. *
  421. * Returns the duplicate id for the specified layout
  422. *
  423. * Returns: the id
  424. */
  425. gint
  426. xapp_kbd_layout_controller_get_flag_id_for_group (XAppKbdLayoutController *controller,
  427. guint group)
  428. {
  429. g_return_val_if_fail (controller->priv->enabled, 0);
  430. g_return_val_if_fail (group < controller->priv->num_groups, 0);
  431. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  432. return GROUP_DATA (priv->group_data, group)->group_dupe_id;
  433. }
  434. /**
  435. * xapp_kbd_layout_controller_get_current_short_group_label:
  436. *
  437. * Returns the short group label (and subscript, if any) of the current layout
  438. *
  439. * Returns: (transfer full): a new string or NULL.
  440. */
  441. gchar *
  442. xapp_kbd_layout_controller_get_current_short_group_label (XAppKbdLayoutController *controller)
  443. {
  444. g_return_val_if_fail (controller->priv->enabled, NULL);
  445. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  446. guint current = gkbd_configuration_get_current_group (priv->config);
  447. return g_strdup (GROUP_DATA (priv->group_data, current)->group_label);
  448. }
  449. /**
  450. * xapp_kbd_layout_controller_get_short_group_label_for_group:
  451. *
  452. * Returns the short group label and subscript of the specified layout.
  453. *
  454. * Returns: (transfer full): a new string or NULL.
  455. */
  456. gchar *
  457. xapp_kbd_layout_controller_get_short_group_label_for_group (XAppKbdLayoutController *controller,
  458. guint group)
  459. {
  460. g_return_val_if_fail (controller->priv->enabled, NULL);
  461. g_return_val_if_fail (group < controller->priv->num_groups, NULL);
  462. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  463. return g_strdup (GROUP_DATA (priv->group_data, group)->group_label);
  464. }
  465. /**
  466. * xapp_kbd_layout_controller_get_current_variant_label:
  467. *
  468. * Returns the variant label (and subscript, if any) of the current layout
  469. *
  470. * Returns: (transfer full): a new string or NULL.
  471. */
  472. gchar *
  473. xapp_kbd_layout_controller_get_current_variant_label (XAppKbdLayoutController *controller)
  474. {
  475. g_return_val_if_fail (controller->priv->enabled, NULL);
  476. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  477. guint current = gkbd_configuration_get_current_group (priv->config);
  478. return g_strdup (GROUP_DATA (priv->group_data, current)->variant_label);
  479. }
  480. /**
  481. * xapp_kbd_layout_controller_get_variant_label_for_group:
  482. *
  483. * Returns the variant label and subscript of the specified layout.
  484. *
  485. * Returns: (transfer full): a new string or NULL.
  486. */
  487. gchar *
  488. xapp_kbd_layout_controller_get_variant_label_for_group (XAppKbdLayoutController *controller,
  489. guint group)
  490. {
  491. g_return_val_if_fail (controller->priv->enabled, NULL);
  492. XAppKbdLayoutControllerPrivate *priv = controller->priv;
  493. g_return_val_if_fail (group < controller->priv->num_groups, NULL);
  494. return g_strdup (GROUP_DATA (priv->group_data, group)->variant_label);
  495. }
  496. void
  497. xapp_kbd_layout_controller_render_cairo_subscript (cairo_t *cr,
  498. gdouble x,
  499. gdouble y,
  500. gdouble width,
  501. gdouble height,
  502. gint subscript)
  503. {
  504. if (subscript == 0)
  505. {
  506. return;
  507. }
  508. cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, .5);
  509. cairo_rectangle (cr, x, y, width, height);
  510. cairo_fill (cr);
  511. cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, .8);
  512. cairo_rectangle (cr, x + 1, y + 1, width - 2, height - 2);
  513. cairo_fill (cr);
  514. cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
  515. gchar *num_string = g_strdup_printf ("%d", subscript);
  516. cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
  517. cairo_set_font_size (cr, height - 2.0);
  518. cairo_text_extents_t ext;
  519. cairo_text_extents (cr, num_string, &ext);
  520. cairo_move_to (cr,
  521. (x + (width / 2.0) - (ext.width / 2.0)),
  522. (y + (height / 2.0) + (ext.height / 2.0)));
  523. cairo_show_text (cr, num_string);
  524. g_free (num_string);
  525. }