1
/*
2
 * This file is part of hildon-input-method-plugins-example 
3
 *
4
 * Copyright (C) 2006-2007 Nokia Corporation. All rights reserved.
5
 *
6
 * Author: Joaquim Rocha <jrocha@igalia.com>
7
 *
8
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
9
 *
10
    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
12
    * Neither the name of Nokia Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
13
 *
14
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
15
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
16
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
17
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
18
 * THE POSSIBILITY OF SUCH DAMAGE.
19
 * 
20
*/
21
22
#include "hildon-im-plugin.h"
23
#include "hildon-im-ui.h"
24
25
#include <stdio.h>
26
#include <string.h>
27
#include <glib.h>
28
#include <gdk/gdk.h>
29
#include <gdk/gdkx.h>
30
#include <libosso.h>
31
#include <mce/dbus-names.h>
32
#include <gtk/gtk.h>
33
#include <hildon/hildon.h>
34
35
#define SLIDING_TIMEOUT 1000
36
#define FAVORITE_HAND RIGHT
37
#define KEYS_VALUES_DATA "key_values"
38
#define PERSISTENT_VALUE_DATA "persistent_value"
39
40
#define MCE_RULE "type='signal', interface='" MCE_SIGNAL_IF "', member='" MCE_DEVICE_ORIENTATION_SIG "'"
41
#define MCE_PORTRAIT_MODE_NAME "portrait"
42
43
#define HILDON_IM_ONEHAND_FKB_TYPE hildon_im_onehand_fkb_get_type ()
44
#define HILDON_IM_ONEHAND_FKB(obj) GTK_CHECK_CAST(obj, hildon_im_onehand_fkb_get_type (), HildonIMOneHandFKB)
45
#define HILDON_IM_ONEHAND_FKB_CLASS(klass) \
46
        GTK_CHECK_CLASS_CAST(klass, hildon_im_onehand_fkb_get_type, \
47
                             HildonIMOneHandFKBClass)
48
#define HILDON_IS_IM_ONEHAND_FKB(obj) \
49
        GTK_CHECK_TYPE(obj, hildon_im_onehand_fkb_get_type ())
50
#define HILDON_IM_ONEHAND_FKB_GET_PRIVATE(obj) \
51
        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_IM_ONEHAND_FKB_TYPE,\
52
                                      HildonIMOneHandFKBPrivate))
53
54
static gchar* sliding_keys[] = {".,-_1'?!¿¡:;€£$\"~()[]{}%&/\\|", "abc2çčæáàãâäå",
55
                                "def3éèêë", "ghi4íìîïı", "jkl5", "mno6ñóòõöø",
56
                                "pqrs7š", "tuv8úùûü", "wxyz9"};
57
static gchar* qwerty_keys[] = {"q","w","e","r","t","y","u","i","o","p","a","s",
58
				"d","f","g","h","j","k","l",";:\"\'","z","x","c",
59
				"v","b","n","m",".,-_'?!¿¡:;€£$\"~()[]{}%&/\\|"};
60
static gchar* qwerty_symbol_keys[] = {"1","2","3","4","5","6","7","8","9","0","*","+",
61
				"=","#","-","_","({",")}","!","?","&","$","/",
62
				"\\","\"","\'","€","|","|"};
63
64
typedef enum
65
{
66
  PORTRAIT,
67
  LANDSCAPE
68
} ScreenMode;
69
70
typedef enum
71
{
72
  RIGHT,
73
  LEFT
74
} HandMode;
75
76
typedef struct
77
{
78
  GtkContainerClass parent;
79
}
80
HildonIMOneHandFKBClass;
81
82
typedef struct
83
{
84
  GtkContainer parent;
85
  
86
}
87
HildonIMOneHandFKB;
88
89
typedef struct
90
{
91
  HildonIMUI *ui;
92
93
  GtkWidget *window;
94
  GtkWidget *text_view;
95
  GtkWidget *keyboard;
96
  GtkWidget *keyboard_qwerty;
97
  GtkWidget *keyboard_symbols;
98
99
  GtkWidget *keyboard_keys[G_N_ELEMENTS (qwerty_keys)];
100
101
  GtkWidget *caps_button;
102
  GtkWidget *symbol_page_button;
103
104
  gint saved_offset;
105
106
  gint keys_width;
107
108
  ScreenMode screen_mode;
109
  HandMode hand_mode;
110
  gint fkb_width;
111
  gint fkb_height;
112
113
  gint slide_index;
114
  gint replace_previous_char;
115
  gint timeout_id;
116
117
  GtkWidget *repeating_key;
118
  GtkWidget *pressed_key;
119
120
  gint keyboard_page;
121
}
122
HildonIMOneHandFKBPrivate;
123
124
static GType hildon_im_onehand_fkb_type = 0;
125
static GtkWidgetClass *parent_class = NULL;
126
127
GType hildon_im_onehand_fkb_get_type (void);
128
GtkWidget *hildon_im_onehand_fkb_new (HildonIMUI *kbd);
129
130
/* 
131
 * HildonIMPlugin interface
132
 */
133
static void hildon_im_onehand_fkb_iface_init (HildonIMPluginIface *iface);
134
135
static void hildon_im_onehand_fkb_enable (HildonIMPlugin *plugin, gboolean init);
136
static void hildon_im_onehand_fkb_disable (HildonIMPlugin *plugin);
137
static void hildon_im_onehand_fkb_surrounding_received (HildonIMPlugin *plugin,
138
                                                       const gchar *surrounding,
139
                                                       gint offset);
