evolution-3.6.4/calendar/gui/e-alarm-list.c

No issues found

  1 /*
  2  * EAlarmList - list of calendar alarms with GtkTreeModel interface.
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  *
 18  * Authors:
 19  *		Hans Petter Jansson  <hpj@ximian.com>
 20  *
 21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 22  *
 23  */
 24 
 25 #ifdef HAVE_CONFIG_H
 26 #include <config.h>
 27 #endif
 28 
 29 #include <string.h>
 30 #include <glib/gi18n.h>
 31 
 32 #include "calendar-config.h"
 33 #include "e-alarm-list.h"
 34 
 35 #define G_LIST(x)                    ((GList *) x)
 36 #define E_ALARM_LIST_IS_SORTED(list) (E_ALARM_LIST (list)->sort_column_id != -2)
 37 #define IS_VALID_ITER(dt_list, iter) (iter!= NULL && iter->user_data != NULL && \
 38                                       dt_list->stamp == iter->stamp)
 39 
 40 static GType column_types[E_ALARM_LIST_NUM_COLUMNS];
 41 
 42 static void         e_alarm_list_tree_model_init (GtkTreeModelIface  *iface);
 43 static GtkTreeModelFlags e_alarm_list_get_flags       (GtkTreeModel       *tree_model);
 44 static gint         e_alarm_list_get_n_columns   (GtkTreeModel       *tree_model);
 45 static GType        e_alarm_list_get_column_type (GtkTreeModel       *tree_model,
 46 						  gint                index);
 47 static gboolean     e_alarm_list_get_iter        (GtkTreeModel       *tree_model,
 48 						  GtkTreeIter        *iter,
 49 						  GtkTreePath        *path);
 50 static GtkTreePath *e_alarm_list_get_path        (GtkTreeModel       *tree_model,
 51 						  GtkTreeIter        *iter);
 52 static void         e_alarm_list_get_value       (GtkTreeModel       *tree_model,
 53 						  GtkTreeIter        *iter,
 54 						  gint                column,
 55 						  GValue             *value);
 56 static gboolean     e_alarm_list_iter_next       (GtkTreeModel       *tree_model,
 57 						  GtkTreeIter        *iter);
 58 static gboolean     e_alarm_list_iter_children   (GtkTreeModel       *tree_model,
 59 						  GtkTreeIter        *iter,
 60 						  GtkTreeIter        *parent);
 61 static gboolean     e_alarm_list_iter_has_child  (GtkTreeModel       *tree_model,
 62 						  GtkTreeIter        *iter);
 63 static gint         e_alarm_list_iter_n_children (GtkTreeModel       *tree_model,
 64 						  GtkTreeIter        *iter);
 65 static gboolean     e_alarm_list_iter_nth_child  (GtkTreeModel       *tree_model,
 66 						  GtkTreeIter        *iter,
 67 						  GtkTreeIter        *parent,
 68 						  gint                n);
 69 static gboolean     e_alarm_list_iter_parent     (GtkTreeModel       *tree_model,
 70 						  GtkTreeIter        *iter,
 71 						  GtkTreeIter        *child);
 72 
 73 G_DEFINE_TYPE_WITH_CODE (
 74 	EAlarmList,
 75 	e_alarm_list,
 76 	G_TYPE_OBJECT,
 77 	G_IMPLEMENT_INTERFACE (
 78 		GTK_TYPE_TREE_MODEL,
 79 		e_alarm_list_tree_model_init))
 80 
 81 static void
 82 e_alarm_list_class_init (EAlarmListClass *class)
 83 {
 84 	column_types[E_ALARM_LIST_COLUMN_DESCRIPTION] = G_TYPE_STRING;
 85 }
 86 
 87 static void
 88 e_alarm_list_tree_model_init (GtkTreeModelIface *iface)
 89 {
 90 	iface->get_flags = e_alarm_list_get_flags;
 91 	iface->get_n_columns = e_alarm_list_get_n_columns;
 92 	iface->get_column_type = e_alarm_list_get_column_type;
 93 	iface->get_iter = e_alarm_list_get_iter;
 94 	iface->get_path = e_alarm_list_get_path;
 95 	iface->get_value = e_alarm_list_get_value;
 96 	iface->iter_next = e_alarm_list_iter_next;
 97 	iface->iter_children = e_alarm_list_iter_children;
 98 	iface->iter_has_child = e_alarm_list_iter_has_child;
 99 	iface->iter_n_children = e_alarm_list_iter_n_children;
100 	iface->iter_nth_child = e_alarm_list_iter_nth_child;
101 	iface->iter_parent = e_alarm_list_iter_parent;
102 }
103 
104 static void
105 e_alarm_list_init (EAlarmList *alarm_list)
106 {
107 	alarm_list->stamp         = g_random_int ();
108 	alarm_list->columns_dirty = FALSE;
109 	alarm_list->list          = NULL;
110 }
111 
112 EAlarmList *
113 e_alarm_list_new (void)
114 {
115 	EAlarmList *alarm_list;
116 
117 	alarm_list = E_ALARM_LIST (g_object_new (e_alarm_list_get_type (), NULL));
118 
119 	return alarm_list;
120 }
121 
122 static void
123 all_rows_deleted (EAlarmList *alarm_list)
124 {
125 	GtkTreePath *path;
126 	gint         i;
127 
128 	if (!alarm_list->list)
129 		return;
130 
131 	path = gtk_tree_path_new ();
132 	i = g_list_length (alarm_list->list);
133 	gtk_tree_path_append_index (path, i);
134 
135 	for (; i >= 0; i--) {
136 		gtk_tree_model_row_deleted (GTK_TREE_MODEL (alarm_list), path);
137 		gtk_tree_path_prev (path);
138 	}
139 
140 	gtk_tree_path_free (path);
141 }
142 
143 static void
144 row_deleted (EAlarmList *alarm_list,
145              gint n)
146 {
147 	GtkTreePath *path;
148 
149 	path = gtk_tree_path_new ();
150 	gtk_tree_path_append_index (path, n);
151 	gtk_tree_model_row_deleted (GTK_TREE_MODEL (alarm_list), path);
152 	gtk_tree_path_free (path);
153 }
154 
155 static void
156 row_added (EAlarmList *alarm_list,
157            gint n)
158 {
159 	GtkTreePath *path;
160 	GtkTreeIter  iter;
161 
162 	path = gtk_tree_path_new ();
163 	gtk_tree_path_append_index (path, n);
164 
165 	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (alarm_list), &iter, path))
166 		gtk_tree_model_row_inserted (GTK_TREE_MODEL (alarm_list), path, &iter);
167 
168 	gtk_tree_path_free (path);
169 }
170 
171 static void
172 row_updated (EAlarmList *alarm_list,
173              gint n)
174 {
175 	GtkTreePath *path;
176 	GtkTreeIter  iter;
177 
178 	path = gtk_tree_path_new ();
179 	gtk_tree_path_append_index (path, n);
180 
181 	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (alarm_list), &iter, path))
182 		gtk_tree_model_row_changed (GTK_TREE_MODEL (alarm_list), path, &iter);
183 
184 	gtk_tree_path_free (path);
185 }
186 
187 /* Fulfill the GtkTreeModel requirements */
188 static GtkTreeModelFlags
189 e_alarm_list_get_flags (GtkTreeModel *tree_model)
190 {
191 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), 0);
192 
193 	return GTK_TREE_MODEL_LIST_ONLY;
194 }
195 
196 static gint
197 e_alarm_list_get_n_columns (GtkTreeModel *tree_model)
198 {
199 	EAlarmList *alarm_list = (EAlarmList *) tree_model;
200 
201 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), 0);
202 
203 	alarm_list->columns_dirty = TRUE;
204 	return E_ALARM_LIST_NUM_COLUMNS;
205 }
206 
207 static GType
208 e_alarm_list_get_column_type (GtkTreeModel *tree_model,
209                               gint index)
210 {
211 	EAlarmList *alarm_list = (EAlarmList *) tree_model;
212 
213 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), G_TYPE_INVALID);
214 	g_return_val_if_fail (index < E_ALARM_LIST_NUM_COLUMNS &&
215 			      index >= 0, G_TYPE_INVALID);
216 
217 	alarm_list->columns_dirty = TRUE;
218 	return column_types[index];
219 }
220 
221 const ECalComponentAlarm *
222 e_alarm_list_get_alarm (EAlarmList *alarm_list,
223                         GtkTreeIter *iter)
224 {
225 	g_return_val_if_fail (IS_VALID_ITER (alarm_list, iter), NULL);
226 
227 	return G_LIST (iter->user_data)->data;
228 }
229 
230 static void
231 free_alarm (ECalComponentAlarm *alarm)
232 {
233 	e_cal_component_alarm_free (alarm);
234 }
235 
236 static ECalComponentAlarm *
237 copy_alarm (const ECalComponentAlarm *alarm)
238 {
239 	return e_cal_component_alarm_clone ((ECalComponentAlarm *) alarm);
240 }
241 
242 void
243 e_alarm_list_set_alarm (EAlarmList *alarm_list,
244                         GtkTreeIter *iter,
245                         const ECalComponentAlarm *alarm)
246 {
247 	ECalComponentAlarm *alarm_old;
248 
249 	g_return_if_fail (IS_VALID_ITER (alarm_list, iter));
250 
251 	alarm_old = G_LIST (iter->user_data)->data;
252 	free_alarm (alarm_old);
253 	G_LIST (iter->user_data)->data = copy_alarm (alarm);
254 	row_updated (alarm_list, g_list_position (alarm_list->list, G_LIST (iter->user_data)));
255 }
256 
257 void
258 e_alarm_list_append (EAlarmList *alarm_list,
259                      GtkTreeIter *iter,
260                      const ECalComponentAlarm *alarm)
261 {
262 	g_return_if_fail (alarm != NULL);
263 
264 	alarm_list->list = g_list_append (alarm_list->list, copy_alarm (alarm));
265 	row_added (alarm_list, g_list_length (alarm_list->list) - 1);
266 
267 	if (iter) {
268 		iter->user_data = g_list_last (alarm_list->list);
269 		iter->stamp     = alarm_list->stamp;
270 	}
271 }
272 
273 void
274 e_alarm_list_remove (EAlarmList *alarm_list,
275                      GtkTreeIter *iter)
276 {
277 	gint n;
278 
279 	g_return_if_fail (IS_VALID_ITER (alarm_list, iter));
280 
281 	n = g_list_position (alarm_list->list, G_LIST (iter->user_data));
282 	free_alarm ((ECalComponentAlarm *) G_LIST (iter->user_data)->data);
283 	alarm_list->list = g_list_delete_link (alarm_list->list, G_LIST (iter->user_data));
284 	row_deleted (alarm_list, n);
285 }
286 
287 void
288 e_alarm_list_clear (EAlarmList *alarm_list)
289 {
290 	GList *l;
291 
292 	all_rows_deleted (alarm_list);
293 
294 	for (l = alarm_list->list; l; l = g_list_next (l)) {
295 		free_alarm ((ECalComponentAlarm *) l->data);
296 	}
297 
298 	g_list_free (alarm_list->list);
299 	alarm_list->list = NULL;
300 }
301 
302 static gboolean
303 e_alarm_list_get_iter (GtkTreeModel *tree_model,
304                        GtkTreeIter *iter,
305                        GtkTreePath *path)
306 {
307 	EAlarmList *alarm_list = (EAlarmList *) tree_model;
308 	GList      *l;
309 	gint        i;
310 
311 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), FALSE);
312 	g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
313 
314 	if (!alarm_list->list)
315 		return FALSE;
316 
317 	alarm_list->columns_dirty = TRUE;
318 
319 	i = gtk_tree_path_get_indices (path)[0];
320 	l = g_list_nth (alarm_list->list, i);
321 	if (!l)
322 		return FALSE;
323 
324 	iter->user_data = l;
325 	iter->stamp     = alarm_list->stamp;
326 	return TRUE;
327 }
328 
329 static GtkTreePath *
330 e_alarm_list_get_path (GtkTreeModel *tree_model,
331                        GtkTreeIter *iter)
332 {
333 	EAlarmList  *alarm_list = (EAlarmList *) tree_model;
334 	GtkTreePath *retval;
335 	GList       *l;
336 
337 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), NULL);
338 	g_return_val_if_fail (iter->stamp == E_ALARM_LIST (tree_model)->stamp, NULL);
339 
340 	l = iter->user_data;
341 	retval = gtk_tree_path_new ();
342 	gtk_tree_path_append_index (retval, g_list_position (alarm_list->list, l));
343 	return retval;
344 }
345 
346 /* Builds a string for the duration of the alarm.  If the duration is zero, returns NULL. */
347 static gchar *
348 get_alarm_duration_string (struct icaldurationtype *duration)
349 {
350 	GString *string = g_string_new (NULL);
351 	gchar *ret;
352 	gboolean have_something;
353 
354 	have_something = FALSE;
355 
356 	if (duration->days >= 1) {
357 		/* Translator: Entire string is like "Pop up an alert %d days before start of appointment" */
358 		g_string_printf (string, ngettext ("%d day", "%d days", duration->days), duration->days);
359 		have_something = TRUE;
360 	}
361 
362 	if (duration->weeks >= 1) {
363 		/* Translator: Entire string is like "Pop up an alert %d weeks before start of appointment" */
364 		g_string_printf (string, ngettext ("%d week","%d weeks", duration->weeks), duration->weeks);
365 		have_something = TRUE;
366 	}
367 
368 	if (duration->hours >= 1) {
369 		/* Translator: Entire string is like "Pop up an alert %d hours before start of appointment" */
370 		g_string_printf (string, ngettext ("%d hour", "%d hours", duration->hours), duration->hours);
371 		have_something = TRUE;
372 	}
373 
374 	if (duration->minutes >= 1) {
375 		/* Translator: Entire string is like "Pop up an alert %d minutes before start of appointment" */
376 		g_string_printf (string, ngettext ("%d minute", "%d minutes", duration->minutes), duration->minutes);
377 		have_something = TRUE;
378 	}
379 
380 	if (duration->seconds >= 1) {
381 		/* Translator: Entire string is like "Pop up an alert %d seconds before start of appointment" */
382 		g_string_printf (string, ngettext ("%d second", "%d seconds", duration->seconds), duration->seconds);
383 		have_something = TRUE;
384 	}
385 
386 	if (have_something) {
387 		ret = string->str;
388 		g_string_free (string, FALSE);
389 		return ret;
390 	} else {
391 		g_string_free (string, TRUE);
392 		return NULL;
393 	}
394 }
395 
396 static gchar *
397 get_alarm_string (ECalComponentAlarm *alarm)
398 {
399 	ECalComponentAlarmAction action;
400 	ECalComponentAlarmTrigger trigger;
401 	gchar *base, *str = NULL, *dur;
402 
403 	e_cal_component_alarm_get_action (alarm, &action);
404 	e_cal_component_alarm_get_trigger (alarm, &trigger);
405 
406 	switch (action) {
407 	case E_CAL_COMPONENT_ALARM_AUDIO:
408 		base = _("Play a sound");
409 		break;
410 
411 	case E_CAL_COMPONENT_ALARM_DISPLAY:
412 		base = _("Pop up an alert");
413 		break;
414 
415 	case E_CAL_COMPONENT_ALARM_EMAIL:
416 		base = _("Send an email");
417 		break;
418 
419 	case E_CAL_COMPONENT_ALARM_PROCEDURE:
420 		base = _("Run a program");
421 		break;
422 
423 	case E_CAL_COMPONENT_ALARM_NONE:
424 	case E_CAL_COMPONENT_ALARM_UNKNOWN:
425 	default:
426 		base = _("Unknown action to be performed");
427 		break;
428 	}
429 
430 	/* FIXME: This does not look like it will localize correctly. */
431 
432 	switch (trigger.type) {
433 	case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START:
434 		dur = get_alarm_duration_string (&trigger.u.rel_duration);
435 
436 		if (dur) {
437 			if (trigger.u.rel_duration.is_neg)
438 				/*Translator: The first %s refers to the base, which would be actions like
439 				 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
440 				str = g_strdup_printf (
441 					_("%s %s before the start of the appointment"),
442 					base, dur);
443 			else
444 				/*Translator: The first %s refers to the base, which would be actions like
445 				 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes"*/
446 				str = g_strdup_printf (
447 					_("%s %s after the start of the appointment"),
448 					base, dur);
449 
450 			g_free (dur);
451 		} else
452 			/*Translator: The %s refers to the base, which would be actions like
453 			 * "Play a sound" */
454 			str = g_strdup_printf (_("%s at the start of the appointment"), base);
455 
456 		break;
457 
458 	case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_END:
459 		dur = get_alarm_duration_string (&trigger.u.rel_duration);
460 
461 		if (dur) {
462 			if (trigger.u.rel_duration.is_neg)
463 				/* Translator: The first %s refers to the base, which would be actions like
464 				 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
465 				str = g_strdup_printf (
466 					_("%s %s before the end of the appointment"),
467 					base, dur);
468 			else
469 				/* Translator: The first %s refers to the base, which would be actions like
470 				 * "Play a Sound". Second %s refers to the duration string e.g:"15 minutes" */
471 				str = g_strdup_printf (
472 					_("%s %s after the end of the appointment"),
473 					base, dur);
474 
475 			g_free (dur);
476 		} else
477 			/* Translator: The %s refers to the base, which would be actions like
478 			 * "Play a sound" */
479 			str = g_strdup_printf (_("%s at the end of the appointment"), base);
480 
481 		break;
482 
483 	case E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE: {
484 		struct icaltimetype itt;
485 		icaltimezone *utc_zone, *current_zone;
486 		struct tm tm;
487 		gchar buf[256];
488 
489 		/* Absolute triggers come in UTC, so convert them to the local timezone */
490 
491 		itt = trigger.u.abs_time;
492 
493 		utc_zone = icaltimezone_get_utc_timezone ();
494 		current_zone = calendar_config_get_icaltimezone ();
495 
496 		tm = icaltimetype_to_tm_with_zone (&itt, utc_zone, current_zone);
497 
498 		e_time_format_date_and_time (&tm, calendar_config_get_24_hour_format (),
499 					     FALSE, FALSE, buf, sizeof (buf));
500 
501 		/* Translator: The first %s refers to the base, which would be actions like
502 		 * "Play a Sound". Second %s is an absolute time, e.g. "10:00AM" */
503 		str = g_strdup_printf (_("%s at %s"), base, buf);
504 
505 		break; }
506 
507 	case E_CAL_COMPONENT_ALARM_TRIGGER_NONE:
508 	default:
509 		/* Translator: The %s refers to the base, which would be actions like
510 		 * "Play a sound". "Trigger types" are absolute or relative dates */
511 		str = g_strdup_printf (_("%s for an unknown trigger type"), base);
512 		break;
513 	}
514 
515 	return str;
516 }
517 
518 static void
519 e_alarm_list_get_value (GtkTreeModel *tree_model,
520                         GtkTreeIter *iter,
521                         gint column,
522                         GValue *value)
523 {
524 	EAlarmList        *alarm_list = E_ALARM_LIST (tree_model);
525 	ECalComponentAlarm *alarm;
526 	GList             *l;
527 	gchar	  *str;
528 
529 	g_return_if_fail (E_IS_ALARM_LIST (tree_model));
530 	g_return_if_fail (column < E_ALARM_LIST_NUM_COLUMNS);
531 	g_return_if_fail (E_ALARM_LIST (tree_model)->stamp == iter->stamp);
532 	g_return_if_fail (IS_VALID_ITER (alarm_list, iter));
533 
534 	g_value_init (value, column_types[column]);
535 
536 	if (!alarm_list->list)
537 		return;
538 
539 	l = iter->user_data;
540 	alarm = l->data;
541 
542 	if (!alarm)
543 		return;
544 
545 	switch (column) {
546 		case E_ALARM_LIST_COLUMN_DESCRIPTION:
547 			str = get_alarm_string (alarm);
548 			g_value_set_string (value, str);
549 			g_free (str);
550 			break;
551 	}
552 }
553 
554 static gboolean
555 e_alarm_list_iter_next (GtkTreeModel *tree_model,
556                         GtkTreeIter *iter)
557 {
558 	GList *l;
559 
560 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), FALSE);
561 	g_return_val_if_fail (IS_VALID_ITER (E_ALARM_LIST (tree_model), iter), FALSE);
562 
563 	if (!E_ALARM_LIST (tree_model)->list)
564 		return FALSE;
565 
566 	l = iter->user_data;
567 	l = g_list_next (l);
568 	if (l) {
569 		iter->user_data = l;
570 		return TRUE;
571 	}
572 
573 	return FALSE;
574 }
575 
576 static gboolean
577 e_alarm_list_iter_children (GtkTreeModel *tree_model,
578                             GtkTreeIter *iter,
579                             GtkTreeIter *parent)
580 {
581 	EAlarmList *alarm_list = E_ALARM_LIST (tree_model);
582 
583 	/* this is a list, nodes have no children */
584 	if (parent)
585 		return FALSE;
586 
587 	/* but if parent == NULL we return the list itself as children of the
588 	 * "root" */
589 
590 	if (!alarm_list->list)
591 		return FALSE;
592 
593 	iter->stamp     = E_ALARM_LIST (tree_model)->stamp;
594 	iter->user_data = alarm_list->list;
595 	return TRUE;
596 }
597 
598 static gboolean
599 e_alarm_list_iter_has_child (GtkTreeModel *tree_model,
600                              GtkTreeIter *iter)
601 {
602 	g_return_val_if_fail (IS_VALID_ITER (E_ALARM_LIST (tree_model), iter), FALSE);
603 	return FALSE;
604 }
605 
606 static gint
607 e_alarm_list_iter_n_children (GtkTreeModel *tree_model,
608                               GtkTreeIter *iter)
609 {
610 	EAlarmList *alarm_list = E_ALARM_LIST (tree_model);
611 
612 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), -1);
613 
614 	if (iter == NULL)
615 		return g_list_length (alarm_list->list);
616 
617 	g_return_val_if_fail (E_ALARM_LIST (tree_model)->stamp == iter->stamp, -1);
618 	return 0;
619 }
620 
621 static gboolean
622 e_alarm_list_iter_nth_child (GtkTreeModel *tree_model,
623                              GtkTreeIter *iter,
624                              GtkTreeIter *parent,
625                              gint n)
626 {
627 	EAlarmList *alarm_list = E_ALARM_LIST (tree_model);
628 
629 	g_return_val_if_fail (E_IS_ALARM_LIST (tree_model), FALSE);
630 
631 	if (parent)
632 		return FALSE;
633 
634 	if (alarm_list->list) {
635 		GList *l;
636 
637 		l = g_list_nth (alarm_list->list, n);
638 		if (!l)
639 			return FALSE;
640 
641 		iter->stamp     = alarm_list->stamp;
642 		iter->user_data = l;
643 		return TRUE;
644 	}
645 
646 	return FALSE;
647 }
648 
649 static gboolean
650 e_alarm_list_iter_parent (GtkTreeModel *tree_model,
651                           GtkTreeIter *iter,
652                           GtkTreeIter *child)
653 {
654 	return FALSE;
655 }