No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Chris Lahey <clahey@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28
29 #include <gdk/gdkkeysyms.h>
30
31 #include "e-text-event-processor-emacs-like.h"
32 #include "e-util.h"
33
34 static gint e_text_event_processor_emacs_like_event
35 (ETextEventProcessor *tep,
36 ETextEventProcessorEvent *event);
37
38 G_DEFINE_TYPE (
39 ETextEventProcessorEmacsLike,
40 e_text_event_processor_emacs_like,
41 E_TEXT_EVENT_PROCESSOR_TYPE)
42
43 static const ETextEventProcessorCommand control_keys[26] = {
44 { E_TEP_START_OF_LINE, E_TEP_MOVE, 0, "" }, /* a */
45 { E_TEP_BACKWARD_CHARACTER, E_TEP_MOVE, 0, "" }, /* b */
46 { E_TEP_SELECTION, E_TEP_COPY, 0, "" }, /* c */
47 { E_TEP_FORWARD_CHARACTER, E_TEP_DELETE, 0, "" }, /* d */
48 { E_TEP_END_OF_LINE, E_TEP_MOVE, 0, "" }, /* e */
49 { E_TEP_FORWARD_CHARACTER, E_TEP_MOVE, 0, "" }, /* f */
50 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* g */
51 { E_TEP_BACKWARD_CHARACTER, E_TEP_DELETE, 0, "" }, /* h */
52 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* i */
53 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* j */
54 { E_TEP_END_OF_LINE, E_TEP_DELETE, 0, "" }, /* k */
55 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* l */
56 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* m */
57 { E_TEP_FORWARD_LINE, E_TEP_MOVE, 0, "" }, /* n */
58 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* o */
59 { E_TEP_BACKWARD_LINE, E_TEP_MOVE, 0, "" }, /* p */
60 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* q */
61 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* r */
62 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* s */
63 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* t */
64 { E_TEP_START_OF_LINE, E_TEP_DELETE, 0, "" }, /* u */
65 { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* v */
66 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* w */
67 { E_TEP_SELECTION, E_TEP_DELETE, 0, "" }, /* x */
68 { E_TEP_SELECTION, E_TEP_PASTE, 0, "" }, /* y */
69 { E_TEP_SELECTION, E_TEP_NOP, 0, "" } /* z */
70 };
71
72 static const ETextEventProcessorCommand alt_keys[26] = {
73 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* a */
74 { E_TEP_BACKWARD_WORD, E_TEP_MOVE, 0, "" }, /* b */
75 { E_TEP_SELECTION, E_TEP_CAPS, E_TEP_CAPS_TITLE, "" },/* c */
76 { E_TEP_FORWARD_WORD, E_TEP_DELETE, 0, "" }, /* d */
77 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* e */
78 { E_TEP_FORWARD_WORD, E_TEP_MOVE, 0, "" }, /* f */
79 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* g */
80 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* h */
81 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* i */
82 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* j */
83 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* k */
84 { E_TEP_SELECTION, E_TEP_CAPS, E_TEP_CAPS_LOWER, "" }, /* l */
85 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* m */
86 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* n */
87 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* o */
88 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* p */
89 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* q */
90 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* r */
91 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* s */
92 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* t */
93 { E_TEP_SELECTION, E_TEP_CAPS, E_TEP_CAPS_UPPER, "" }, /* u */
94 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* v */
95 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* w */
96 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* x */
97 { E_TEP_SELECTION, E_TEP_NOP, 0, "" }, /* y */
98 { E_TEP_SELECTION, E_TEP_NOP, 0, "" } /* z */
99
100 };
101
102 static void
103 e_text_event_processor_emacs_like_class_init (ETextEventProcessorEmacsLikeClass *class)
104 {
105 ETextEventProcessorClass *processor_class;
106
107 processor_class = (ETextEventProcessorClass *) class;
108
109 processor_class->event = e_text_event_processor_emacs_like_event;
110 }
111
112 static void
113 e_text_event_processor_emacs_like_init (ETextEventProcessorEmacsLike *tep)
114 {
115 }
116
117 static gint
118 e_text_event_processor_emacs_like_event (ETextEventProcessor *tep,
119 ETextEventProcessorEvent *event)
120 {
121 ETextEventProcessorCommand command;
122 ETextEventProcessorEmacsLike *tep_el = E_TEXT_EVENT_PROCESSOR_EMACS_LIKE (tep);
123 command.action = E_TEP_NOP;
124
125 /* Warning from the Intel compiler here:
126 * e-text-event-processor-emacs-like.c(136): warning #589:
127 * transfer of control bypasses initialization of:
128 * variable "key" (declared at line 194)
129 * switch (event->type) {
130 * ^
131 */
132 switch (event->type) {
133 case GDK_BUTTON_PRESS:
134 if (event->button.button == 1 || event->button.button == 2) {
135 command.action = E_TEP_GRAB;
136 command.time = event->button.time;
137 g_signal_emit_by_name (tep, "command", &command);
138 if (event->button.button == 1 && event->button.state & GDK_SHIFT_MASK)
139 command.action = E_TEP_SELECT;
140 else
141 command.action = E_TEP_MOVE;
142 command.position = E_TEP_VALUE;
143 command.value = event->button.position;
144 command.time = event->button.time;
145 tep_el->mouse_down = event->button.button == 1;
146 }
147 break;
148
149 case GDK_2BUTTON_PRESS:
150 if (event->button.button == 1) {
151 command.action = E_TEP_SELECT;
152 command.position = E_TEP_SELECT_WORD;
153 command.time = event->button.time;
154 }
155 break;
156
157 case GDK_3BUTTON_PRESS:
158 if (event->button.button == 1) {
159 command.action = E_TEP_SELECT;
160 command.position = E_TEP_SELECT_ALL;
161 command.time = event->button.time;
162 }
163 break;
164
165 case GDK_BUTTON_RELEASE:
166 if (event->button.button == 1) {
167 command.action = E_TEP_UNGRAB;
168 command.time = event->button.time;
169 tep_el->mouse_down = FALSE;
170 } else if (event->button.button == 2) {
171 command.action = E_TEP_MOVE;
172 command.position = E_TEP_VALUE;
173 command.value = event->button.position;
174 command.time = event->button.time;
175 g_signal_emit_by_name (tep, "command", &command);
176
177 command.action = E_TEP_GET_SELECTION;
178 command.position = E_TEP_SELECTION;
179 command.value = 0;
180 command.time = event->button.time;
181 }
182 break;
183
184 case GDK_MOTION_NOTIFY:
185 if (tep_el->mouse_down) {
186 command.action = E_TEP_SELECT;
187 command.position = E_TEP_VALUE;
188 command.time = event->motion.time;
189 command.value = event->motion.position;
190 }
191 break;
192
193 case GDK_KEY_PRESS:
194 {
195 ETextEventProcessorEventKey key = event->key;
196
197 command.time = event->key.time;
198
199 if (key.state & GDK_SHIFT_MASK)
200 command.action = E_TEP_SELECT;
201 else if (key.state & GDK_MOD1_MASK)
202 command.action = E_TEP_NOP;
203 else
204 command.action = E_TEP_MOVE;
205
206 switch (key.keyval) {
207 case GDK_KEY_Home:
208 case GDK_KEY_KP_Home:
209 if (key.state & GDK_CONTROL_MASK)
210 command.position = E_TEP_START_OF_BUFFER;
211 else
212 command.position = E_TEP_START_OF_LINE;
213 break;
214
215 case GDK_KEY_End:
216 case GDK_KEY_KP_End:
217 if (key.state & GDK_CONTROL_MASK)
218 command.position = E_TEP_END_OF_BUFFER;
219 else
220 command.position = E_TEP_END_OF_LINE;
221 break;
222
223 case GDK_KEY_Page_Up:
224 case GDK_KEY_KP_Page_Up:
225 command.position = E_TEP_BACKWARD_PAGE;
226 break;
227
228 case GDK_KEY_Page_Down:
229 case GDK_KEY_KP_Page_Down:
230 command.position = E_TEP_FORWARD_PAGE;
231 break;
232
233 /* CUA has Ctrl-Up/Ctrl-Down as paragraph up down */
234 case GDK_KEY_Up:
235 case GDK_KEY_KP_Up:
236 command.position = E_TEP_BACKWARD_LINE;
237 break;
238
239 case GDK_KEY_Down:
240 case GDK_KEY_KP_Down:
241 command.position = E_TEP_FORWARD_LINE;
242 break;
243
244 case GDK_KEY_Left:
245 case GDK_KEY_KP_Left:
246 if (key.state & GDK_CONTROL_MASK)
247 command.position = E_TEP_BACKWARD_WORD;
248 else
249 command.position = E_TEP_BACKWARD_CHARACTER;
250 break;
251
252 case GDK_KEY_Right:
253 case GDK_KEY_KP_Right:
254 if (key.state & GDK_CONTROL_MASK)
255 command.position = E_TEP_FORWARD_WORD;
256 else
257 command.position = E_TEP_FORWARD_CHARACTER;
258 break;
259
260 case GDK_KEY_BackSpace:
261 command.action = E_TEP_DELETE;
262 if (key.state & GDK_CONTROL_MASK)
263 command.position = E_TEP_BACKWARD_WORD;
264 else
265 command.position = E_TEP_BACKWARD_CHARACTER;
266 break;
267
268 case GDK_KEY_Clear:
269 command.action = E_TEP_DELETE;
270 command.position = E_TEP_END_OF_LINE;
271 break;
272
273 case GDK_KEY_Insert:
274 case GDK_KEY_KP_Insert:
275 if (key.state & GDK_SHIFT_MASK) {
276 command.action = E_TEP_PASTE;
277 command.position = E_TEP_SELECTION;
278 } else if (key.state & GDK_CONTROL_MASK) {
279 command.action = E_TEP_COPY;
280 command.position = E_TEP_SELECTION;
281 } else {
282 /* gtk_toggle_insert(text) -- IMPLEMENT -- FIXME */
283 }
284 break;
285
286 case GDK_KEY_F16:
287 command.action = E_TEP_COPY;
288 command.position = E_TEP_SELECTION;
289 break;
290
291 case GDK_KEY_F18:
292 command.action = E_TEP_PASTE;
293 command.position = E_TEP_SELECTION;
294 break;
295
296 case GDK_KEY_F20:
297 command.action = E_TEP_COPY;
298 command.position = E_TEP_SELECTION;
299 g_signal_emit_by_name (
300 tep, "command", &command);
301
302 command.action = E_TEP_DELETE;
303 command.position = E_TEP_SELECTION;
304 break;
305
306 case GDK_KEY_Delete:
307 case GDK_KEY_KP_Delete:
308 if (key.state & GDK_CONTROL_MASK) {
309 command.action = E_TEP_DELETE;
310 command.position = E_TEP_FORWARD_WORD;
311 } else if (key.state & GDK_SHIFT_MASK) {
312 command.action = E_TEP_COPY;
313 command.position = E_TEP_SELECTION;
314 g_signal_emit_by_name (
315 tep, "command", &command);
316
317 command.action = E_TEP_DELETE;
318 command.position = E_TEP_SELECTION;
319 } else {
320 command.action = E_TEP_DELETE;
321 command.position = E_TEP_FORWARD_CHARACTER;
322 }
323 break;
324
325 case GDK_KEY_Tab:
326 case GDK_KEY_KP_Tab:
327 case GDK_KEY_ISO_Left_Tab:
328 case GDK_KEY_3270_BackTab:
329 /* Don't insert literally */
330 command.action = E_TEP_NOP;
331 command.position = E_TEP_SELECTION;
332 break;
333
334 case GDK_KEY_Return:
335 case GDK_KEY_KP_Enter:
336 if (tep->allow_newlines) {
337 if (key.state & GDK_CONTROL_MASK) {
338 command.action = E_TEP_ACTIVATE;
339 command.position = E_TEP_SELECTION;
340 } else {
341 command.action = E_TEP_INSERT;
342 command.position = E_TEP_SELECTION;
343 command.value = 1;
344 command.string = "\n";
345 }
346 } else {
347 if (key.state & GDK_CONTROL_MASK) {
348 command.action = E_TEP_NOP;
349 command.position = E_TEP_SELECTION;
350 } else {
351 command.action = E_TEP_ACTIVATE;
352 command.position = E_TEP_SELECTION;
353 }
354 }
355 break;
356
357 case GDK_KEY_Escape:
358 /* Don't insert literally */
359 command.action = E_TEP_NOP;
360 command.position = E_TEP_SELECTION;
361 break;
362
363 case GDK_KEY_KP_Space:
364 command.action = E_TEP_INSERT;
365 command.position = E_TEP_SELECTION;
366 command.value = 1;
367 command.string = " ";
368 break;
369
370 case GDK_KEY_KP_Equal:
371 command.action = E_TEP_INSERT;
372 command.position = E_TEP_SELECTION;
373 command.value = 1;
374 command.string = "=";
375 break;
376
377 case GDK_KEY_KP_Multiply:
378 command.action = E_TEP_INSERT;
379 command.position = E_TEP_SELECTION;
380 command.value = 1;
381 command.string = "*";
382 break;
383
384 case GDK_KEY_KP_Add:
385 command.action = E_TEP_INSERT;
386 command.position = E_TEP_SELECTION;
387 command.value = 1;
388 command.string = "+";
389 break;
390
391 case GDK_KEY_KP_Subtract:
392 command.action = E_TEP_INSERT;
393 command.position = E_TEP_SELECTION;
394 command.value = 1;
395 command.string = "-";
396 break;
397
398 case GDK_KEY_KP_Decimal:
399 command.action = E_TEP_INSERT;
400 command.position = E_TEP_SELECTION;
401 command.value = 1;
402 command.string = ".";
403 break;
404
405 case GDK_KEY_KP_Divide:
406 command.action = E_TEP_INSERT;
407 command.position = E_TEP_SELECTION;
408 command.value = 1;
409 command.string = "/";
410 break;
411
412 case GDK_KEY_KP_0:
413 command.action = E_TEP_INSERT;
414 command.position = E_TEP_SELECTION;
415 command.value = 1;
416 command.string = "0";
417 break;
418
419 case GDK_KEY_KP_1:
420 command.action = E_TEP_INSERT;
421 command.position = E_TEP_SELECTION;
422 command.value = 1;
423 command.string = "1";
424 break;
425
426 case GDK_KEY_KP_2:
427 command.action = E_TEP_INSERT;
428 command.position = E_TEP_SELECTION;
429 command.value = 1;
430 command.string = "2";
431 break;
432
433 case GDK_KEY_KP_3:
434 command.action = E_TEP_INSERT;
435 command.position = E_TEP_SELECTION;
436 command.value = 1;
437 command.string = "3";
438 break;
439
440 case GDK_KEY_KP_4:
441 command.action = E_TEP_INSERT;
442 command.position = E_TEP_SELECTION;
443 command.value = 1;
444 command.string = "4";
445 break;
446
447 case GDK_KEY_KP_5:
448 command.action = E_TEP_INSERT;
449 command.position = E_TEP_SELECTION;
450 command.value = 1;
451 command.string = "5";
452 break;
453
454 case GDK_KEY_KP_6:
455 command.action = E_TEP_INSERT;
456 command.position = E_TEP_SELECTION;
457 command.value = 1;
458 command.string = "6";
459 break;
460
461 case GDK_KEY_KP_7:
462 command.action = E_TEP_INSERT;
463 command.position = E_TEP_SELECTION;
464 command.value = 1;
465 command.string = "7";
466 break;
467
468 case GDK_KEY_KP_8:
469 command.action = E_TEP_INSERT;
470 command.position = E_TEP_SELECTION;
471 command.value = 1;
472 command.string = "8";
473 break;
474
475 case GDK_KEY_KP_9:
476 command.action = E_TEP_INSERT;
477 command.position = E_TEP_SELECTION;
478 command.value = 1;
479 command.string = "9";
480 break;
481
482 default:
483 if ((key.state & GDK_CONTROL_MASK) &&
484 !(key.state & GDK_MOD1_MASK)) {
485 if ((key.keyval >= 'A') && (key.keyval <= 'Z'))
486 key.keyval -= 'A' - 'a';
487
488 if ((key.keyval >= 'a') && (key.keyval <= 'z')) {
489 command.position = control_keys[(gint) (key.keyval - 'a')].position;
490 if (control_keys[(gint) (key.keyval - 'a')].action != E_TEP_MOVE)
491 command.action = control_keys[(gint) (key.keyval - 'a')].action;
492 command.value = control_keys[(gint) (key.keyval - 'a')].value;
493 command.string = control_keys[(gint) (key.keyval - 'a')].string;
494 }
495
496 if (key.keyval == ' ') {
497 command.action = E_TEP_NOP;
498 }
499
500 if (key.keyval == 'x') {
501 command.action = E_TEP_COPY;
502 command.position = E_TEP_SELECTION;
503 g_signal_emit_by_name (tep, "command", &command);
504
505 command.action = E_TEP_DELETE;
506 command.position = E_TEP_SELECTION;
507 }
508
509 break;
510
511 } else if ((key.state & GDK_MOD1_MASK) &&
512 !(key.state & GDK_CONTROL_MASK)) {
513 if ((key.keyval >= 'A') && (key.keyval <= 'Z'))
514 key.keyval -= 'A' - 'a';
515
516 if ((key.keyval >= 'a') && (key.keyval <= 'z')) {
517 command.position = alt_keys[(gint) (key.keyval - 'a')].position;
518 if (alt_keys[(gint) (key.keyval - 'a')].action != E_TEP_MOVE)
519 command.action = alt_keys[(gint) (key.keyval - 'a')].action;
520 command.value = alt_keys[(gint) (key.keyval - 'a')].value;
521 command.string = alt_keys[(gint) (key.keyval - 'a')].string;
522 }
523 } else if (!(key.state & GDK_MOD1_MASK) &&
524 !(key.state & GDK_CONTROL_MASK) &&
525 key.length > 0) {
526 if (key.keyval >= GDK_KEY_KP_0 &&
527 key.keyval <= GDK_KEY_KP_9) {
528 key.keyval = '0';
529 key.string = "0";
530 }
531 command.action = E_TEP_INSERT;
532 command.position = E_TEP_SELECTION;
533 command.value = strlen (key.string);
534 command.string = key.string;
535
536 } else {
537 command.action = E_TEP_NOP;
538 }
539 }
540 break;
541
542 case GDK_KEY_RELEASE:
543 command.time = event->key.time;
544 command.action = E_TEP_NOP;
545 break;
546
547 default:
548 command.action = E_TEP_NOP;
549 break;
550 }
551 }
552
553 if (command.action != E_TEP_NOP) {
554 g_signal_emit_by_name (tep, "command", &command);
555 return 1;
556 } else
557 return 0;
558 }
559
560 ETextEventProcessor *
561 e_text_event_processor_emacs_like_new (void)
562 {
563 return g_object_new (E_TEXT_EVENT_PROCESSOR_EMACS_LIKE_TYPE, NULL);
564 }