140
141
/*
142
 * GObject functions
143
 */
144
static void hildon_im_onehand_fkb_finalize     (GObject *obj);
145
static void hildon_im_onehand_fkb_get_property (GObject *object,
146
                                                guint prop_id,
147
                                                GValue *value,
148
                                                GParamSpec *pspec);
149
static void hildon_im_onehand_fkb_set_property (GObject *object,
150
                                                guint prop_id,
151
                                                const GValue *value,
152
                                                GParamSpec *pspec);
153
 
154
static void hildon_im_onehand_fkb_class_init (HildonIMOneHandFKBClass *klass);
155
static void hildon_im_onehand_fkb_init       (HildonIMOneHandFKB *self);
156
157
/*
158
 * Internal functions
159
 */
160
static void create_window (HildonIMOneHandFKB *self);
161
162
static GtkWidget *
163
create_sliding_keyboard_portrait (HildonIMOneHandFKB *self, gchar **keys, gint page);
164
static GtkWidget *
165
create_sliding_keyboard_landscape (HildonIMOneHandFKB *self, gchar **keys, gint page);
166
167
static void
168
unparent_contents (HildonIMOneHandFKB *self);
169
170
static void set_fkb_size (HildonIMOneHandFKB *self);
171
static void set_kb_mode (HildonIMOneHandFKB *self, ScreenMode mode);
172
static void backspace (HildonIMOneHandFKB *self);
173
static void write (HildonIMOneHandFKB *self, const gchar *text);
174
static gboolean get_cursor_offset (HildonIMOneHandFKB *self);
175
static void clear_timeout_id (HildonIMOneHandFKB *self);
176
177
/*
178
 * Module functions
179
 */
180
181
HildonIMPlugin* 
182
module_create (HildonIMUI *keyboard)
183
{
184
  return HILDON_IM_PLUGIN (hildon_im_onehand_fkb_new (keyboard));
185
}
186
187
void
188
module_exit(void)
189
{
190
  /* empty */
191
}
192
193
void
194
module_init(GTypeModule *module)
195
{
196
  static const GTypeInfo type_info = {
197
    sizeof(HildonIMOneHandFKBClass),
198
    NULL, /* base_init */
199
    NULL, /* base_finalize */
200
    (GClassInitFunc) hildon_im_onehand_fkb_class_init,
201
    NULL, /* class_finalize */
202
    NULL, /* class_data */
203
    sizeof(HildonIMOneHandFKB),
204
    0,    /* n_preallocs */
205
    (GInstanceInitFunc) hildon_im_onehand_fkb_init,
206
  };
207
208
  static const GInterfaceInfo plugin_info = {
209
    (GInterfaceInitFunc) hildon_im_onehand_fkb_iface_init,
210
    NULL, /* interface_finalize */
211
    NULL, /* interface_data */
212
  };
213
214
  hildon_im_onehand_fkb_type =
215
          g_type_module_register_type(module,
216
                                      GTK_TYPE_CONTAINER, "HildonIMOneHandFKB",
217
                                      &type_info,
218
                                      0);
219
  
220
  g_type_module_add_interface(module,
221
                              HILDON_IM_ONEHAND_FKB_TYPE,
222
                              HILDON_IM_TYPE_PLUGIN,
223
                              &plugin_info);
224
}
225
226
/*
227
 * This is used to know the plugin's information when loading the module
228
 */
229
const HildonIMPluginInfo *
230
hildon_im_plugin_get_info(void)
231
{
232
  static const HildonIMPluginInfo info =
233
  {
234
    "HIM One Hand FKB",                 /* description */
235
    "hildon_im_onehand_fkb",            /* name */
236
    NULL,                               /* menu title */
237
    NULL,                               /* gettext domain */
238
    TRUE,                               /* visible in menu */
239
    FALSE,                              /* cached */
240
    HILDON_IM_TYPE_FULLSCREEN,          /* UI type */
241
    HILDON_IM_GROUP_LATIN,              /* group */
242
    HILDON_IM_DEFAULT_PLUGIN_PRIORITY,  /* priority */
243
    NULL,                               /* special character plugin */
244
    NULL,                               /* help page */
245
    TRUE,                               /* disable common UI buttons */
246
    0,                                  /* plugin height */
247
    HILDON_IM_TRIGGER_FINGER            /* trigger */
248
  };
249
250
  return &info;
251
}
252
253
/*
254
 * This function returns the list of available languages supported
255
 * by the plugin.
256
 */
257
gchar ** 
258
hildon_im_plugin_get_available_languages (gboolean *free)
259
{
260
  static gchar *langs[] = {"en_GB", NULL};
261
  *free = FALSE;
262
263
  return langs;
264
}
265
266
GType
267
hildon_im_onehand_fkb_get_type (void)
268
{
269
  return hildon_im_onehand_fkb_type;
270
}
271
272
/*
273
 * Implement the interface.
274
 */
