evolution-3.6.4/e-util/e-text-event-processor-emacs-like.c

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 }