1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
6 * subtitlehandler.c is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
17 #include "hb-backend.h"
19 #include "callbacks.h"
22 #include "audiohandler.h"
23 #include "subtitlehandler.h"
25 static void add_to_subtitle_list(signal_user_data_t *ud, GValue *settings);
28 free_subtitle_index_list(gpointer data)
34 free_subtitle_key(gpointer data)
41 mustBurn(signal_user_data_t *ud, gint track)
45 mux = ghb_settings_combo_int(ud->settings, "FileFormat");
46 if (mux == HB_MUX_MP4)
50 // MP4 can only handle burned vobsubs. make sure there isn't
51 // already something burned in the list
52 source = ghb_subtitle_track_source(ud, track);
62 ghb_subtitle_exclusive_burn(signal_user_data_t *ud, gint index)
64 GValue *subtitle_list;
72 g_debug("ghb_subtitle_exclusive_burn");
73 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
74 count = ghb_array_len(subtitle_list);
75 for (ii = 0; ii < count; ii++)
77 settings = ghb_array_get_nth(subtitle_list, ii);
78 tt = ghb_settings_combo_int(settings, "SubtitleTrack");
79 burned = ghb_settings_get_boolean(settings, "SubtitleBurned");
81 tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
82 g_return_if_fail(tv != NULL);
83 tm = gtk_tree_view_get_model(tv);
84 gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii);
85 if (burned && ii != index && !mustBurn(ud, tt))
87 ghb_settings_set_boolean(settings, "SubtitleBurned", FALSE);
88 gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 2, FALSE, -1);
94 ghb_subtitle_exclusive_default(signal_user_data_t *ud, gint index)
96 GValue *subtitle_list;
104 g_debug("ghb_subtitle_exclusive_default");
105 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
106 count = ghb_array_len(subtitle_list);
107 for (ii = 0; ii < count; ii++)
109 settings = ghb_array_get_nth(subtitle_list, ii);
110 def = ghb_settings_get_boolean(settings, "SubtitleDefaultTrack");
112 tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
113 g_return_if_fail(tv != NULL);
114 tm = gtk_tree_view_get_model(tv);
115 gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii);
116 if (def && ii != index)
119 ghb_settings_set_boolean(settings, "SubtitleDefaultTrack", FALSE);
120 gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 3, FALSE, -1);
126 ghb_add_subtitle(signal_user_data_t *ud, GValue *settings)
128 // Add the current subtitle settings to the list.
129 GValue *subtitle_list;
136 g_debug("ghb_add_subtitle ()");
138 // Add the long track description so the queue can access it
139 // when a different title is selected.
140 track = ghb_settings_combo_option(settings, "SubtitleTrack");
141 ghb_settings_set_string(settings, "SubtitleTrackDescription", track);
143 lang = ghb_settings_combo_string(settings, "SubtitleTrack");
144 ghb_settings_set_string(settings, "SubtitleLanguage", lang);
146 tt = ghb_settings_get_int(settings, "SubtitleTrack");
147 source = ghb_subtitle_track_source(ud, tt);
148 ghb_settings_set_int(settings, "SubtitleSource", source);
150 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
151 if (subtitle_list == NULL)
153 subtitle_list = ghb_array_value_new(8);
154 ghb_settings_set_value(ud->settings, "subtitle_list", subtitle_list);
156 count = ghb_array_len(subtitle_list);
158 // Don't allow more than 99
159 // This is a had limit imposed by libhb/sync.c:GetFifoForId()
162 ghb_value_free(settings);
166 ghb_array_append(subtitle_list, settings);
167 add_to_subtitle_list(ud, settings);
169 burned = ghb_settings_get_boolean(settings, "SubtitleBurned");
171 ghb_subtitle_exclusive_burn(ud, count);
175 widget = GHB_WIDGET (ud->builder, "subtitle_add");
176 gtk_widget_set_sensitive(widget, FALSE);
182 add_all_pref_subtitles(signal_user_data_t *ud)
184 const GValue *pref_subtitle;
186 gint count, ii, track;
189 pref_subtitle = ghb_settings_get_value(ud->settings, "SubtitleList");
190 count = ghb_array_len(pref_subtitle);
191 for (ii = 0; ii < count; ii++)
193 subtitle = ghb_value_dup(ghb_array_get_nth(pref_subtitle, ii));
194 lang = ghb_settings_get_string(subtitle, "SubtitleLanguage");
195 // If there are multiple subtitles using the same language, then
196 // select sequential tracks for each. The hash keeps track
197 // of the tracks used for each language.
198 track = ghb_find_pref_subtitle_track(lang);
202 // Add to subtitle list
203 ghb_settings_set_int(subtitle, "SubtitleTrack", track);
204 ghb_add_subtitle(ud, subtitle);
210 ghb_set_pref_subtitle(gint titleindex, signal_user_data_t *ud)
213 GHashTable *track_indices;
214 gchar *lang, *pref_lang = NULL;
216 gint foreign_lang_index = -1;
218 const GValue *pref_subtitle;
222 g_debug("ghb_set_pref_subtitle %d", titleindex);
224 // Check to see if we need to add a subtitle track for foreign audio
225 // language films. A subtitle track will be added if:
227 // The first (default) audio track language does NOT match the users
228 // chosen Preferred Language AND the Preferred Language is NOT Any (und).
230 audio_lang = ghb_get_user_audio_lang(ud, titleindex, 0);
231 pref_lang = ghb_settings_get_string(ud->settings, "PreferredLanguage");
233 if (audio_lang != NULL && pref_lang != NULL &&
234 (strcmp(audio_lang, pref_lang) == 0 || strcmp("und", pref_lang) == 0))
240 track_indices = g_hash_table_new_full(g_str_hash, g_str_equal,
241 free_subtitle_key, free_subtitle_index_list);
243 ghb_ui_update(ud, "SubtitleTrack", ghb_int_value(0));
245 // Clear the subtitle list
246 ghb_clear_subtitle_list(ud);
249 add_all_pref_subtitles(ud);
253 // Find "best" subtitle based on subtitle preferences
254 pref_subtitle = ghb_settings_get_value(ud->settings, "SubtitleList");
256 count = ghb_array_len(pref_subtitle);
258 for (ii = 0; ii < count; ii++)
260 subtitle = ghb_array_get_nth(pref_subtitle, ii);
261 lang = ghb_settings_get_string(subtitle, "SubtitleLanguage");
262 // If there are multiple subtitles using the same language, then
263 // select sequential tracks for each. The hash keeps track
264 // of the tracks used for each language.
265 track = ghb_find_subtitle_track(titleindex, lang, track_indices);
268 GValue *dup = ghb_value_dup(subtitle);
269 ghb_settings_set_int(dup, "SubtitleTrack", track);
270 if (foreign_lang_index < 0 && pref_lang != NULL &&
271 strcmp(lang, pref_lang) == 0)
273 foreign_lang_index = jj;
274 ghb_settings_take_value(dup, "SubtitleForced",
275 ghb_boolean_value_new(FALSE));
276 ghb_settings_take_value(dup, "SubtitleDefaultTrack",
277 ghb_boolean_value_new(TRUE));
279 ghb_add_subtitle(ud, dup);
284 if (foreign_lang_index < 0 && pref_lang != NULL)
289 track = ghb_find_subtitle_track(titleindex, pref_lang, track_indices);
292 burn = mustBurn(ud, track);
293 settings = ghb_dict_value_new();
294 ghb_settings_set_int(settings, "SubtitleTrack", track);
295 ghb_settings_take_value(settings, "SubtitleForced",
296 ghb_boolean_value_new(FALSE));
297 ghb_settings_take_value(settings, "SubtitleBurned",
298 ghb_boolean_value_new(burn));
299 ghb_settings_take_value(settings, "SubtitleDefaultTrack",
300 ghb_boolean_value_new(TRUE));
302 ghb_add_subtitle(ud, settings);
303 foreign_lang_index = jj;
306 if (foreign_lang_index >= 0)
308 GValue *subtitle_list;
311 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
312 subtitle = ghb_array_get_nth(subtitle_list, foreign_lang_index);
314 burn = ghb_settings_get_boolean(subtitle, "SubtitleBurned");
315 def = ghb_settings_get_boolean(subtitle, "SubtitleDefaultTrack");
317 ghb_subtitle_exclusive_burn(ud, foreign_lang_index);
319 ghb_subtitle_exclusive_default(ud, foreign_lang_index);
320 ghb_log("adding subtitle for foreign language audio: %s", audio_lang);
322 if (pref_lang != NULL)
324 if (audio_lang != NULL)
326 g_hash_table_destroy(track_indices);
330 ghb_selected_subtitle_row(signal_user_data_t *ud)
332 GtkTreeView *treeview;
333 GtkTreePath *treepath;
334 GtkTreeSelection *selection;
340 g_debug("ghb_selected_subtitle_row ()");
341 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
342 selection = gtk_tree_view_get_selection (treeview);
343 if (gtk_tree_selection_get_selected(selection, &store, &iter))
345 // Get the row number
346 treepath = gtk_tree_model_get_path (store, &iter);
347 indices = gtk_tree_path_get_indices (treepath);
349 gtk_tree_path_free(treepath);
355 ghb_selected_subtitle_settings(signal_user_data_t *ud)
357 GtkTreeView *treeview;
358 GtkTreePath *treepath;
359 GtkTreeSelection *selection;
364 GValue *settings = NULL;
365 const GValue *subtitle_list;
367 g_debug("get_selected_settings ()");
368 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
369 selection = gtk_tree_view_get_selection (treeview);
370 if (gtk_tree_selection_get_selected(selection, &store, &iter))
372 // Get the row number
373 treepath = gtk_tree_model_get_path (store, &iter);
374 indices = gtk_tree_path_get_indices (treepath);
376 gtk_tree_path_free(treepath);
377 // find subtitle settings
378 if (row < 0) return NULL;
379 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
380 if (row >= ghb_array_len(subtitle_list))
382 settings = ghb_array_get_nth(subtitle_list, row);
388 subtitle_forced_toggled_cb(
389 GtkCellRendererToggle *cell,
391 signal_user_data_t *ud)
400 GValue *subtitle_list, *settings;
403 g_debug("forced toggled");
404 tp = gtk_tree_path_new_from_string (path);
405 tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
406 g_return_if_fail(tv != NULL);
407 tm = gtk_tree_view_get_model(tv);
408 g_return_if_fail(tm != NULL);
409 gtk_tree_model_get_iter(tm, &ti, tp);
410 gtk_tree_model_get(tm, &ti, 1, &active, -1);
413 // Get the row number
414 indices = gtk_tree_path_get_indices (tp);
416 gtk_tree_path_free(tp);
418 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
420 if (row < 0 || row >= ghb_array_len(subtitle_list))
423 settings = ghb_array_get_nth(subtitle_list, row);
424 track = ghb_settings_combo_int(settings, "SubtitleTrack");
426 source = ghb_subtitle_track_source(ud, track);
427 if (source != VOBSUB)
430 ghb_settings_set_boolean(settings, "SubtitleForced", active);
431 gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 1, active, -1);
436 subtitle_burned_toggled_cb(
437 GtkCellRendererToggle *cell,
439 signal_user_data_t *ud)
448 GValue *subtitle_list;
449 gint count, track, source;
452 g_debug("burned toggled");
453 tp = gtk_tree_path_new_from_string (path);
454 tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
455 g_return_if_fail(tv != NULL);
456 tm = gtk_tree_view_get_model(tv);
457 g_return_if_fail(tm != NULL);
458 gtk_tree_model_get_iter(tm, &ti, tp);
459 gtk_tree_model_get(tm, &ti, 2, &active, -1);
462 // Get the row number
463 indices = gtk_tree_path_get_indices (tp);
465 gtk_tree_path_free(tp);
467 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
468 count = ghb_array_len(subtitle_list);
469 if (row < 0 || row >= count)
472 settings = ghb_array_get_nth(subtitle_list, row);
473 track = ghb_settings_combo_int(settings, "SubtitleTrack");
475 source = ghb_subtitle_track_source(ud, track);
476 if (source != VOBSUB)
479 if (!active && mustBurn(ud, track))
482 ghb_settings_set_boolean(settings, "SubtitleBurned", active);
484 gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 2, active, -1);
487 ghb_subtitle_exclusive_burn(ud, row);
492 subtitle_default_toggled_cb(
493 GtkCellRendererToggle *cell,
495 signal_user_data_t *ud)
504 GValue *subtitle_list;
508 g_debug("default toggled");
509 tp = gtk_tree_path_new_from_string (path);
510 tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
511 g_return_if_fail(tv != NULL);
512 tm = gtk_tree_view_get_model(tv);
513 g_return_if_fail(tm != NULL);
514 gtk_tree_model_get_iter(tm, &ti, tp);
515 gtk_tree_model_get(tm, &ti, 3, &active, -1);
518 // Get the row number
519 indices = gtk_tree_path_get_indices (tp);
521 gtk_tree_path_free(tp);
523 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
524 count = ghb_array_len(subtitle_list);
525 if (row < 0 || row >= count)
528 settings = ghb_array_get_nth(subtitle_list, row);
529 track = ghb_settings_combo_int(settings, "SubtitleTrack");
531 ghb_settings_set_boolean(settings, "SubtitleDefaultTrack", active);
533 gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 3, active, -1);
534 // allow only one default
535 ghb_subtitle_exclusive_default(ud, row);
540 subtitle_list_refresh_selected(signal_user_data_t *ud)
542 GtkTreeView *treeview;
543 GtkTreePath *treepath;
544 GtkTreeSelection *selection;
549 GValue *settings = NULL;
550 const GValue *subtitle_list;
552 g_debug("subtitle_list_refresh_selected ()");
553 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
554 selection = gtk_tree_view_get_selection (treeview);
555 if (gtk_tree_selection_get_selected(selection, &store, &iter))
557 const gchar *track, *source;
558 gboolean forced, burned, def;
562 // Get the row number
563 treepath = gtk_tree_model_get_path (store, &iter);
564 indices = gtk_tree_path_get_indices (treepath);
566 gtk_tree_path_free(treepath);
567 // find audio settings
569 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
570 if (row >= ghb_array_len(subtitle_list))
572 settings = ghb_array_get_nth(subtitle_list, row);
574 track = ghb_settings_combo_option(settings, "SubtitleTrack");
575 forced = ghb_settings_get_boolean(settings, "SubtitleForced");
576 burned = ghb_settings_get_boolean(settings, "SubtitleBurned");
577 def = ghb_settings_get_boolean(settings, "SubtitleDefaultTrack");
579 s_track = ghb_settings_get_string(settings, "SubtitleTrack");
580 i_track = ghb_settings_get_int(settings, "SubtitleTrack");
581 source = ghb_subtitle_track_source_name(ud, i_track);
584 i_source = ghb_subtitle_track_source(ud, i_track);
585 if (i_source != VOBSUB)
587 // Force and burn only apply to VOBSUBS
590 ghb_settings_set_boolean(settings, "SubtitleForced", forced);
591 ghb_settings_set_boolean(settings, "SubtitleBurned", burned);
594 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
595 // These are displayed in list
601 // These are used to set combo box values when a list item is selected
606 ghb_subtitle_exclusive_burn(ud, row);
611 subtitle_track_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
615 g_debug("subtitle_track_changed_cb ()");
616 ghb_check_dependency(ud, widget);
617 ghb_widget_to_setting(ud->settings, widget);
618 settings = ghb_selected_subtitle_settings(ud);
619 if (settings != NULL)
624 ghb_widget_to_setting(settings, widget);
625 subtitle_list_refresh_selected(ud);
626 track = ghb_settings_combo_option(settings, "SubtitleTrack");
627 ghb_settings_set_string(settings, "SubtitleTrackDescription", track);
628 tt = ghb_settings_get_int(settings, "SubtitleTrack");
629 source = ghb_subtitle_track_source(ud, tt);
630 ghb_settings_set_int(settings, "SubtitleSource", source);
637 ghb_clear_subtitle_list(signal_user_data_t *ud)
639 GtkTreeView *treeview;
641 GValue *subtitle_list;
643 g_debug("clear_subtitle_list ()");
644 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
645 if (subtitle_list == NULL)
647 subtitle_list = ghb_array_value_new(8);
648 ghb_settings_set_value(ud->settings, "subtitle_list", subtitle_list);
651 ghb_array_value_reset(subtitle_list, 8);
652 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
653 store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
654 gtk_list_store_clear (store);
658 add_to_subtitle_list(
659 signal_user_data_t *ud,
662 GtkTreeView *treeview;
665 GtkTreeSelection *selection;
666 const gchar *track, *source;
667 gboolean forced, burned, def;
671 g_debug("add_to_subtitle_list ()");
672 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
673 selection = gtk_tree_view_get_selection (treeview);
674 store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
676 track = ghb_settings_combo_option(settings, "SubtitleTrack");
677 forced = ghb_settings_get_boolean(settings, "SubtitleForced");
678 burned = ghb_settings_get_boolean(settings, "SubtitleBurned");
679 def = ghb_settings_get_boolean(settings, "SubtitleDefaultTrack");
681 s_track = ghb_settings_get_string(settings, "SubtitleTrack");
682 i_track = ghb_settings_get_int(settings, "SubtitleTrack");
683 source = ghb_subtitle_track_source_name(ud, i_track);
685 gtk_list_store_append(store, &iter);
686 gtk_list_store_set(store, &iter,
687 // These are displayed in list
693 // These are used to set combo box values when a list item is selected
696 gtk_tree_selection_select_iter(selection, &iter);
701 subtitle_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
707 g_debug("subtitle_list_selection_changed_cb ()");
708 if (gtk_tree_selection_get_selected(selection, &store, &iter))
712 gtk_tree_model_get(store, &iter, 5, &track, -1);
713 ghb_ui_update(ud, "SubtitleTrack", ghb_string_value(track));
715 widget = GHB_WIDGET (ud->builder, "subtitle_remove");
716 gtk_widget_set_sensitive(widget, TRUE);
721 subtitle_add_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
723 // Add the current subtitle settings to the list.
725 gboolean burned = FALSE;
728 g_debug("subtitle_add_clicked_cb ()");
730 track = ghb_settings_get_int(ud->settings, "SubtitleTrack");
731 if (mustBurn(ud, track))
735 settings = ghb_dict_value_new();
736 ghb_settings_set_int(settings, "SubtitleTrack", track);
737 ghb_settings_take_value(settings, "SubtitleForced",
738 ghb_boolean_value_new(FALSE));
739 ghb_settings_take_value(settings, "SubtitleBurned",
740 ghb_boolean_value_new(burned));
741 ghb_settings_take_value(settings, "SubtitleDefaultTrack",
742 ghb_boolean_value_new(FALSE));
744 ghb_add_subtitle(ud, settings);
748 subtitle_remove_clicked_cb(GtkWidget *widget, signal_user_data_t *ud)
750 GtkTreeView *treeview;
751 GtkTreePath *treepath;
752 GtkTreeSelection *selection;
754 GtkTreeIter iter, nextIter;
757 GValue *subtitle_list;
759 g_debug("subtitle_remove_clicked_cb ()");
760 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
761 selection = gtk_tree_view_get_selection (treeview);
762 if (gtk_tree_selection_get_selected(selection, &store, &iter))
765 if (!gtk_tree_model_iter_next(store, &nextIter))
768 if (gtk_tree_model_get_iter_first(store, &nextIter))
770 gtk_tree_selection_select_iter (selection, &nextIter);
775 gtk_tree_selection_select_iter (selection, &nextIter);
777 // Get the row number
778 treepath = gtk_tree_model_get_path (store, &iter);
779 indices = gtk_tree_path_get_indices (treepath);
781 gtk_tree_path_free(treepath);
782 // Remove the selected item
783 gtk_list_store_remove (GTK_LIST_STORE(store), &iter);
784 // remove from subtitle settings list
786 widget = GHB_WIDGET (ud->builder, "subtitle_add");
787 gtk_widget_set_sensitive(widget, TRUE);
788 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
789 if (row >= ghb_array_len(subtitle_list))
791 GValue *old = ghb_array_get_nth(subtitle_list, row);
793 ghb_array_remove(subtitle_list, row);
799 ghb_subtitle_prune(signal_user_data_t *ud)
804 GValue *subtitle_list, *settings;
805 gint count, ii, track;
807 gint first_track = 0, one_burned = 0;
809 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
810 if (subtitle_list == NULL)
812 count = ghb_array_len(subtitle_list);
814 tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list"));
815 g_return_if_fail(tv != NULL);
816 tm = gtk_tree_view_get_model(tv);
817 for (ii = count-1; ii >= 0; ii--)
819 settings = ghb_array_get_nth(subtitle_list, ii);
820 burned = ghb_settings_get_boolean(settings, "SubtitleBurned");
821 track = ghb_settings_combo_int(settings, "SubtitleTrack");
822 if (!burned && mustBurn(ud, track))
824 gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii);
825 gtk_list_store_remove (GTK_LIST_STORE(tm), &ti);
826 ghb_array_remove(subtitle_list, ii);
836 ghb_subtitle_exclusive_burn(ud, first_track);
841 ghb_reset_subtitles(signal_user_data_t *ud, GValue *settings)
848 g_debug("ghb_reset_subtitles");
849 ghb_clear_subtitle_list(ud);
850 titleindex = ghb_settings_combo_int(ud->settings, "title");
854 slist = ghb_settings_get_value(settings, "subtitle_list");
855 count = ghb_array_len(slist);
856 for (ii = 0; ii < count; ii++)
858 subtitle = ghb_value_dup(ghb_array_get_nth(slist, ii));
859 ghb_add_subtitle(ud, subtitle);