275
static void
276
hildon_im_onehand_fkb_iface_init (HildonIMPluginIface *iface)
277
{
278
  iface->enable = hildon_im_onehand_fkb_enable;
279
  iface->disable = hildon_im_onehand_fkb_disable;
280
  iface->surrounding_received = hildon_im_onehand_fkb_surrounding_received;
281
}
282
283
static void
284
hildon_im_onehand_fkb_class_init (HildonIMOneHandFKBClass *klass)
285
{
286
  GObjectClass *object_class;
287
  GtkObjectClass *gtk_object_class;
288
  GtkWidgetClass *widget_class;
289
  GtkContainerClass *container_class;
290
291
  parent_class = g_type_class_peek_parent (klass);
292
  g_type_class_add_private (klass, sizeof (HildonIMOneHandFKBPrivate));
293
294
  object_class = G_OBJECT_CLASS(klass);
295
  gtk_object_class = GTK_OBJECT_CLASS(klass);
296
  widget_class = GTK_WIDGET_CLASS(klass);
297
  container_class = GTK_CONTAINER_CLASS(klass);
298
299
  object_class->set_property  = hildon_im_onehand_fkb_set_property;
300
  object_class->get_property  = hildon_im_onehand_fkb_get_property;
301
  object_class->finalize      = hildon_im_onehand_fkb_finalize;
302
  
303
  g_object_class_install_property (object_class, HILDON_IM_PROP_UI,
304
                                   g_param_spec_object (HILDON_IM_PROP_UI_DESCRIPTION, 
305
                                                        HILDON_IM_PROP_UI_DESCRIPTION,
306
                                                        "UI that uses plugin",
307
                                                        HILDON_IM_TYPE_UI,
308
                                                        G_PARAM_READWRITE
309
                                                        | G_PARAM_CONSTRUCT_ONLY));
310
}
311
312
static void
313
hildon_im_onehand_fkb_init (HildonIMOneHandFKB *self)
314
{
315
  HildonIMOneHandFKBPrivate *priv;
316
317
  g_return_if_fail (HILDON_IS_IM_ONEHAND_FKB (self));
318
319
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
320
321
  priv->text_view = NULL;
322
  priv->keyboard = NULL;
323
  priv->caps_button = NULL;
324
325
  priv->repeating_key = NULL;
326
  priv->pressed_key = NULL;
327
328
  priv->screen_mode = LANDSCAPE;
329
  priv->hand_mode = FAVORITE_HAND;
330
  priv->replace_previous_char = FALSE;
331
  priv->slide_index = -1;
332
  priv->timeout_id = 0;
333
334
  priv->keyboard_page = 0;
335
336
  priv->keyboard_qwerty =
337
    create_sliding_keyboard_landscape (self, qwerty_keys, 0);
338
339
  priv->keyboard = priv->keyboard_qwerty;
340
341
}
342
343
static void 
344
hildon_im_onehand_fkb_finalize(GObject *obj)
345
{
346
  if (G_OBJECT_CLASS (parent_class)->finalize)
347
  {
348
    G_OBJECT_CLASS (parent_class)->finalize (obj);
349
  }
350
}
351
352
GtkWidget *
353
hildon_im_onehand_fkb_new (HildonIMUI *kbd)
354
{
355
  return g_object_new (HILDON_IM_ONEHAND_FKB_TYPE,
356
                       HILDON_IM_PROP_UI_DESCRIPTION, kbd, NULL);
357
}
358
359
static void
360
hildon_im_onehand_fkb_get_property (GObject *object,
361
                                    guint prop_id,
362
                                    GValue *value,
363
                                    GParamSpec *pspec)
364
{
365
  HildonIMOneHandFKBPrivate *priv;
366
367
  g_return_if_fail (HILDON_IS_IM_ONEHAND_FKB(object));
368
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(object);
369
370
  switch (prop_id)
371
  {
372
    case HILDON_IM_PROP_UI:
373
      g_value_set_object(value, priv->ui);
374
      break;
375
376
    default:
377
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378
      break;
379
  }
380
}
381
382
static void
383
hildon_im_onehand_fkb_set_property (GObject *object,
384
                                    guint prop_id,
385
                                    const GValue *value,
386
                                    GParamSpec *pspec)
387
{
388
  HildonIMOneHandFKBPrivate *priv;
389
390
  g_return_if_fail (HILDON_IS_IM_ONEHAND_FKB (object));
391
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(object);
392
393
  switch (prop_id)
394
  {
395
    case HILDON_IM_PROP_UI:
396
      priv->ui = g_value_get_object(value);
397
      break;
398
    default:
399
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
400
      break;
401
  }
402
}
403
404
static DBusHandlerResult
405
screen_mode_dbus_handler (DBusConnection *connection,
406
                          DBusMessage *message,
407
                          gpointer data)
408
{
409
  HildonIMOneHandFKB *self;
410
  HildonIMOneHandFKBPrivate *priv;
411
  DBusMessageIter iter;
412
  const gchar *mode = NULL;
413
414
  self = HILDON_IM_ONEHAND_FKB (data);
415
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
416
417
  if (dbus_message_is_signal (message, MCE_SIGNAL_IF, MCE_DEVICE_ORIENTATION_SIG) &&
418
      dbus_message_iter_init (message, &iter))
419
  {
420
    dbus_message_iter_get_basic(&iter, &mode);
421
    if (mode != NULL)
422
    {
423
      ScreenMode screen_mode = priv->screen_mode;
424
425
      if (g_strcmp0 (mode, MCE_PORTRAIT_MODE_NAME) == 0)
426
      {
427
        screen_mode = PORTRAIT;
428
      }
429
      else
430
      {
431
        screen_mode = LANDSCAPE;
432
      }
433
434
      if (screen_mode != priv->screen_mode)
435
      {
436
        priv->screen_mode = screen_mode;
437
438
        hildon_gtk_window_set_portrait_flags (GTK_WINDOW (priv->window), 
439
					priv->screen_mode == PORTRAIT ?
440
                                         HILDON_PORTRAIT_MODE_REQUEST :
441
 	                                0);
442
        set_fkb_size (self);
443
	if (screen_mode == PORTRAIT)  {
444
	  gtk_widget_set_size_request (priv->text_view, -1, 400);
445
	}
446
	else {
447
	  gtk_widget_set_size_request (priv->text_view, 100, -1);
448
	}
449
        //set_kb_mode (self, priv->screen_mode);
450
      }
451
    }
452
  }
453
454
  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
455
}
456
457
static ScreenMode
458
get_screen_mode (osso_context_t* context)
459
{
460
  ScreenMode mode = LANDSCAPE;
461
  osso_return_t ret;
462
  osso_rpc_t return_value;
463
464
  ret = osso_rpc_run_system (context,
465
                             MCE_SERVICE, MCE_REQUEST_PATH,
466
                             MCE_REQUEST_IF,
467
                             MCE_DEVICE_ORIENTATION_GET,
468
                             &return_value,
469
                             DBUS_TYPE_INVALID);
470
  if (ret == OSSO_OK)
471
  {
472
    if (g_strcmp0 (return_value.value.s, MCE_PORTRAIT_MODE_NAME) == 0)
473
    {
474
      mode = PORTRAIT;
475
    }
476
    osso_rpc_free_val(&return_value);
477
  }
478
479
  return mode;
480
}
481
482
483
static void
484
hildon_im_onehand_fkb_enable (HildonIMPlugin *plugin, gboolean init)
485
{
486
  DBusConnection *connection;
487
  HildonIMOneHandFKB *self;
488
  HildonIMOneHandFKBPrivate *priv;
489
490
  g_return_if_fail (HILDON_IS_IM_ONEHAND_FKB (plugin));
491
  self = HILDON_IM_ONEHAND_FKB(plugin);
492
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(self);
493
494
  connection = osso_get_sys_dbus_connection (priv->ui->osso);
495
496
  priv->screen_mode = get_screen_mode (priv->ui->osso);
497
498
  set_fkb_size (self);
499
500
  if (priv->window == NULL)
501
  {
502
    create_window (self);
503
504
    dbus_bus_add_match (connection, MCE_RULE, NULL);
505
    dbus_connection_add_filter (connection, screen_mode_dbus_handler, self, NULL);
506
  }
507
508
  hildon_im_ui_send_communication_message (priv->ui,
509
					   HILDON_IM_CONTEXT_REQUEST_SURROUNDING_FULL);
510
  gtk_window_fullscreen (GTK_WINDOW (priv->window));
511
  gtk_widget_show_all (priv->window);
512
513
  gtk_widget_hide( priv->keyboard_qwerty );
514
  gtk_widget_hide( priv->keyboard_symbols );
515
  gtk_widget_show( priv->keyboard );
516
517
  gdk_window_set_transient_for (GTK_WIDGET (priv->window)->window,
518
                                gtk_widget_get_root_window(GTK_WIDGET(priv->window)));
519
520
  priv->saved_offset = get_cursor_offset (self);
521
}
522
523
static void
524
hildon_im_onehand_fkb_disable (HildonIMPlugin *plugin)
525
{
526
  DBusConnection *connection;
527
  HildonIMOneHandFKB *self;
528
  HildonIMOneHandFKBPrivate *priv;
529
530
  g_return_if_fail (HILDON_IS_IM_ONEHAND_FKB (plugin));
531
  self = HILDON_IM_ONEHAND_FKB (plugin);
532
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
533
534
  connection = osso_get_sys_dbus_connection (priv->ui->osso);
535
  dbus_connection_remove_filter (connection, screen_mode_dbus_handler, NULL);
536
537
  gtk_widget_hide (GTK_WIDGET (priv->window));
538
  hildon_im_ui_restore_previous_mode (priv->ui);
539
}
540
541
static void
542
hildon_im_onehand_fkb_surrounding_received(HildonIMPlugin *plugin,
543
                                           const gchar *surrounding,
544
                                           gint offset)
545
{
546
  HildonIMOneHandFKB *self;
547
  HildonIMOneHandFKBPrivate *priv;
548
  GtkTextBuffer *buffer;
549
  GtkTextIter cursor_iter;
550
551
  self = HILDON_IM_ONEHAND_FKB(plugin);
552
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(self);
553
554
  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->text_view));
555
556
  /* I don't know why, but the view doesn't reflect the changes immediately */
557
  gtk_text_buffer_set_text(buffer, surrounding, -1);
558
  gtk_text_buffer_get_iter_at_offset(buffer, &cursor_iter, offset);
559
  gtk_text_buffer_place_cursor(buffer, &cursor_iter);
560
  
561
  priv->saved_offset = offset;
562
}
563
564
static gint
565
get_cursor_offset (HildonIMOneHandFKB *self)
566
{
567
  HildonIMOneHandFKBPrivate *priv;
568
  GtkTextBuffer *buffer;
569
  GtkTextIter iter;
570
571
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
572
573
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
574
  gtk_text_buffer_get_iter_at_mark (buffer,
575
                                    &iter,
576
                                    gtk_text_buffer_get_selection_bound (buffer));
577
578
  return gtk_text_iter_get_offset (&iter);
579
}
580
581
static gboolean
582
textview_button_press_cb (GtkWidget *textview, GdkEventButton *event, gpointer data)
583
{
584
  HildonIMOneHandFKB *self;
585
  HildonIMOneHandFKBPrivate *priv;
586
  gint offset;
587
588
  g_return_val_if_fail (HILDON_IS_IM_ONEHAND_FKB (data), FALSE);
589
  self = HILDON_IM_ONEHAND_FKB (data);
590
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
591
592
  priv->saved_offset = get_cursor_offset (self);
593
594
  GTK_WIDGET_GET_CLASS (priv->text_view)->button_press_event (priv->text_view, event);
595
596
  if (hildon_im_ui_get_commit_mode (priv->ui) == HILDON_IM_COMMIT_REDIRECT)
597
  {
598
    offset = get_cursor_offset (self);
599
600
    hildon_im_ui_send_surrounding_offset (priv->ui,
601
                                          TRUE,
602
                                          offset - priv->saved_offset);
603
    priv->saved_offset = offset;
604
  }
605
606
  return TRUE;
607
}
608
609
static gboolean
610
delete_selection (HildonIMOneHandFKB *self)
611
{
612
  HildonIMOneHandFKBPrivate *priv;
613
  GtkTextIter start, end;
614
  GtkTextBuffer *buffer;
615
616
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
617
618
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
619
620
  if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
621
  {
622
    if (hildon_im_ui_get_commit_mode (priv->ui) == HILDON_IM_COMMIT_REDIRECT)
623
    {
624
      gchar *selected_text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
625
626
      hildon_im_ui_send_surrounding_offset (priv->ui,
627
                                            TRUE,
628
                                            gtk_text_iter_get_offset (&end) - get_cursor_offset (self));
629
630
      gint i;
631
      for (i = 0; i < g_utf8_strlen (selected_text, -1); i++)
632
      {
633
        hildon_im_ui_send_communication_message (priv->ui,
634
                                                 HILDON_IM_CONTEXT_HANDLE_BACKSPACE);
635
      }
636
    }
637
    gtk_text_buffer_delete (buffer, &start, &end);
638
639
    return TRUE;
640
  }
641
642
  return FALSE;
643
}
644
645
static void
646
close_fkb_cb (GtkWidget *widget, gpointer data)
647
{
648
  hildon_im_onehand_fkb_disable (HILDON_IM_PLUGIN (data));
649
}
650
651
static void
652
backspace_pressed_cb (GtkWidget *widget, gpointer data)
653
{
654
  HildonIMOneHandFKB *self = HILDON_IM_ONEHAND_FKB (data);
655
656
  if (! delete_selection (self))
657
  {
658
    backspace (self);
659
  }
660
}
661
662
static void
663
enter_key_pressed_cb (GtkWidget *widget, gpointer data)
664
{
665
  HildonIMOneHandFKB *self = HILDON_IM_ONEHAND_FKB (data);
666
  HildonIMOneHandFKBPrivate *priv;
667
668
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
669
670
  clear_timeout_id (self);
671
672
  write (self, "\n");
673
}
674
675
static void
676
backspace (HildonIMOneHandFKB *self)
677
{
678
  HildonIMOneHandFKBPrivate *priv;
679
  GtkTextIter iter;
680
  GtkTextBuffer *buffer;
681
682
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
683
684
  clear_timeout_id (self);
685
686
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
687
  gtk_text_buffer_get_iter_at_mark(buffer,
688
                                   &iter,
689
                                   gtk_text_buffer_get_insert (buffer));
690
691
  gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
692
693
  if (hildon_im_ui_get_commit_mode(priv->ui) == HILDON_IM_COMMIT_REDIRECT)
694
  {
695
    hildon_im_ui_send_communication_message(priv->ui,
696
                                            HILDON_IM_CONTEXT_HANDLE_BACKSPACE);
697
    gtk_text_buffer_get_iter_at_mark(buffer,
698
                                     &iter,
699
                                     gtk_text_buffer_get_selection_bound (buffer));
700
    priv->saved_offset = gtk_text_iter_get_offset(&iter);
701
  }
702
}
703
704
static void
705
write (HildonIMOneHandFKB *self, const gchar *text)
706
{
707
  HildonIMOneHandFKBPrivate *priv;
708
  GtkTextBuffer *buffer;
709
710
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(self);
711
712
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
713
714
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(self);
715
716
  if (hildon_im_ui_get_commit_mode (priv->ui) == HILDON_IM_COMMIT_REDIRECT)
717
  {
718
    delete_selection (self);
719
    hildon_im_ui_send_utf8(priv->ui, text);
720
  }
721
  
722
  if (hildon_im_ui_get_commit_mode (priv->ui) == HILDON_IM_COMMIT_SURROUNDING) {
723
      hildon_im_ui_send_utf8(priv->ui, text);
724
  }
725
726
  gtk_text_buffer_insert_at_cursor (buffer, text, -1);
727
728
  gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (priv->text_view),
729
                                      gtk_text_buffer_get_insert (buffer));
730
}
731
732
static GtkWidget *
733
create_special_key (HildonIMOneHandFKB *self,
734
                    HildonSizeType size,
735
                    gchar *icon_name,
736
                    GCallback callback)
737
{
738
  GtkWidget *image;
739
  GtkWidget *key;
740
741
  key = hildon_gtk_button_new (size);
742
743
  if (icon_name != NULL)
744
  {
745
    image = gtk_image_new_from_icon_name (icon_name, -1);
746
    gtk_container_add (GTK_CONTAINER (key), image);
747
  }
748
749
  if (callback != NULL)
750
  {
751
    g_signal_connect (key,
752
                      "clicked",
753
                      G_CALLBACK (callback),
754
                      self);
755
  }
756
757
  return key;
758
}
759
760
761
static void
762
set_fkb_size (HildonIMOneHandFKB *self)
763
{
764
  HildonIMOneHandFKBPrivate *priv;
765
  GdkScreen *screen;
766
767
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(self);
768
  screen = gdk_screen_get_default ();
769
770
  priv->fkb_width = gdk_screen_get_width (screen);
771
  priv->fkb_height = gdk_screen_get_height (screen);
772
}
773
774
775
776
static gchar *
777
get_first_char (gchar *full_string)
778
{
779
  gunichar chr = g_utf8_get_char_validated (full_string, -1);
780
  gchar character[7];
781
  int len = g_unichar_to_utf8 (chr, character);
782
  character[len] = '\0';
783
784
  return g_strndup (character, len);
785
}
786
787
static void
788
clear_timeout_id (HildonIMOneHandFKB *self)
789
{
790
  HildonIMOneHandFKBPrivate *priv;
791
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
792
793
  if (priv->timeout_id > 0)
794
  {
795
    g_source_remove (priv->timeout_id);
796
  }
797
}
798
799
static gboolean
800
press_expired (gpointer data)
801
{
802
  HildonIMOneHandFKB *self = HILDON_IM_ONEHAND_FKB (data);
803
  HildonIMOneHandFKBPrivate *priv;
804
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
805
806
  priv->replace_previous_char = FALSE;
807
  priv->slide_index = -1;
808
  priv->repeating_key = NULL;
809
810
  const gchar *text = g_strdup (g_object_get_data (G_OBJECT (priv->pressed_key),
811
                                                   PERSISTENT_VALUE_DATA));
812
813
  if (text == NULL)
814
  {
815
    gchar *key_values = g_object_get_data (G_OBJECT (priv->pressed_key),
816
                                           KEYS_VALUES_DATA);
817
    text = g_strdup (get_first_char (key_values));
818
  }
819
820
  if (text != NULL)
821
  {
822
    write (self, text);
823
  }
824
825
  priv->pressed_key = NULL;
826
827
  return FALSE;
828
}
829
830
static void
831
sliding_key_pressed_cb (GtkWidget *key, gpointer data)
832
{
833
  HildonIMOneHandFKB *self = HILDON_IM_ONEHAND_FKB (data);
834
  HildonIMOneHandFKBPrivate *priv;
835
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
836
837
  clear_timeout_id (self);
838
  priv->pressed_key = key;
839
  priv->timeout_id = g_timeout_add (SLIDING_TIMEOUT, press_expired, self);
840
}
841
842
static void change_keys (HildonIMOneHandFKB *self) {
843
  HildonIMOneHandFKBPrivate *priv;
844
845
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
846
847
  if (priv->keyboard_page == 0) {
848
    gtk_widget_show (priv->caps_button); 
849
  } else {
850
    gtk_widget_hide (priv->caps_button);
851
  }
852
853
  int i = 0;
854
  while (i < G_N_ELEMENTS (qwerty_keys)) {
855
856
    gchar* label = (priv->keyboard_page == 0) ? qwerty_keys[i] : qwerty_symbol_keys[i];
857
    
858
    g_object_set_data (G_OBJECT (priv->keyboard_keys[i]), KEYS_VALUES_DATA, label);
859
    g_object_set_data (G_OBJECT (priv->keyboard_keys[i]), PERSISTENT_VALUE_DATA, label);
860
861
    hildon_button_set_title (HILDON_BUTTON (priv->keyboard_keys[i]), label);
862
    i++;
863
  }
864
}
865
866
static void 
867
swap_keyboard_page (HildonIMOneHandFKB *self)
868
{
869
  HildonIMOneHandFKBPrivate *priv;
870
871
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
872
873
  if (priv->keyboard_page == 0)
874
  {
875
    priv->keyboard_page++;
876
  }
877
  else {
878
    priv->keyboard_page = 0;
879
  }
880
  change_keys(self);
881
}
882
883
static void
884
sliding_keyboard_change_page_cb(GtkWidget *key, gpointer data)
885
{
886
  HildonIMOneHandFKB *self = HILDON_IM_ONEHAND_FKB (data);
887
  swap_keyboard_page (self);
888
}
889
890
static gboolean
891
slide_expired (gpointer data)
892
{
893
  HildonIMOneHandFKB *self = HILDON_IM_ONEHAND_FKB (data);
894
  HildonIMOneHandFKBPrivate *priv;
895
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
896
897
  priv->replace_previous_char = FALSE;
898
  priv->slide_index = -1;
899
  priv->repeating_key = NULL;
900
901
  return FALSE;
902
}
903
904
static void
905
sliding_key_released_cb (GtkWidget *key, gpointer data)
906
{
907
  const gchar *key_value = NULL;
908
  gchar *sub_str = NULL;
909
  gint nr_values = 1;
910
  gboolean is_slide_key = TRUE;
911
  gboolean caps_on = FALSE;
912
  HildonIMOneHandFKB *self = HILDON_IM_ONEHAND_FKB (data);
913
  HildonIMOneHandFKBPrivate *priv;
914
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
915
916
  if (priv->pressed_key == NULL)
917
  {
918
    return;
919
  }
920
921
  caps_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->caps_button));
922
923
  key_value = g_object_get_data (G_OBJECT (key), KEYS_VALUES_DATA);
924
  nr_values = g_utf8_strlen (key_value, -1);
925
926
  if (nr_values <= 1)
927
  {
928
    is_slide_key = FALSE;
929
  }
930
931
  clear_timeout_id (self);
932
933
  if (is_slide_key)
934
  {
935
    if (priv->repeating_key == key)
936
    {
937
      priv->slide_index++;
938
    }
939
    else
940
    {
941
      priv->slide_index = 0;
942
    }
943
944
    if (priv->slide_index >= nr_values)
945
    {
946
      priv->slide_index = 0;
947
    }
948
949
    sub_str = g_utf8_offset_to_pointer (key_value, priv->slide_index);
950
    key_value = g_strdup (get_first_char (sub_str));
951
952
    if (key_value == NULL)
953
      return;
954
955
    if (priv->replace_previous_char && priv->repeating_key == key)
956
    {
957
      backspace (self);
958
    }
959
960
    priv->timeout_id = g_timeout_add (SLIDING_TIMEOUT, slide_expired, self);
961
    priv->replace_previous_char = TRUE;
962
  }
963
964
  priv->repeating_key = key;
965
  write (self, caps_on ? g_utf8_strup (key_value, -1) : key_value);
966
}
967
968
static GtkWidget *
969
create_sliding_key (HildonIMOneHandFKB *self, gchar *title, gchar *key_values, gint visible_offset)
970
{
971
  gchar *label = key_values;
972
  GtkWidget *key = hildon_button_new (HILDON_SIZE_THUMB_HEIGHT,
973
                                      HILDON_BUTTON_ARRANGEMENT_VERTICAL);
974
975
  g_object_set_data (G_OBJECT (key), KEYS_VALUES_DATA, key_values);
976
  g_object_set_data (G_OBJECT (key), PERSISTENT_VALUE_DATA, title);
977
978
  if (visible_offset > 0)
979
  {
980
    gint byte_count = g_utf8_offset_to_pointer (key_values, visible_offset) - key_values;
981
    label = g_strndup (key_values, byte_count);
982
  }
983
984
  //hildon_button_set_value (HILDON_BUTTON (key), label);
985
  hildon_button_set_title (HILDON_BUTTON (key), label);
986
987
  hildon_button_set_title_alignment (HILDON_BUTTON (key), 0.5, 0.5);
988
989
  g_signal_connect (key,
990
                    "pressed",
991
                    G_CALLBACK (sliding_key_pressed_cb),
992
                    self);
993
994
  g_signal_connect (key,
995
                    "released",
996
                    G_CALLBACK (sliding_key_released_cb),
997
                    self);
998
999
  return key;
1000
}
1001
1002
static void
1003
unparent_contents (HildonIMOneHandFKB *self)
1004
{
1005
  GtkWidget *container;
1006
  HildonIMOneHandFKBPrivate *priv;
1007
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
1008
1009
  container = gtk_bin_get_child (GTK_BIN (priv->window));
1010
1011
  if (!GTK_IS_WIDGET (container))
1012
  {
1013
    return;
1014
  }
1015
1016
  g_object_ref (priv->text_view);
1017
  g_object_ref (priv->keyboard);
1018
1019
  gtk_container_remove (GTK_CONTAINER (container), priv->text_view);
1020
  gtk_container_remove (GTK_CONTAINER (container), priv->keyboard);
1021
  //gtk_container_remove (GTK_CONTAINER (container), priv->keyboard_qwerty);
1022
  gtk_container_remove (GTK_CONTAINER (priv->window), container);
1023
}
1024
1025
static void
1026
set_kb_mode (HildonIMOneHandFKB *self, ScreenMode mode)
1027
{
1028
  //CURRENTLY BROKEN FOR LAYOUT CHANGE REINIT
1029
  GtkWidget *container, *text_area;
1030
  HildonIMOneHandFKBPrivate *priv;
1031
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE (self);
1032
1033
  unparent_contents (self);
1034
1035
  if (mode == LANDSCAPE)
1036
  {
1037
    container = gtk_vbox_new (FALSE, 0); //swapped to vertical alignment, better qwerty
1038
    gtk_widget_set_size_request (priv->text_view, 100, -1);
1039
  }
1040
  else
1041
  {
1042
    gtk_widget_set_size_request (priv->text_view, -1, 400);
1043
    container = gtk_vbox_new (FALSE, 0);
1044
  }
1045
1046
  text_area = gtk_scrolled_window_new (NULL, NULL);
1047
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (text_area),
1048
                                  GTK_POLICY_AUTOMATIC,
1049
                                  GTK_POLICY_AUTOMATIC);
1050
  gtk_container_add (GTK_CONTAINER (text_area), priv->text_view);
1051
  gtk_container_add (GTK_CONTAINER (container), text_area);
1052
  gtk_container_add (GTK_CONTAINER (container), priv->keyboard);
1053
  //gtk_container_add (GTK_CONTAINER (container), priv->keyboard_symbols);
1054
  gtk_container_add (GTK_CONTAINER (priv->window), container);
1055
}
1056
1057
static GtkWidget *
1058
create_sliding_keyboard_landscape (HildonIMOneHandFKB *self, gchar **keys, gint page)
1059
{
1060
  HildonIMOneHandFKBPrivate *priv;
1061
  GtkWidget *image;
1062
  GtkWidget *key;
1063
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(self);
1064
  GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
1065
  GtkWidget *contents = gtk_vbox_new (FALSE, 0);
1066
1067
  GtkWidget *row = gtk_hbox_new (FALSE, 0);
1068
1069
  row = gtk_hbox_new (FALSE, 0);
1070
  gint num_keys = G_N_ELEMENTS (qwerty_keys);
1071
  gint i;
1072
  
1073
  for (i = 0; i < num_keys; i++)
1074
  {
1075
    gint visible_offset = 4; 
1076
    
1077
    if (i + 1 == 21) {
1078
      if (page == 0) {
1079
	priv->caps_button = hildon_gtk_toggle_button_new (HILDON_SIZE_THUMB_HEIGHT);
1080
	image = gtk_image_new_from_icon_name ("keyboard_move_up", -1);
1081
	gtk_container_add (GTK_CONTAINER (priv->caps_button), image);
1082
	  
1083
	gtk_size_group_add_widget (group, priv->caps_button);
1084
	  
1085
	gtk_box_pack_start (GTK_BOX (row), priv->caps_button, TRUE, TRUE, 0);
1086
      }
1087
      else {
1088
	key = create_sliding_key (self, NULL, ".,", visible_offset);
1089
	gtk_size_group_add_widget (group, key);
1090
	gtk_box_pack_start (GTK_BOX (row), key, TRUE, TRUE, 0);
1091
      }
1092
    }
1093
    
1094
    priv->keyboard_keys[i] =
1095
      create_sliding_key (self, NULL, keys[i], visible_offset);
1096
    gtk_size_group_add_widget (group, priv->keyboard_keys[i]);
1097
    gtk_box_pack_start (GTK_BOX (row), priv->keyboard_keys[i], TRUE, TRUE, 0);
1098
1099
    if (i + 1 == 28){
1100
      key = create_special_key (self,
1101
				HILDON_SIZE_THUMB_HEIGHT,
1102
				"keyboard_backspace",
1103
				G_CALLBACK (backspace_pressed_cb));
1104
      gtk_size_group_add_widget (group, key);
1105
      gtk_box_pack_start (GTK_BOX (row), key, TRUE, TRUE, 0);	
1106
    }
1107
    
1108
1109
    if (i+1 == 10 || i+1 == 20) {
1110
      gtk_box_pack_start (GTK_BOX (contents), row, TRUE, TRUE, 0);
1111
      row = gtk_hbox_new (FALSE, 0);
1112
    }
1113
  }	
1114
1115
  gtk_box_pack_start (GTK_BOX (contents), row, TRUE, TRUE, 0);
1116
  row = gtk_hbox_new (FALSE, 0);
1117
1118
1119
1120
  //symbol page toggle
1121
  key = create_special_key (self,
1122
		    HILDON_SIZE_THUMB_HEIGHT,
1123
		    "general_refresh",
1124
		    G_CALLBACK (sliding_keyboard_change_page_cb));  
1125
  gtk_size_group_add_widget (group, key);
1126
1127
  gtk_box_pack_start (GTK_BOX (row), key, TRUE, TRUE, 0);
1128
  gtk_box_pack_start (GTK_BOX (contents), row, TRUE, TRUE, 0);
1129
1130
  key = create_sliding_key (self, NULL, " ", 8);
1131
  gtk_widget_set_size_request (key, 100 , 32);
1132
  gtk_size_group_add_widget (group, key);
1133
  gtk_box_pack_start (GTK_BOX (row), key, TRUE, TRUE, 0);
1134
1135
  key = create_special_key (self,
1136
			    HILDON_SIZE_THUMB_HEIGHT,
1137
			    "keyboard_close",
1138
			    G_CALLBACK (close_fkb_cb));
1139
  
1140
  gtk_size_group_add_widget (group, key);
1141
  gtk_box_pack_start (GTK_BOX (row), key, TRUE, TRUE, 0);
1142
1143
1144
1145
  key = create_special_key (self,
1146
			    HILDON_SIZE_THUMB_HEIGHT,
1147
			    "keyboard_enter",
1148
			    G_CALLBACK (enter_key_pressed_cb));
1149
  gtk_size_group_add_widget (group, key);
1150
1151
  gtk_box_pack_start (GTK_BOX (row), key, TRUE, TRUE, 0);
1152
  gtk_box_pack_start (GTK_BOX (contents), row, TRUE, TRUE, 0);
1153
1154
  return contents;
1155
}
1156
1157
static void
1158
create_window (HildonIMOneHandFKB *self)
1159
{
1160
  HildonIMOneHandFKBPrivate *priv;
1161
  priv = HILDON_IM_ONEHAND_FKB_GET_PRIVATE(self);
1162
1163
  priv->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1164
1165
  gtk_window_set_type_hint (GTK_WINDOW (priv->window), GDK_WINDOW_TYPE_HINT_DIALOG);
1166
  gtk_window_set_decorated (GTK_WINDOW (priv->window), FALSE);
1167
  gtk_window_fullscreen (GTK_WINDOW (priv->window));
1168
  hildon_gtk_window_set_portrait_flags (GTK_WINDOW (priv->window), 
1169
                                        priv->screen_mode == PORTRAIT ?
1170
                                        HILDON_PORTRAIT_MODE_REQUEST :
1171
                                        HILDON_PORTRAIT_MODE_SUPPORT);
1172
1173
  priv->text_view = gtk_text_view_new ();
1174
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->text_view), GTK_WRAP_WORD_CHAR);
1175
  gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (priv->text_view), TRUE);
1176
1177
  gtk_widget_set_name (priv->text_view, "him-textview");
1178
  g_signal_connect (priv->text_view,
1179
                    "button-press-event",
1180
                    G_CALLBACK (textview_button_press_cb),
1181
                    self);
1182
 
1183
  set_kb_mode (self, priv->screen_mode);
1184
}