diff options
-rw-r--r-- | gtk/src/Makefile.am | 18 | ||||
-rw-r--r-- | gtk/src/callbacks.c | 421 | ||||
-rw-r--r-- | gtk/src/callbacks.h | 1 | ||||
-rw-r--r-- | gtk/src/ghb.ui | 7 | ||||
-rw-r--r-- | gtk/src/ghbcellrenderertext.c | 1972 | ||||
-rw-r--r-- | gtk/src/ghbcellrenderertext.h | 105 | ||||
-rw-r--r-- | gtk/src/hb-backend.c | 387 | ||||
-rw-r--r-- | gtk/src/hb-backend.h | 5 | ||||
-rw-r--r-- | gtk/src/main.c | 13 | ||||
-rw-r--r-- | gtk/src/marshalers.c | 125 | ||||
-rw-r--r-- | gtk/src/marshalers.h | 28 | ||||
-rw-r--r-- | gtk/src/plist.c | 8 | ||||
-rw-r--r-- | gtk/src/plist.h | 2 | ||||
-rw-r--r-- | gtk/src/presets.c | 90 | ||||
-rw-r--r-- | gtk/src/presets.h | 3 | ||||
-rw-r--r-- | gtk/src/settings.c | 13 | ||||
-rw-r--r-- | gtk/src/settings.h | 3 |
17 files changed, 2946 insertions, 255 deletions
diff --git a/gtk/src/Makefile.am b/gtk/src/Makefile.am index 02eca572d..2de9f748d 100644 --- a/gtk/src/Makefile.am +++ b/gtk/src/Makefile.am @@ -98,8 +98,12 @@ ghb_SOURCES = \ hb-backend.h \ renderer_button.h \ renderer_button.c \ + ghbcellrenderertext.c \ + ghbcellrenderertext.h \ ghb-dvd.c \ - ghb-dvd.h + ghb-dvd.h \ + marshalers.c \ + marshalers.h ghb_LDFLAGS = \ -Wl,--export-dynamic @@ -119,6 +123,8 @@ makewidgetdeps_LDADD = $(GHBTOOLS_LIBS) quotestring_SOURCES = preset_to_string.c +callbacks.c: widget_deps.h widget_reverse_deps.h + widget_deps.h: makewidgetdeps quotestring ./makewidgetdeps ./quotestring widget_deps widget_deps.h @@ -127,12 +133,22 @@ widget_reverse_deps.h: makewidgetdeps quotestring ./makewidgetdeps ./quotestring widget_reverse_deps widget_reverse_deps.h +presets.c: internal_defaults.h standard_presets.h + internal_defaults.h: quotestring internal_defaults.xml ./quotestring internal_defaults.xml internal_defaults.h standard_presets.h: quotestring standard_presets.xml ./quotestring standard_presets.xml standard_presets.h +ghbcellrenderertext.c: marshalers.h + +marshalers.h: marshalers.list + glib-genmarshal --prefix=ghb_marshal marshalers.list --header > marshalers.h + +marshalers.c: marshalers.list + glib-genmarshal --prefix=ghb_marshal marshalers.list --body > marshalers.c + EXTRA_DIST = $(builder_DATA) $(icons) HandBrakeCLI uninstall-local: diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index a0517616b..bd07e34b2 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -21,6 +21,7 @@ #include <sys/stat.h> #include <libhal-storage.h> #include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> #include <glib/gstdio.h> #include <gio/gio.h> @@ -31,6 +32,7 @@ #include "plist.h" #include "hb-backend.h" #include "ghb-dvd.h" +#include "ghbcellrenderertext.h" static void update_chapter_list(signal_user_data_t *ud); static void clear_audio_list(signal_user_data_t *ud); @@ -261,7 +263,7 @@ expand_tilde(const gchar *path) void on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud) { - gint state = ghb_get_state(); + gint state = ghb_get_queue_state(); g_debug("on_quit1_activate ()"); if (state & GHB_STATE_WORKING) { @@ -685,6 +687,43 @@ update_destination_extension(signal_user_data_t *ud) g_free(filename); } +static void +destination_select_title(GtkEntry *entry) +{ + const gchar *dest; + gint start, end; + + dest = gtk_entry_get_text(entry); + for (end = strlen(dest)-1; end > 0; end--) + { + if (dest[end] == '.') + { + break; + } + } + for (start = end; start >= 0; start--) + { + if (dest[start] == '/') + { + start++; + break; + } + } + if (start < end) + { + gtk_editable_select_region(GTK_EDITABLE(entry), start, end); + } +} + +gboolean +destination_grab_cb( + GtkEntry *entry, + signal_user_data_t *ud) +{ + destination_select_title(entry); + return FALSE; +} + static gboolean update_default_destination = FALSE; void @@ -757,7 +796,7 @@ window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t * gboolean window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) { - gint state = ghb_get_state(); + gint state = ghb_get_queue_state(); g_debug("window_delete_event_cb ()"); if (state & GHB_STATE_WORKING) { @@ -2674,7 +2713,6 @@ static gboolean queue_add(signal_user_data_t *ud) { // Add settings to the queue - static gint unique_id = 0; GValue *settings; g_debug("queue_add ()"); @@ -2686,14 +2724,12 @@ queue_add(signal_user_data_t *ud) ud->queue = ghb_array_value_new(32); // Make a copy of current settings to be used for the new job settings = ghb_value_dup(ud->settings); - ghb_settings_set_int(settings, "job_unique_id", unique_id); ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING); - + ghb_settings_set_int(settings, "job_unique_id", 0); ghb_array_append(ud->queue, settings); add_to_queue_list(ud, settings); - ghb_add_job (settings, unique_id); + ghb_save_queue(ud->queue); - unique_id++; return TRUE; } @@ -2763,13 +2799,13 @@ queue_remove_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud) { return; } + unique_id = ghb_settings_get_int(settings, "job_unique_id"); + ghb_remove_job(unique_id); } // Remove the selected item gtk_tree_store_remove(GTK_TREE_STORE(store), &iter); // Remove the corresponding item from the queue list - unique_id = ghb_settings_get_int(settings, "job_unique_id"); ghb_array_remove(ud->queue, row); - ghb_remove_job(unique_id); } else { @@ -2785,6 +2821,7 @@ find_queue_job(GValue *queue, gint unique_id, GValue **job) gint job_unique_id; *job = NULL; + g_debug("find_queue_job"); count = ghb_array_len(queue); for (ii = 0; ii < count; ii++) { @@ -2804,14 +2841,20 @@ queue_buttons_grey(signal_user_data_t *ud, gboolean working) { GtkWidget *widget; GtkAction *action; - gint titleindex = ghb_settings_get_int(ud->settings, "title"); - gboolean title_ok = (titleindex >= 0); + gint queue_count; + gint titleindex; + gboolean title_ok; + + queue_count = ghb_array_len(ud->queue); + titleindex = ghb_settings_get_int(ud->settings, "title"); + title_ok = (titleindex >= 0); + widget = GHB_WIDGET (ud->builder, "queue_start1"); - gtk_widget_set_sensitive (widget, !working && title_ok); + gtk_widget_set_sensitive (widget, !working && (title_ok || queue_count)); widget = GHB_WIDGET (ud->builder, "queue_start2"); - gtk_widget_set_sensitive (widget, !working && title_ok); + gtk_widget_set_sensitive (widget, !working && (title_ok || queue_count)); action = GHB_ACTION (ud->builder, "queue_start_menu"); - gtk_action_set_sensitive (action, !working && title_ok); + gtk_action_set_sensitive (action, !working && (title_ok || queue_count)); widget = GHB_WIDGET (ud->builder, "queue_pause1"); gtk_widget_set_sensitive (widget, working); widget = GHB_WIDGET (ud->builder, "queue_pause2"); @@ -2827,6 +2870,97 @@ queue_buttons_grey(signal_user_data_t *ud, gboolean working) void queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud); static void +submit_job(GValue *settings) +{ + static gint unique_id = 1; + + g_debug("submit_job"); + if (settings == NULL) return; + ghb_settings_set_int(settings, "job_unique_id", unique_id); + ghb_settings_set_int(settings, "job_status", GHB_QUEUE_RUNNING); + ghb_add_job (settings, unique_id); + ghb_start_queue(); + unique_id++; +} + +static void +queue_scan(GValue *js) +{ + gchar *path; + gint titleindex; + + path = ghb_settings_get_string( js, "source"); + titleindex = ghb_settings_get_int(js, "title"); + ghb_backend_queue_scan(path, titleindex+1); + g_free(path); +} + +static GValue* +start_next_job(signal_user_data_t *ud, gboolean find_first) +{ + static gint current = 0; + gint count, ii, jj; + GValue *js; + gint status; + + g_debug("start_next_job"); + count = ghb_array_len(ud->queue); + if (find_first) + { // Start the first pending item in the queue + current = 0; + for (ii = 0; ii < count; ii++) + { + + js = ghb_array_get_nth(ud->queue, ii); + status = ghb_settings_get_int(js, "job_status"); + if (status == GHB_QUEUE_PENDING) + { + current = ii; + queue_scan(js); + return js; + } + } + // Nothing pending + return NULL; + } + // Find the next pending item after the current running item + for (ii = 0; ii < count-1; ii++) + { + js = ghb_array_get_nth(ud->queue, ii); + status = ghb_settings_get_int(js, "job_status"); + if (status == GHB_QUEUE_RUNNING) + { + for (jj = ii+1; jj < count; jj++) + { + js = ghb_array_get_nth(ud->queue, jj); + status = ghb_settings_get_int(js, "job_status"); + if (status == GHB_QUEUE_PENDING) + { + current = jj; + queue_scan(js); + return js; + } + } + } + } + // No running item found? Maybe it was deleted + // Look for a pending item starting from the last index we started + for (ii = current; ii < count; ii++) + { + js = ghb_array_get_nth(ud->queue, ii); + status = ghb_settings_get_int(js, "job_status"); + if (status == GHB_QUEUE_PENDING) + { + current = ii; + queue_scan(js); + return js; + } + } + // Nothing found + return NULL; +} + +static void ghb_backend_events(signal_user_data_t *ud) { ghb_status_t status; @@ -2834,7 +2968,6 @@ ghb_backend_events(signal_user_data_t *ud) GtkProgressBar *progress; gint titleindex; GValue *js; - static gint current_id = -1; gint index; GtkTreeView *treeview; GtkTreeStore *store; @@ -2845,6 +2978,8 @@ ghb_backend_events(signal_user_data_t *ud) ghb_track_status(); ghb_get_status(&status); progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar")); + // First handle the status of title scans + // Then handle the status of the queue if (status.state & GHB_STATE_SCANNING) { status_str = g_strdup_printf ("Scanning title %d of %d...", @@ -2879,22 +3014,56 @@ ghb_backend_events(signal_user_data_t *ud) gtk_progress_bar_set_text (progress, "No Source"); } ghb_clear_state(GHB_STATE_SCANDONE); - queue_buttons_grey(ud, (0 != (status.state & GHB_STATE_WORKING))); + queue_buttons_grey(ud, (0 != (status.queue_state & GHB_STATE_WORKING))); + } + else if (status.queue_state & GHB_STATE_SCANNING) + { + status_str = g_strdup_printf ("Scanning ..."); + gtk_progress_bar_set_text (progress, status_str); + g_free(status_str); + gtk_progress_bar_set_fraction (progress, 0); } - else if (status.state & GHB_STATE_PAUSED) + else if (status.queue_state & GHB_STATE_SCANDONE) + { + ghb_clear_queue_state(GHB_STATE_SCANDONE); + submit_job(ud->current_job); + } + else if (status.queue_state & GHB_STATE_PAUSED) { status_str = g_strdup_printf ("Paused"); gtk_progress_bar_set_text (progress, status_str); g_free(status_str); } - else if (status.state & GHB_STATE_WORKING) + else if (status.queue_state & GHB_STATE_WORKING) { + gchar *task_str, *job_str; + gint qcount; + + if (status.job_count > 1) + { + task_str = g_strdup_printf("pass %d of %d, ", + status.job_cur, status.job_count); + } + else + { + task_str = g_strdup(""); + } + qcount = ghb_array_len(ud->queue); + if (qcount > 1) + { + index = find_queue_job(ud->queue, status.unique_id, &js); + job_str = g_strdup_printf("job %d of %d, ", index+1, qcount); + } + else + { + job_str = g_strdup(""); + } if(status.seconds > -1) { status_str= g_strdup_printf( - "Encoding: task %d of %d, %.2f %%" + "Encoding: %s%s%.2f %%" " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", - status.job_cur, status.job_count, + job_str, task_str, 100.0 * status.progress, status.rate_cur, status.rate_avg, status.hours, status.minutes, status.seconds ); @@ -2902,28 +3071,34 @@ ghb_backend_events(signal_user_data_t *ud) else { status_str= g_strdup_printf( - "Encoding: task %d of %d, %.2f %%", - status.job_cur, status.job_count, + "Encoding: %s%s%.2f %%", + job_str, task_str, 100.0 * status.progress ); } + g_free(job_str); + g_free(task_str); gtk_progress_bar_set_text (progress, status_str); gtk_progress_bar_set_fraction (progress, status.progress); g_free(status_str); } - else if (status.state & GHB_STATE_WORKDONE) + else if (status.queue_state & GHB_STATE_WORKDONE) { + gint qstatus; + work_started = FALSE; queue_buttons_grey(ud, FALSE); - index = find_queue_job(ud->queue, current_id, &js); + index = find_queue_job(ud->queue, status.unique_id, &js); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); + if (ud->cancel_encode) + status.error = GHB_ERROR_CANCELED; switch( status.error ) { case GHB_ERROR_NONE: gtk_progress_bar_set_text( progress, "Rip done!" ); + qstatus = GHB_QUEUE_DONE; if (js != NULL) { - ghb_settings_set_int(js, "job_status", GHB_QUEUE_DONE); gchar *path = g_strdup_printf ("%d", index); if (gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL(store), &iter, path)) @@ -2935,9 +3110,9 @@ ghb_backend_events(signal_user_data_t *ud) break; case GHB_ERROR_CANCELED: gtk_progress_bar_set_text( progress, "Rip canceled." ); + qstatus = GHB_QUEUE_CANCELED; if (js != NULL) { - ghb_settings_set_int(js, "job_status", GHB_QUEUE_CANCELED); gchar *path = g_strdup_printf ("%d", index); if (gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL(store), &iter, path)) @@ -2949,9 +3124,9 @@ ghb_backend_events(signal_user_data_t *ud) break; default: gtk_progress_bar_set_text( progress, "Rip failed."); + qstatus = GHB_QUEUE_CANCELED; if (js != NULL) { - ghb_settings_set_int(js, "job_status", GHB_QUEUE_CANCELED); gchar *path = g_strdup_printf ("%d", index); if (gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL(store), &iter, path)) @@ -2961,49 +3136,29 @@ ghb_backend_events(signal_user_data_t *ud) g_free(path); } } - current_id = -1; gtk_progress_bar_set_fraction (progress, 1.0); - ghb_clear_state(GHB_STATE_WORKDONE); + ghb_clear_queue_state(GHB_STATE_WORKDONE); + if (!ud->cancel_encode) + ud->current_job = start_next_job(ud, FALSE); + else + ud->current_job = NULL; + if (js) + ghb_settings_set_int(js, "job_status", qstatus); + ghb_save_queue(ud->queue); + ud->cancel_encode = FALSE; } - else if (status.state & GHB_STATE_MUXING) + else if (status.queue_state & GHB_STATE_MUXING) { gtk_progress_bar_set_text(progress, "Muxing: this may take awhile..."); } - if (status.state & GHB_STATE_WORKING) + if (status.queue_state & GHB_STATE_WORKING) { if (!work_started) { work_started = TRUE; queue_buttons_grey(ud, TRUE); } - if (status.unique_id != current_id) - { - index = find_queue_job(ud->queue, current_id, &js); - if (js != NULL) - { - ghb_settings_set_int(js, "job_status", GHB_QUEUE_DONE); - treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); - store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); - gchar *path = g_strdup_printf ("%d", index); - if (gtk_tree_model_get_iter_from_string( - GTK_TREE_MODEL(store), &iter, path)) - { - gtk_tree_store_set(store, &iter, 0, "hb-complete", -1); - } - g_free(path); - } - - index = find_queue_job(ud->queue, status.unique_id, &js); - if (js != NULL) - { - ghb_settings_set_int(js, "job_status", GHB_QUEUE_RUNNING); - current_id = status.unique_id; - } - } - else - { - index = find_queue_job(ud->queue, status.unique_id, &js); - } + index = find_queue_job(ud->queue, status.unique_id, &js); if (index >= 0) { gchar working_icon[] = "hb-working0"; @@ -3293,38 +3448,32 @@ update_chapter_list(signal_user_data_t *ud) } } -static GtkTreePath *nextPath = NULL; -static gboolean chapter_selection_changed = FALSE; +static gint chapter_edit_key = 0; -static gboolean -next_cell(signal_user_data_t *ud) +gboolean +chapter_keypress_cb( + GhbCellRendererText *cell, + GdkEventKey *event, + signal_user_data_t *ud) { - GtkTreeView *treeview; - GtkTreeViewColumn *column; - - if (nextPath) - { - if (!chapter_selection_changed) - { - treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); - column = gtk_tree_view_get_column(treeview, 1); - gtk_tree_view_set_cursor(treeview, nextPath, column, TRUE); - } - gtk_tree_path_free(nextPath); - nextPath = NULL; - } - chapter_selection_changed = FALSE; + chapter_edit_key = event->keyval; return FALSE; } void -chapter_edited_cb(GtkCellRendererText *cell, gchar *path, gchar *text, signal_user_data_t *ud) +chapter_edited_cb( + GhbCellRendererText *cell, + gchar *path, + gchar *text, + signal_user_data_t *ud) { GtkTreePath *treepath; GtkListStore *store; GtkTreeView *treeview; GtkTreeIter iter; gint index; + gint *pi; + gint row; g_debug("chapter_edited_cb ()"); g_debug("path (%s)", path); @@ -3332,8 +3481,9 @@ chapter_edited_cb(GtkCellRendererText *cell, gchar *path, gchar *text, signal_us treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview)); treepath = gtk_tree_path_new_from_string (path); + pi = gtk_tree_path_get_indices(treepath); + row = pi[0]; gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath); - gtk_tree_path_free (treepath); gtk_list_store_set(store, &iter, 1, text, 2, TRUE, @@ -3346,29 +3496,54 @@ chapter_edited_cb(GtkCellRendererText *cell, gchar *path, gchar *text, signal_us chapters = ghb_settings_get_value(ud->settings, "chapter_list"); chapter = ghb_array_get_nth(chapters, index-1); g_value_set_string(chapter, text); - if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) + if ((chapter_edit_key == GDK_Return || chapter_edit_key == GDK_Down) && + gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) { - nextPath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); - chapter_selection_changed = FALSE; + GtkTreeViewColumn *column; + + gtk_tree_path_next(treepath); // When a cell has been edited, I want to advance to the // next cell and start editing it automaitcally. // Unfortunately, we may not be in a state here where // editing is allowed. This happens when the user selects // a new cell with the mouse instead of just hitting enter. // Some kind of Gtk quirk. widget_editable==NULL assertion. - // Editing is enabled again once the current event has been + // Editing is enabled again once the selection event has been // processed. So I'm queueing up a callback to be called // when things go idle. There, I will advance to the next // cell and initiate editing. - g_idle_add((GSourceFunc)next_cell, ud); + // + // Now, you might be asking why I don't catch the keypress + // event and determine what action to take based on that. + // The Gtk developers in their infinite wisdom have made the + // actual GtkEdit widget being used a private member of + // GtkCellRendererText, so it can not be accessed to hang a + // signal handler off of. And they also do not propagate the + // keypress signals in any other way. So that information is lost. + //g_idle_add((GSourceFunc)next_cell, ud); + // + // Keeping the above comment for posterity. + // I got industrious and made my own CellTextRendererText that + // passes on the key-press-event. So now I have much better + // control of this. + column = gtk_tree_view_get_column(treeview, 1); + gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); + } + else if (chapter_edit_key == GDK_Up && row > 0) + { + GtkTreeViewColumn *column; + gtk_tree_path_prev(treepath); + column = gtk_tree_view_get_column(treeview, 1); + gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); } + gtk_tree_path_free (treepath); } void chapter_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud) { g_debug("chapter_list_selection_changed_cb ()"); - chapter_selection_changed = TRUE; + //chapter_selection_changed = TRUE; } void @@ -3427,6 +3602,7 @@ queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) gboolean running = FALSE; gint count, ii; gint status; + gint state; count = ghb_array_len(ud->queue); for (ii = 0; ii < count; ii++) @@ -3447,12 +3623,18 @@ queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) if (!queue_add(ud)) return; } - ghb_start_queue(); + state = ghb_get_queue_state(); + if (state == GHB_STATE_IDLE) + { + // Add the first pending queue item and start + ud->current_job = start_next_job(ud, TRUE); + } } void queue_stop_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { + ud->cancel_encode = TRUE; cancel_encode(NULL); } @@ -3880,3 +4062,68 @@ easter_egg_cb( return FALSE; } +gboolean +ghb_reload_queue(signal_user_data_t *ud) +{ + GValue *queue; + gint unfinished = 0; + gint count, ii; + gint status; + GValue *settings; + gchar *message; + + g_debug("ghb_reload_queue"); + queue = ghb_load_queue(); + // Look for unfinished entries + count = ghb_array_len(queue); + for (ii = 0; ii < count; ii++) + { + settings = ghb_array_get_nth(queue, ii); + status = ghb_settings_get_int(settings, "job_status"); + if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_CANCELED) + { + unfinished++; + } + } + if (unfinished) + { + message = g_strdup_printf( + "You have %d unfinished jobs in a saved queue.\n\n" + "Would you like to reload them?", + unfinished); + if (ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "No", "Yes")) + { + GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window"); + gtk_widget_show (widget); + + ud->queue = queue; + // First get rid of any old items we don't want + for (ii = count-1; ii >= 0; ii--) + { + settings = ghb_array_get_nth(queue, ii); + status = ghb_settings_get_int(settings, "job_status"); + if (status == GHB_QUEUE_DONE || status == GHB_QUEUE_CANCELED) + { + ghb_array_remove(queue, ii); + } + } + count = ghb_array_len(queue); + for (ii = 0; ii < count; ii++) + { + settings = ghb_array_get_nth(queue, ii); + ghb_settings_set_int(settings, "job_unique_id", 0); + ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING); + add_to_queue_list(ud, settings); + } + queue_buttons_grey(ud, FALSE); + } + else + { + ghb_value_free(queue); + ghb_remove_queue_file(); + } + g_free(message); + } + return FALSE; +} + diff --git a/gtk/src/callbacks.h b/gtk/src/callbacks.h index 652c6ae53..6bb8d3aea 100644 --- a/gtk/src/callbacks.h +++ b/gtk/src/callbacks.h @@ -42,6 +42,7 @@ gboolean ghb_message_dialog( GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes); void ghb_init_dep_map(); +gboolean ghb_reload_queue(signal_user_data_t *ud); #endif // _CALLBACKS_H_ diff --git a/gtk/src/ghb.ui b/gtk/src/ghb.ui index 9377dc085..8f24a88a0 100644 --- a/gtk/src/ghb.ui +++ b/gtk/src/ghb.ui @@ -715,6 +715,8 @@ <property name="tooltip-text" translatable="yes">Destination path with file name for output.</property> <property name="width_chars">41</property> <signal handler="destination_entry_changed_cb" name="changed"/> + <signal handler="destination_grab_cb" name="grab-focus" after="yes"/> + <accelerator key="d" signal="grab-focus" modifiers="GDK_MOD1_MASK"/> </object> <packing> <property name="position">1</property> @@ -858,7 +860,7 @@ <child type="label"> <object class="GtkLabel" id="label11"> <property name="visible">True</property> - <property name="label" translatable="yes"><b>Destination</b></property> + <property name="label" translatable="yes"><b><u>D</u>estination</b></property> <property name="use_markup">True</property> </object> </child> @@ -3130,6 +3132,7 @@ no-fast-pskip=0:no-dct-decimate=0:cabac=1</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="headers_clickable">True</property> <property name="rules_hint">True</property> + <property name="enable-search">False</property> </object> </child> </object> @@ -3891,6 +3894,7 @@ this setting.</property> </object> <packing> <property name="position">1</property> + <property name="expand">False</property> </packing> </child> <child> @@ -3914,6 +3918,7 @@ this setting.</property> </object> <packing> <property name="position">2</property> + <property name="expand">True</property> </packing> </child> <child> diff --git a/gtk/src/ghbcellrenderertext.c b/gtk/src/ghbcellrenderertext.c new file mode 100644 index 000000000..849cdd242 --- /dev/null +++ b/gtk/src/ghbcellrenderertext.c @@ -0,0 +1,1972 @@ +/* gtkcellrenderertext.c + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#include <glib/gi18n-lib.h> +#include <gtk/gtkmarshal.h> +//#include <gtk/gtkeditable.h> +//#include <gtk/gtkentry.h> +//#include <gtk/gtkintl.h> +//#include <gtk/gtkprivate.h> +//#include <gtk/gtktreeprivate.h> +//#include <gtk/gtkalias.h> + +#include "marshalers.h" +#include "ghbcellrenderertext.h" + +#ifdef ENABLE_NLS +#define P_(String) dgettext(GETTEXT_PACKAGE "-properties",String) +#else +#define P_(String) (String) +#endif + +#define I_(string) g_intern_static_string (string) + +#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB +#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB +#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB + + + +static void ghb_cell_renderer_text_finalize (GObject *object); + +static void ghb_cell_renderer_text_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void ghb_cell_renderer_text_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void ghb_cell_renderer_text_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height); +static void ghb_cell_renderer_text_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags); + +static GtkCellEditable *ghb_cell_renderer_text_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags); + +enum { + EDITED, + KEYPRESS, + LAST_SIGNAL +}; + +enum { + PROP_0, + + PROP_TEXT, + PROP_MARKUP, + PROP_ATTRIBUTES, + PROP_SINGLE_PARAGRAPH_MODE, + PROP_WIDTH_CHARS, + PROP_WRAP_WIDTH, + PROP_ALIGN, + + /* Style args */ + PROP_BACKGROUND, + PROP_FOREGROUND, + PROP_BACKGROUND_GDK, + PROP_FOREGROUND_GDK, + PROP_FONT, + PROP_FONT_DESC, + PROP_FAMILY, + PROP_STYLE, + PROP_VARIANT, + PROP_WEIGHT, + PROP_STRETCH, + PROP_SIZE, + PROP_SIZE_POINTS, + PROP_SCALE, + PROP_EDITABLE, + PROP_STRIKETHROUGH, + PROP_UNDERLINE, + PROP_RISE, + PROP_LANGUAGE, + PROP_ELLIPSIZE, + PROP_WRAP_MODE, + + /* Whether-a-style-arg-is-set args */ + PROP_BACKGROUND_SET, + PROP_FOREGROUND_SET, + PROP_FAMILY_SET, + PROP_STYLE_SET, + PROP_VARIANT_SET, + PROP_WEIGHT_SET, + PROP_STRETCH_SET, + PROP_SIZE_SET, + PROP_SCALE_SET, + PROP_EDITABLE_SET, + PROP_STRIKETHROUGH_SET, + PROP_UNDERLINE_SET, + PROP_RISE_SET, + PROP_LANGUAGE_SET, + PROP_ELLIPSIZE_SET, + PROP_ALIGN_SET +}; + +static guint text_cell_renderer_signals [LAST_SIGNAL]; + +#define GHB_CELL_RENDERER_TEXT_PATH "gtk-cell-renderer-text-path" + +#define GHB_CELL_RENDERER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererTextPrivate)) + +typedef struct _GhbCellRendererTextPrivate GhbCellRendererTextPrivate; +struct _GhbCellRendererTextPrivate +{ + guint single_paragraph : 1; + guint language_set : 1; + guint markup_set : 1; + guint ellipsize_set : 1; + guint align_set : 1; + + gulong focus_out_id; + PangoLanguage *language; + PangoEllipsizeMode ellipsize; + PangoWrapMode wrap_mode; + PangoAlignment align; + + gulong populate_popup_id; + gulong entry_menu_popdown_timeout; + gboolean in_entry_menu; + + gint width_chars; + gint wrap_width; + + GtkWidget *entry; +}; + +G_DEFINE_TYPE (GhbCellRendererText, ghb_cell_renderer_text, GTK_TYPE_CELL_RENDERER) + +static void +ghb_cell_renderer_text_init (GhbCellRendererText *celltext) +{ + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (celltext); + + GTK_CELL_RENDERER (celltext)->xalign = 0.0; + GTK_CELL_RENDERER (celltext)->yalign = 0.5; + GTK_CELL_RENDERER (celltext)->xpad = 2; + GTK_CELL_RENDERER (celltext)->ypad = 2; + celltext->font_scale = 1.0; + celltext->fixed_height_rows = -1; + celltext->font = pango_font_description_new (); + + priv->width_chars = -1; + priv->wrap_width = -1; + priv->wrap_mode = PANGO_WRAP_CHAR; + priv->align = PANGO_ALIGN_LEFT; + priv->align_set = FALSE; +} + +static void +ghb_cell_renderer_text_class_init (GhbCellRendererTextClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + + object_class->finalize = ghb_cell_renderer_text_finalize; + + object_class->get_property = ghb_cell_renderer_text_get_property; + object_class->set_property = ghb_cell_renderer_text_set_property; + + cell_class->get_size = ghb_cell_renderer_text_get_size; + cell_class->render = ghb_cell_renderer_text_render; + cell_class->start_editing = ghb_cell_renderer_text_start_editing; + + g_object_class_install_property (object_class, + PROP_TEXT, + g_param_spec_string ("text", + P_("Text"), + P_("Text to render"), + NULL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_MARKUP, + g_param_spec_string ("markup", + P_("Markup"), + P_("Marked up text to render"), + NULL, + GTK_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_ATTRIBUTES, + g_param_spec_boxed ("attributes", + P_("Attributes"), + P_("A list of style attributes to apply to the text of the renderer"), + PANGO_TYPE_ATTR_LIST, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_SINGLE_PARAGRAPH_MODE, + g_param_spec_boolean ("single-paragraph-mode", + P_("Single Paragraph Mode"), + P_("Whether or not to keep all text in a single paragraph"), + FALSE, + GTK_PARAM_READWRITE)); + + + g_object_class_install_property (object_class, + PROP_BACKGROUND, + g_param_spec_string ("background", + P_("Background color name"), + P_("Background color as a string"), + NULL, + GTK_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_BACKGROUND_GDK, + g_param_spec_boxed ("background-gdk", + P_("Background color"), + P_("Background color as a GdkColor"), + GDK_TYPE_COLOR, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_FOREGROUND, + g_param_spec_string ("foreground", + P_("Foreground color name"), + P_("Foreground color as a string"), + NULL, + GTK_PARAM_WRITABLE)); + + g_object_class_install_property (object_class, + PROP_FOREGROUND_GDK, + g_param_spec_boxed ("foreground-gdk", + P_("Foreground color"), + P_("Foreground color as a GdkColor"), + GDK_TYPE_COLOR, + GTK_PARAM_READWRITE)); + + + g_object_class_install_property (object_class, + PROP_EDITABLE, + g_param_spec_boolean ("editable", + P_("Editable"), + P_("Whether the text can be modified by the user"), + FALSE, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_FONT, + g_param_spec_string ("font", + P_("Font"), + P_("Font description as a string, e.g. \"Sans Italic 12\""), + NULL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_FONT_DESC, + g_param_spec_boxed ("font-desc", + P_("Font"), + P_("Font description as a PangoFontDescription struct"), + PANGO_TYPE_FONT_DESCRIPTION, + GTK_PARAM_READWRITE)); + + + g_object_class_install_property (object_class, + PROP_FAMILY, + g_param_spec_string ("family", + P_("Font family"), + P_("Name of the font family, e.g. Sans, Helvetica, Times, Monospace"), + NULL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_STYLE, + g_param_spec_enum ("style", + P_("Font style"), + P_("Font style"), + PANGO_TYPE_STYLE, + PANGO_STYLE_NORMAL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_VARIANT, + g_param_spec_enum ("variant", + P_("Font variant"), + P_("Font variant"), + PANGO_TYPE_VARIANT, + PANGO_VARIANT_NORMAL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_WEIGHT, + g_param_spec_int ("weight", + P_("Font weight"), + P_("Font weight"), + 0, + G_MAXINT, + PANGO_WEIGHT_NORMAL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_STRETCH, + g_param_spec_enum ("stretch", + P_("Font stretch"), + P_("Font stretch"), + PANGO_TYPE_STRETCH, + PANGO_STRETCH_NORMAL, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_SIZE, + g_param_spec_int ("size", + P_("Font size"), + P_("Font size"), + 0, + G_MAXINT, + 0, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_SIZE_POINTS, + g_param_spec_double ("size-points", + P_("Font points"), + P_("Font size in points"), + 0.0, + G_MAXDOUBLE, + 0.0, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_SCALE, + g_param_spec_double ("scale", + P_("Font scale"), + P_("Font scaling factor"), + 0.0, + G_MAXDOUBLE, + 1.0, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_RISE, + g_param_spec_int ("rise", + P_("Rise"), + P_("Offset of text above the baseline " + "(below the baseline if rise is negative)"), + -G_MAXINT, + G_MAXINT, + 0, + GTK_PARAM_READWRITE)); + + + g_object_class_install_property (object_class, + PROP_STRIKETHROUGH, + g_param_spec_boolean ("strikethrough", + P_("Strikethrough"), + P_("Whether to strike through the text"), + FALSE, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_UNDERLINE, + g_param_spec_enum ("underline", + P_("Underline"), + P_("Style of underline for this text"), + PANGO_TYPE_UNDERLINE, + PANGO_UNDERLINE_NONE, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_LANGUAGE, + g_param_spec_string ("language", + P_("Language"), + P_("The language this text is in, as an ISO code. " + "Pango can use this as a hint when rendering the text. " + "If you don't understand this parameter, you probably don't need it"), + NULL, + GTK_PARAM_READWRITE)); + + + /** + * GhbCellRendererText:ellipsize: + * + * Specifies the preferred place to ellipsize the string, if the cell renderer + * does not have enough room to display the entire string. Setting it to + * %PANGO_ELLIPSIZE_NONE turns off ellipsizing. See the wrap-width property + * for another way of making the text fit into a given width. + * + * Since: 2.6 + */ + g_object_class_install_property (object_class, + PROP_ELLIPSIZE, + g_param_spec_enum ("ellipsize", + P_("Ellipsize"), + P_("The preferred place to ellipsize the string, " + "if the cell renderer does not have enough room " + "to display the entire string"), + PANGO_TYPE_ELLIPSIZE_MODE, + PANGO_ELLIPSIZE_NONE, + GTK_PARAM_READWRITE)); + + /** + * GhbCellRendererText:width-chars: + * + * The desired width of the cell, in characters. If this property is set to + * -1, the width will be calculated automatically, otherwise the cell will + * request either 3 characters or the property value, whichever is greater. + * + * Since: 2.6 + **/ + g_object_class_install_property (object_class, + PROP_WIDTH_CHARS, + g_param_spec_int ("width-chars", + P_("Width In Characters"), + P_("The desired width of the label, in characters"), + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE)); + + /** + * GhbCellRendererText:wrap-mode: + * + * Specifies how to break the string into multiple lines, if the cell + * renderer does not have enough room to display the entire string. + * This property has no effect unless the wrap-width property is set. + * + * Since: 2.8 + */ + g_object_class_install_property (object_class, + PROP_WRAP_MODE, + g_param_spec_enum ("wrap-mode", + P_("Wrap mode"), + P_("How to break the string into multiple lines, " + "if the cell renderer does not have enough room " + "to display the entire string"), + PANGO_TYPE_WRAP_MODE, + PANGO_WRAP_CHAR, + GTK_PARAM_READWRITE)); + + /** + * GhbCellRendererText:wrap-width: + * + * Specifies the width at which the text is wrapped. The wrap-mode property can + * be used to influence at what character positions the line breaks can be placed. + * Setting wrap-width to -1 turns wrapping off. + * + * Since: 2.8 + */ + g_object_class_install_property (object_class, + PROP_WRAP_WIDTH, + g_param_spec_int ("wrap-width", + P_("Wrap width"), + P_("The width at which the text is wrapped"), + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE)); + + /** + * GhbCellRendererText:alignment: + * + * Specifies how to align the lines of text with respect to each other. + * + * Note that this property describes how to align the lines of text in + * case there are several of them. The "xalign" property of #GtkCellRenderer, + * on the other hand, sets the horizontal alignment of the whole text. + * + * Since: 2.10 + */ + g_object_class_install_property (object_class, + PROP_ALIGN, + g_param_spec_enum ("alignment", + P_("Alignment"), + P_("How to align the lines"), + PANGO_TYPE_ALIGNMENT, + PANGO_ALIGN_LEFT, + GTK_PARAM_READWRITE)); + + /* Style props are set or not */ + +#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (object_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE)) + + ADD_SET_PROP ("background-set", PROP_BACKGROUND_SET, + P_("Background set"), + P_("Whether this tag affects the background color")); + + ADD_SET_PROP ("foreground-set", PROP_FOREGROUND_SET, + P_("Foreground set"), + P_("Whether this tag affects the foreground color")); + + ADD_SET_PROP ("editable-set", PROP_EDITABLE_SET, + P_("Editability set"), + P_("Whether this tag affects text editability")); + + ADD_SET_PROP ("family-set", PROP_FAMILY_SET, + P_("Font family set"), + P_("Whether this tag affects the font family")); + + ADD_SET_PROP ("style-set", PROP_STYLE_SET, + P_("Font style set"), + P_("Whether this tag affects the font style")); + + ADD_SET_PROP ("variant-set", PROP_VARIANT_SET, + P_("Font variant set"), + P_("Whether this tag affects the font variant")); + + ADD_SET_PROP ("weight-set", PROP_WEIGHT_SET, + P_("Font weight set"), + P_("Whether this tag affects the font weight")); + + ADD_SET_PROP ("stretch-set", PROP_STRETCH_SET, + P_("Font stretch set"), + P_("Whether this tag affects the font stretch")); + + ADD_SET_PROP ("size-set", PROP_SIZE_SET, + P_("Font size set"), + P_("Whether this tag affects the font size")); + + ADD_SET_PROP ("scale-set", PROP_SCALE_SET, + P_("Font scale set"), + P_("Whether this tag scales the font size by a factor")); + + ADD_SET_PROP ("rise-set", PROP_RISE_SET, + P_("Rise set"), + P_("Whether this tag affects the rise")); + + ADD_SET_PROP ("strikethrough-set", PROP_STRIKETHROUGH_SET, + P_("Strikethrough set"), + P_("Whether this tag affects strikethrough")); + + ADD_SET_PROP ("underline-set", PROP_UNDERLINE_SET, + P_("Underline set"), + P_("Whether this tag affects underlining")); + + ADD_SET_PROP ("language-set", PROP_LANGUAGE_SET, + P_("Language set"), + P_("Whether this tag affects the language the text is rendered as")); + + ADD_SET_PROP ("ellipsize-set", PROP_ELLIPSIZE_SET, + P_("Ellipsize set"), + P_("Whether this tag affects the ellipsize mode")); + + ADD_SET_PROP ("align-set", PROP_ALIGN_SET, + P_("Align set"), + P_("Whether this tag affects the alignment mode")); + + /** + * GhbCellRendererText::edited + * @renderer: the object which received the signal + * @path: the path identifying the edited cell + * @new_text: the new text + * + * This signal is emitted after @renderer has been edited. + * + * It is the responsibility of the application to update the model + * and store @new_text at the position indicated by @path. + */ + text_cell_renderer_signals [EDITED] = + g_signal_new (I_("edited"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GhbCellRendererTextClass, edited), + NULL, NULL, + ghb_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_STRING); + + text_cell_renderer_signals [KEYPRESS] = + g_signal_new (I_("key-press-event"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GhbCellRendererTextClass, keypress), + NULL, NULL, + ghb_marshal_BOOLEAN__BOXED, + G_TYPE_BOOLEAN, 1, + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + + g_type_class_add_private (object_class, sizeof (GhbCellRendererTextPrivate)); +} + +static void +ghb_cell_renderer_text_finalize (GObject *object) +{ + GhbCellRendererText *celltext = GHB_CELL_RENDERER_TEXT (object); + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (object); + + pango_font_description_free (celltext->font); + + g_free (celltext->text); + + if (celltext->extra_attrs) + pango_attr_list_unref (celltext->extra_attrs); + + if (priv->language) + g_object_unref (priv->language); + + (* G_OBJECT_CLASS (ghb_cell_renderer_text_parent_class)->finalize) (object); +} + +static PangoFontMask +get_property_font_set_mask (guint prop_id) +{ + switch (prop_id) + { + case PROP_FAMILY_SET: + return PANGO_FONT_MASK_FAMILY; + case PROP_STYLE_SET: + return PANGO_FONT_MASK_STYLE; + case PROP_VARIANT_SET: + return PANGO_FONT_MASK_VARIANT; + case PROP_WEIGHT_SET: + return PANGO_FONT_MASK_WEIGHT; + case PROP_STRETCH_SET: + return PANGO_FONT_MASK_STRETCH; + case PROP_SIZE_SET: + return PANGO_FONT_MASK_SIZE; + } + + return 0; +} + +static void +ghb_cell_renderer_text_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GhbCellRendererText *celltext = GHB_CELL_RENDERER_TEXT (object); + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (object); + + switch (param_id) + { + case PROP_TEXT: + g_value_set_string (value, celltext->text); + break; + + case PROP_ATTRIBUTES: + g_value_set_boxed (value, celltext->extra_attrs); + break; + + case PROP_SINGLE_PARAGRAPH_MODE: + g_value_set_boolean (value, priv->single_paragraph); + break; + + case PROP_BACKGROUND_GDK: + { + GdkColor color; + + color.red = celltext->background.red; + color.green = celltext->background.green; + color.blue = celltext->background.blue; + + g_value_set_boxed (value, &color); + } + break; + + case PROP_FOREGROUND_GDK: + { + GdkColor color; + + color.red = celltext->foreground.red; + color.green = celltext->foreground.green; + color.blue = celltext->foreground.blue; + + g_value_set_boxed (value, &color); + } + break; + + case PROP_FONT: + g_value_take_string (value, pango_font_description_to_string (celltext->font)); + break; + + case PROP_FONT_DESC: + g_value_set_boxed (value, celltext->font); + break; + + case PROP_FAMILY: + g_value_set_string (value, pango_font_description_get_family (celltext->font)); + break; + + case PROP_STYLE: + g_value_set_enum (value, pango_font_description_get_style (celltext->font)); + break; + + case PROP_VARIANT: + g_value_set_enum (value, pango_font_description_get_variant (celltext->font)); + break; + + case PROP_WEIGHT: + g_value_set_int (value, pango_font_description_get_weight (celltext->font)); + break; + + case PROP_STRETCH: + g_value_set_enum (value, pango_font_description_get_stretch (celltext->font)); + break; + + case PROP_SIZE: + g_value_set_int (value, pango_font_description_get_size (celltext->font)); + break; + + case PROP_SIZE_POINTS: + g_value_set_double (value, ((double)pango_font_description_get_size (celltext->font)) / (double)PANGO_SCALE); + break; + + case PROP_SCALE: + g_value_set_double (value, celltext->font_scale); + break; + + case PROP_EDITABLE: + g_value_set_boolean (value, celltext->editable); + break; + + case PROP_STRIKETHROUGH: + g_value_set_boolean (value, celltext->strikethrough); + break; + + case PROP_UNDERLINE: + g_value_set_enum (value, celltext->underline_style); + break; + + case PROP_RISE: + g_value_set_int (value, celltext->rise); + break; + + case PROP_LANGUAGE: + g_value_set_static_string (value, pango_language_to_string (priv->language)); + break; + + case PROP_ELLIPSIZE: + g_value_set_enum (value, priv->ellipsize); + break; + + case PROP_WRAP_MODE: + g_value_set_enum (value, priv->wrap_mode); + break; + + case PROP_WRAP_WIDTH: + g_value_set_int (value, priv->wrap_width); + break; + + case PROP_ALIGN: + g_value_set_enum (value, priv->align); + break; + + case PROP_BACKGROUND_SET: + g_value_set_boolean (value, celltext->background_set); + break; + + case PROP_FOREGROUND_SET: + g_value_set_boolean (value, celltext->foreground_set); + break; + + case PROP_FAMILY_SET: + case PROP_STYLE_SET: + case PROP_VARIANT_SET: + case PROP_WEIGHT_SET: + case PROP_STRETCH_SET: + case PROP_SIZE_SET: + { + PangoFontMask mask = get_property_font_set_mask (param_id); + g_value_set_boolean (value, (pango_font_description_get_set_fields (celltext->font) & mask) != 0); + + break; + } + + case PROP_SCALE_SET: + g_value_set_boolean (value, celltext->scale_set); + break; + + case PROP_EDITABLE_SET: + g_value_set_boolean (value, celltext->editable_set); + break; + + case PROP_STRIKETHROUGH_SET: + g_value_set_boolean (value, celltext->strikethrough_set); + break; + + case PROP_UNDERLINE_SET: + g_value_set_boolean (value, celltext->underline_set); + break; + + case PROP_RISE_SET: + g_value_set_boolean (value, celltext->rise_set); + break; + + case PROP_LANGUAGE_SET: + g_value_set_boolean (value, priv->language_set); + break; + + case PROP_ELLIPSIZE_SET: + g_value_set_boolean (value, priv->ellipsize_set); + break; + + case PROP_ALIGN_SET: + g_value_set_boolean (value, priv->align_set); + break; + + case PROP_WIDTH_CHARS: + g_value_set_int (value, priv->width_chars); + break; + + case PROP_BACKGROUND: + case PROP_FOREGROUND: + case PROP_MARKUP: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + + +static void +set_bg_color (GhbCellRendererText *celltext, + GdkColor *color) +{ + if (color) + { + if (!celltext->background_set) + { + celltext->background_set = TRUE; + g_object_notify (G_OBJECT (celltext), "background-set"); + } + + celltext->background.red = color->red; + celltext->background.green = color->green; + celltext->background.blue = color->blue; + } + else + { + if (celltext->background_set) + { + celltext->background_set = FALSE; + g_object_notify (G_OBJECT (celltext), "background-set"); + } + } +} + + +static void +set_fg_color (GhbCellRendererText *celltext, + GdkColor *color) +{ + if (color) + { + if (!celltext->foreground_set) + { + celltext->foreground_set = TRUE; + g_object_notify (G_OBJECT (celltext), "foreground-set"); + } + + celltext->foreground.red = color->red; + celltext->foreground.green = color->green; + celltext->foreground.blue = color->blue; + } + else + { + if (celltext->foreground_set) + { + celltext->foreground_set = FALSE; + g_object_notify (G_OBJECT (celltext), "foreground-set"); + } + } +} + +static PangoFontMask +set_font_desc_fields (PangoFontDescription *desc, + PangoFontMask to_set) +{ + PangoFontMask changed_mask = 0; + + if (to_set & PANGO_FONT_MASK_FAMILY) + { + const char *family = pango_font_description_get_family (desc); + if (!family) + { + family = "sans"; + changed_mask |= PANGO_FONT_MASK_FAMILY; + } + + pango_font_description_set_family (desc, family); + } + if (to_set & PANGO_FONT_MASK_STYLE) + pango_font_description_set_style (desc, pango_font_description_get_style (desc)); + if (to_set & PANGO_FONT_MASK_VARIANT) + pango_font_description_set_variant (desc, pango_font_description_get_variant (desc)); + if (to_set & PANGO_FONT_MASK_WEIGHT) + pango_font_description_set_weight (desc, pango_font_description_get_weight (desc)); + if (to_set & PANGO_FONT_MASK_STRETCH) + pango_font_description_set_stretch (desc, pango_font_description_get_stretch (desc)); + if (to_set & PANGO_FONT_MASK_SIZE) + { + gint size = pango_font_description_get_size (desc); + if (size <= 0) + { + size = 10 * PANGO_SCALE; + changed_mask |= PANGO_FONT_MASK_SIZE; + } + + pango_font_description_set_size (desc, size); + } + + return changed_mask; +} + +static void +notify_set_changed (GObject *object, + PangoFontMask changed_mask) +{ + if (changed_mask & PANGO_FONT_MASK_FAMILY) + g_object_notify (object, "family-set"); + if (changed_mask & PANGO_FONT_MASK_STYLE) + g_object_notify (object, "style-set"); + if (changed_mask & PANGO_FONT_MASK_VARIANT) + g_object_notify (object, "variant-set"); + if (changed_mask & PANGO_FONT_MASK_WEIGHT) + g_object_notify (object, "weight-set"); + if (changed_mask & PANGO_FONT_MASK_STRETCH) + g_object_notify (object, "stretch-set"); + if (changed_mask & PANGO_FONT_MASK_SIZE) + g_object_notify (object, "size-set"); +} + +static void +notify_fields_changed (GObject *object, + PangoFontMask changed_mask) +{ + if (changed_mask & PANGO_FONT_MASK_FAMILY) + g_object_notify (object, "family"); + if (changed_mask & PANGO_FONT_MASK_STYLE) + g_object_notify (object, "style"); + if (changed_mask & PANGO_FONT_MASK_VARIANT) + g_object_notify (object, "variant"); + if (changed_mask & PANGO_FONT_MASK_WEIGHT) + g_object_notify (object, "weight"); + if (changed_mask & PANGO_FONT_MASK_STRETCH) + g_object_notify (object, "stretch"); + if (changed_mask & PANGO_FONT_MASK_SIZE) + g_object_notify (object, "size"); +} + +static void +set_font_description (GhbCellRendererText *celltext, + PangoFontDescription *font_desc) +{ + GObject *object = G_OBJECT (celltext); + PangoFontDescription *new_font_desc; + PangoFontMask old_mask, new_mask, changed_mask, set_changed_mask; + + if (font_desc) + new_font_desc = pango_font_description_copy (font_desc); + else + new_font_desc = pango_font_description_new (); + + old_mask = pango_font_description_get_set_fields (celltext->font); + new_mask = pango_font_description_get_set_fields (new_font_desc); + + changed_mask = old_mask | new_mask; + set_changed_mask = old_mask ^ new_mask; + + pango_font_description_free (celltext->font); + celltext->font = new_font_desc; + + g_object_freeze_notify (object); + + g_object_notify (object, "font-desc"); + g_object_notify (object, "font"); + + if (changed_mask & PANGO_FONT_MASK_FAMILY) + g_object_notify (object, "family"); + if (changed_mask & PANGO_FONT_MASK_STYLE) + g_object_notify (object, "style"); + if (changed_mask & PANGO_FONT_MASK_VARIANT) + g_object_notify (object, "variant"); + if (changed_mask & PANGO_FONT_MASK_WEIGHT) + g_object_notify (object, "weight"); + if (changed_mask & PANGO_FONT_MASK_STRETCH) + g_object_notify (object, "stretch"); + if (changed_mask & PANGO_FONT_MASK_SIZE) + { + g_object_notify (object, "size"); + g_object_notify (object, "size-points"); + } + + notify_set_changed (object, set_changed_mask); + + g_object_thaw_notify (object); +} + +static void +ghb_cell_renderer_text_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GhbCellRendererText *celltext = GHB_CELL_RENDERER_TEXT (object); + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (object); + + switch (param_id) + { + case PROP_TEXT: + g_free (celltext->text); + + if (priv->markup_set) + { + if (celltext->extra_attrs) + pango_attr_list_unref (celltext->extra_attrs); + celltext->extra_attrs = NULL; + priv->markup_set = FALSE; + } + + celltext->text = g_strdup (g_value_get_string (value)); + g_object_notify (object, "text"); + break; + + case PROP_ATTRIBUTES: + if (celltext->extra_attrs) + pango_attr_list_unref (celltext->extra_attrs); + + celltext->extra_attrs = g_value_get_boxed (value); + if (celltext->extra_attrs) + pango_attr_list_ref (celltext->extra_attrs); + break; + case PROP_MARKUP: + { + const gchar *str; + gchar *text = NULL; + GError *error = NULL; + PangoAttrList *attrs = NULL; + + str = g_value_get_string (value); + if (str && !pango_parse_markup (str, + -1, + 0, + &attrs, + &text, + NULL, + &error)) + { + g_warning ("Failed to set text from markup due to error parsing markup: %s", + error->message); + g_error_free (error); + return; + } + + g_free (celltext->text); + + if (celltext->extra_attrs) + pango_attr_list_unref (celltext->extra_attrs); + + celltext->text = text; + celltext->extra_attrs = attrs; + priv->markup_set = TRUE; + } + break; + + case PROP_SINGLE_PARAGRAPH_MODE: + priv->single_paragraph = g_value_get_boolean (value); + break; + + case PROP_BACKGROUND: + { + GdkColor color; + + if (!g_value_get_string (value)) + set_bg_color (celltext, NULL); /* reset to background_set to FALSE */ + else if (gdk_color_parse (g_value_get_string (value), &color)) + set_bg_color (celltext, &color); + else + g_warning ("Don't know color `%s'", g_value_get_string (value)); + + g_object_notify (object, "background-gdk"); + } + break; + + case PROP_FOREGROUND: + { + GdkColor color; + + if (!g_value_get_string (value)) + set_fg_color (celltext, NULL); /* reset to foreground_set to FALSE */ + else if (gdk_color_parse (g_value_get_string (value), &color)) + set_fg_color (celltext, &color); + else + g_warning ("Don't know color `%s'", g_value_get_string (value)); + + g_object_notify (object, "foreground-gdk"); + } + break; + + case PROP_BACKGROUND_GDK: + /* This notifies the GObject itself. */ + set_bg_color (celltext, g_value_get_boxed (value)); + break; + + case PROP_FOREGROUND_GDK: + /* This notifies the GObject itself. */ + set_fg_color (celltext, g_value_get_boxed (value)); + break; + + case PROP_FONT: + { + PangoFontDescription *font_desc = NULL; + const gchar *name; + + name = g_value_get_string (value); + + if (name) + font_desc = pango_font_description_from_string (name); + + set_font_description (celltext, font_desc); + + pango_font_description_free (font_desc); + + if (celltext->fixed_height_rows != -1) + celltext->calc_fixed_height = TRUE; + } + break; + + case PROP_FONT_DESC: + set_font_description (celltext, g_value_get_boxed (value)); + + if (celltext->fixed_height_rows != -1) + celltext->calc_fixed_height = TRUE; + break; + + case PROP_FAMILY: + case PROP_STYLE: + case PROP_VARIANT: + case PROP_WEIGHT: + case PROP_STRETCH: + case PROP_SIZE: + case PROP_SIZE_POINTS: + { + PangoFontMask old_set_mask = pango_font_description_get_set_fields (celltext->font); + + switch (param_id) + { + case PROP_FAMILY: + pango_font_description_set_family (celltext->font, + g_value_get_string (value)); + break; + case PROP_STYLE: + pango_font_description_set_style (celltext->font, + g_value_get_enum (value)); + break; + case PROP_VARIANT: + pango_font_description_set_variant (celltext->font, + g_value_get_enum (value)); + break; + case PROP_WEIGHT: + pango_font_description_set_weight (celltext->font, + g_value_get_int (value)); + break; + case PROP_STRETCH: + pango_font_description_set_stretch (celltext->font, + g_value_get_enum (value)); + break; + case PROP_SIZE: + pango_font_description_set_size (celltext->font, + g_value_get_int (value)); + g_object_notify (object, "size-points"); + break; + case PROP_SIZE_POINTS: + pango_font_description_set_size (celltext->font, + g_value_get_double (value) * PANGO_SCALE); + g_object_notify (object, "size"); + break; + } + + if (celltext->fixed_height_rows != -1) + celltext->calc_fixed_height = TRUE; + + notify_set_changed (object, old_set_mask & pango_font_description_get_set_fields (celltext->font)); + g_object_notify (object, "font-desc"); + g_object_notify (object, "font"); + + break; + } + + case PROP_SCALE: + celltext->font_scale = g_value_get_double (value); + celltext->scale_set = TRUE; + if (celltext->fixed_height_rows != -1) + celltext->calc_fixed_height = TRUE; + g_object_notify (object, "scale-set"); + break; + + case PROP_EDITABLE: + celltext->editable = g_value_get_boolean (value); + celltext->editable_set = TRUE; + if (celltext->editable) + GTK_CELL_RENDERER (celltext)->mode = GTK_CELL_RENDERER_MODE_EDITABLE; + else + GTK_CELL_RENDERER (celltext)->mode = GTK_CELL_RENDERER_MODE_INERT; + g_object_notify (object, "editable-set"); + break; + + case PROP_STRIKETHROUGH: + celltext->strikethrough = g_value_get_boolean (value); + celltext->strikethrough_set = TRUE; + g_object_notify (object, "strikethrough-set"); + break; + + case PROP_UNDERLINE: + celltext->underline_style = g_value_get_enum (value); + celltext->underline_set = TRUE; + g_object_notify (object, "underline-set"); + + break; + + case PROP_RISE: + celltext->rise = g_value_get_int (value); + celltext->rise_set = TRUE; + g_object_notify (object, "rise-set"); + if (celltext->fixed_height_rows != -1) + celltext->calc_fixed_height = TRUE; + break; + + case PROP_LANGUAGE: + priv->language_set = TRUE; + if (priv->language) + g_object_unref (priv->language); + priv->language = pango_language_from_string (g_value_get_string (value)); + g_object_notify (object, "language-set"); + break; + + case PROP_ELLIPSIZE: + priv->ellipsize = g_value_get_enum (value); + priv->ellipsize_set = TRUE; + g_object_notify (object, "ellipsize-set"); + break; + + case PROP_WRAP_MODE: + priv->wrap_mode = g_value_get_enum (value); + break; + + case PROP_WRAP_WIDTH: + priv->wrap_width = g_value_get_int (value); + break; + + case PROP_WIDTH_CHARS: + priv->width_chars = g_value_get_int (value); + break; + + case PROP_ALIGN: + priv->align = g_value_get_enum (value); + priv->align_set = TRUE; + g_object_notify (object, "align-set"); + break; + + case PROP_BACKGROUND_SET: + celltext->background_set = g_value_get_boolean (value); + break; + + case PROP_FOREGROUND_SET: + celltext->foreground_set = g_value_get_boolean (value); + break; + + case PROP_FAMILY_SET: + case PROP_STYLE_SET: + case PROP_VARIANT_SET: + case PROP_WEIGHT_SET: + case PROP_STRETCH_SET: + case PROP_SIZE_SET: + if (!g_value_get_boolean (value)) + { + pango_font_description_unset_fields (celltext->font, + get_property_font_set_mask (param_id)); + } + else + { + PangoFontMask changed_mask; + + changed_mask = set_font_desc_fields (celltext->font, + get_property_font_set_mask (param_id)); + notify_fields_changed (G_OBJECT (celltext), changed_mask); + } + break; + + case PROP_SCALE_SET: + celltext->scale_set = g_value_get_boolean (value); + break; + + case PROP_EDITABLE_SET: + celltext->editable_set = g_value_get_boolean (value); + break; + + case PROP_STRIKETHROUGH_SET: + celltext->strikethrough_set = g_value_get_boolean (value); + break; + + case PROP_UNDERLINE_SET: + celltext->underline_set = g_value_get_boolean (value); + break; + + case PROP_RISE_SET: + celltext->rise_set = g_value_get_boolean (value); + break; + + case PROP_LANGUAGE_SET: + priv->language_set = g_value_get_boolean (value); + break; + + case PROP_ELLIPSIZE_SET: + priv->ellipsize_set = g_value_get_boolean (value); + break; + + case PROP_ALIGN_SET: + priv->align_set = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +/** + * ghb_cell_renderer_text_new: + * + * Creates a new #GhbCellRendererText. Adjust how text is drawn using + * object properties. Object properties can be + * set globally (with g_object_set()). Also, with #GtkTreeViewColumn, + * you can bind a property to a value in a #GtkTreeModel. For example, + * you can bind the "text" property on the cell renderer to a string + * value in the model, thus rendering a different string in each row + * of the #GtkTreeView + * + * Return value: the new cell renderer + **/ +GtkCellRenderer * +ghb_cell_renderer_text_new (void) +{ + return g_object_new (GHB_TYPE_CELL_RENDERER_TEXT, NULL); +} + +static void +add_attr (PangoAttrList *attr_list, + PangoAttribute *attr) +{ + attr->start_index = 0; + attr->end_index = G_MAXINT; + + pango_attr_list_insert (attr_list, attr); +} + +static PangoLayout* +get_layout (GhbCellRendererText *celltext, + GtkWidget *widget, + gboolean will_render, + GtkCellRendererState flags) +{ + PangoAttrList *attr_list; + PangoLayout *layout; + PangoUnderline uline; + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (celltext); + + layout = gtk_widget_create_pango_layout (widget, celltext->text); + + if (celltext->extra_attrs) + attr_list = pango_attr_list_copy (celltext->extra_attrs); + else + attr_list = pango_attr_list_new (); + + pango_layout_set_single_paragraph_mode (layout, priv->single_paragraph); + + if (will_render) + { + /* Add options that affect appearance but not size */ + + /* note that background doesn't go here, since it affects + * background_area not the PangoLayout area + */ + + if (celltext->foreground_set + && (flags & GTK_CELL_RENDERER_SELECTED) == 0) + { + PangoColor color; + + color = celltext->foreground; + + add_attr (attr_list, + pango_attr_foreground_new (color.red, color.green, color.blue)); + } + + if (celltext->strikethrough_set) + add_attr (attr_list, + pango_attr_strikethrough_new (celltext->strikethrough)); + } + + add_attr (attr_list, pango_attr_font_desc_new (celltext->font)); + + if (celltext->scale_set && + celltext->font_scale != 1.0) + add_attr (attr_list, pango_attr_scale_new (celltext->font_scale)); + + if (celltext->underline_set) + uline = celltext->underline_style; + else + uline = PANGO_UNDERLINE_NONE; + + if (priv->language_set) + add_attr (attr_list, pango_attr_language_new (priv->language)); + + if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT) + { + switch (uline) + { + case PANGO_UNDERLINE_NONE: + uline = PANGO_UNDERLINE_SINGLE; + break; + + case PANGO_UNDERLINE_SINGLE: + uline = PANGO_UNDERLINE_DOUBLE; + break; + + default: + break; + } + } + + if (uline != PANGO_UNDERLINE_NONE) + add_attr (attr_list, pango_attr_underline_new (celltext->underline_style)); + + if (celltext->rise_set) + add_attr (attr_list, pango_attr_rise_new (celltext->rise)); + + if (priv->ellipsize_set) + pango_layout_set_ellipsize (layout, priv->ellipsize); + else + pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE); + + if (priv->wrap_width != -1) + { + pango_layout_set_width (layout, priv->wrap_width * PANGO_SCALE); + pango_layout_set_wrap (layout, priv->wrap_mode); + } + else + { + pango_layout_set_width (layout, -1); + pango_layout_set_wrap (layout, PANGO_WRAP_CHAR); + } + + if (priv->align_set) + pango_layout_set_alignment (layout, priv->align); + else + { + PangoAlignment align; + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + align = PANGO_ALIGN_RIGHT; + else + align = PANGO_ALIGN_LEFT; + + pango_layout_set_alignment (layout, align); + } + + pango_layout_set_attributes (layout, attr_list); + + pango_attr_list_unref (attr_list); + + return layout; +} + +static void +get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + PangoLayout *layout, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + GhbCellRendererText *celltext = (GhbCellRendererText *) cell; + PangoRectangle rect; + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (cell); + + if (celltext->calc_fixed_height) + { + PangoContext *context; + PangoFontMetrics *metrics; + PangoFontDescription *font_desc; + gint row_height; + + font_desc = pango_font_description_copy_static (widget->style->font_desc); + pango_font_description_merge_static (font_desc, celltext->font, TRUE); + + if (celltext->scale_set) + pango_font_description_set_size (font_desc, + celltext->font_scale * pango_font_description_get_size (font_desc)); + + context = gtk_widget_get_pango_context (widget); + + metrics = pango_context_get_metrics (context, + font_desc, + pango_context_get_language (context)); + row_height = (pango_font_metrics_get_ascent (metrics) + + pango_font_metrics_get_descent (metrics)); + pango_font_metrics_unref (metrics); + + pango_font_description_free (font_desc); + + gtk_cell_renderer_set_fixed_size (cell, + cell->width, 2*cell->ypad + + celltext->fixed_height_rows * PANGO_PIXELS (row_height)); + + if (height) + { + *height = cell->height; + height = NULL; + } + celltext->calc_fixed_height = FALSE; + if (width == NULL) + return; + } + + if (layout) + g_object_ref (layout); + else + layout = get_layout (celltext, widget, FALSE, 0); + + pango_layout_get_extents (layout, NULL, &rect); + pango_extents_to_pixels (&rect, NULL); + + if (height) + *height = cell->ypad * 2 + rect.height; + + /* The minimum size for ellipsized labels is ~ 3 chars */ + if (width) + { + if (priv->ellipsize || priv->width_chars > 0) + { + PangoContext *context; + PangoFontMetrics *metrics; + gint char_width; + + context = pango_layout_get_context (layout); + metrics = pango_context_get_metrics (context, widget->style->font_desc, pango_context_get_language (context)); + + char_width = pango_font_metrics_get_approximate_char_width (metrics); + pango_font_metrics_unref (metrics); + + *width = cell->xpad * 2 + (PANGO_PIXELS (char_width) * MAX (priv->width_chars, 3)); + } + else + { + *width = cell->xpad * 2 + rect.x + rect.width; + } + } + + if (cell_area) + { + if (x_offset) + { + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + *x_offset = (1.0 - cell->xalign) * (cell_area->width - (rect.x + rect.width + (2 * cell->xpad))); + else + *x_offset = cell->xalign * (cell_area->width - (rect.x + rect.width + (2 * cell->xpad))); + + if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->wrap_width != -1) + *x_offset = MAX(*x_offset, 0); + } + if (y_offset) + { + *y_offset = cell->yalign * (cell_area->height - (rect.height + (2 * cell->ypad))); + *y_offset = MAX (*y_offset, 0); + } + } + else + { + if (x_offset) *x_offset = 0; + if (y_offset) *y_offset = 0; + } + + g_object_unref (layout); +} + + +static void +ghb_cell_renderer_text_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + get_size (cell, widget, cell_area, NULL, + x_offset, y_offset, width, height); +} + +static void +ghb_cell_renderer_text_render (GtkCellRenderer *cell, + GdkDrawable *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + GtkCellRendererState flags) + +{ + GhbCellRendererText *celltext = (GhbCellRendererText *) cell; + PangoLayout *layout; + GtkStateType state; + gint x_offset; + gint y_offset; + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (cell); + + layout = get_layout (celltext, widget, TRUE, flags); + get_size (cell, widget, cell_area, layout, &x_offset, &y_offset, NULL, NULL); + + if (!cell->sensitive) + { + state = GTK_STATE_INSENSITIVE; + } + else if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED) + { + if (GTK_WIDGET_HAS_FOCUS (widget)) + state = GTK_STATE_SELECTED; + else + state = GTK_STATE_ACTIVE; + } + else if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT && + GTK_WIDGET_STATE (widget) == GTK_STATE_PRELIGHT) + { + state = GTK_STATE_PRELIGHT; + } + else + { + if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) + state = GTK_STATE_INSENSITIVE; + else + state = GTK_STATE_NORMAL; + } + + if (celltext->background_set && + (flags & GTK_CELL_RENDERER_SELECTED) == 0) + { + cairo_t *cr = gdk_cairo_create (window); + + if (expose_area) + { + gdk_cairo_rectangle (cr, expose_area); + cairo_clip (cr); + } + + gdk_cairo_rectangle (cr, background_area); + cairo_set_source_rgb (cr, + celltext->background.red / 65535., + celltext->background.green / 65535., + celltext->background.blue / 65535.); + cairo_fill (cr); + + cairo_destroy (cr); + } + + if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) + pango_layout_set_width (layout, + (cell_area->width - x_offset - 2 * cell->xpad) * PANGO_SCALE); + else if (priv->wrap_width == -1) + pango_layout_set_width (layout, -1); + + gtk_paint_layout (widget->style, + window, + state, + TRUE, + expose_area, + widget, + "cellrenderertext", + cell_area->x + x_offset + cell->xpad, + cell_area->y + y_offset + cell->ypad, + layout); + + g_object_unref (layout); +} + +static gboolean +ghb_cell_renderer_text_keypress( + GtkCellEditable *entry, + GdkEventKey *event, + gpointer data) +{ + gboolean result; + g_signal_emit( + data, text_cell_renderer_signals[KEYPRESS], 0, event, &result); + return result; +} + +static void +ghb_cell_renderer_text_editing_done (GtkCellEditable *entry, + gpointer data) +{ + const gchar *path; + const gchar *new_text; + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); + + priv->entry = NULL; + + if (priv->focus_out_id > 0) + { + g_signal_handler_disconnect (entry, priv->focus_out_id); + priv->focus_out_id = 0; + } + + if (priv->populate_popup_id > 0) + { + g_signal_handler_disconnect (entry, priv->populate_popup_id); + priv->populate_popup_id = 0; + } + + if (priv->entry_menu_popdown_timeout) + { + g_source_remove (priv->entry_menu_popdown_timeout); + priv->entry_menu_popdown_timeout = 0; + } + + gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), + GTK_ENTRY (entry)->editing_canceled); + if (GTK_ENTRY (entry)->editing_canceled) + return; + + path = g_object_get_data (G_OBJECT (entry), GHB_CELL_RENDERER_TEXT_PATH); + new_text = gtk_entry_get_text (GTK_ENTRY (entry)); + + g_signal_emit (data, text_cell_renderer_signals[EDITED], 0, path, new_text); +} + +static gboolean +popdown_timeout (gpointer data) +{ + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); + + priv->entry_menu_popdown_timeout = 0; + + if (!GTK_WIDGET_HAS_FOCUS (priv->entry)) + ghb_cell_renderer_text_editing_done (GTK_CELL_EDITABLE (priv->entry), data); + + return FALSE; +} + +static void +ghb_cell_renderer_text_popup_unmap (GtkMenu *menu, + gpointer data) +{ + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); + + priv->in_entry_menu = FALSE; + + if (priv->entry_menu_popdown_timeout) + return; + + priv->entry_menu_popdown_timeout = gdk_threads_add_timeout (500, popdown_timeout, + data); +} + +static void +ghb_cell_renderer_text_populate_popup (GtkEntry *entry, + GtkMenu *menu, + gpointer data) +{ + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); + + if (priv->entry_menu_popdown_timeout) + { + g_source_remove (priv->entry_menu_popdown_timeout); + priv->entry_menu_popdown_timeout = 0; + } + + priv->in_entry_menu = TRUE; + + g_signal_connect (menu, "unmap", + G_CALLBACK (ghb_cell_renderer_text_popup_unmap), data); +} + +static gboolean +ghb_cell_renderer_text_focus_out_event (GtkWidget *entry, + GdkEvent *event, + gpointer data) +{ + GhbCellRendererTextPrivate *priv; + + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); + + if (priv->in_entry_menu) + return FALSE; + + GTK_ENTRY (entry)->editing_canceled = TRUE; + gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry)); + gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry)); + + /* entry needs focus-out-event */ + return FALSE; +} + +static GtkCellEditable * +ghb_cell_renderer_text_start_editing (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GtkRequisition requisition; + GhbCellRendererText *celltext; + GhbCellRendererTextPrivate *priv; + + celltext = GHB_CELL_RENDERER_TEXT (cell); + priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (cell); + + /* If the cell isn't editable we return NULL. */ + if (celltext->editable == FALSE) + return NULL; + + priv->entry = g_object_new (GTK_TYPE_ENTRY, + "has-frame", FALSE, + "xalign", cell->xalign, + NULL); + + if (celltext->text) + gtk_entry_set_text (GTK_ENTRY (priv->entry), celltext->text); + g_object_set_data_full (G_OBJECT (priv->entry), I_(GHB_CELL_RENDERER_TEXT_PATH), g_strdup (path), g_free); + + gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1); + + gtk_widget_size_request (priv->entry, &requisition); + if (requisition.height < cell_area->height) + { + GtkBorder *style_border; + GtkBorder border; + + gtk_widget_style_get (priv->entry, + "inner-border", &style_border, + NULL); + + if (style_border) + { + border = *style_border; + g_boxed_free (GTK_TYPE_BORDER, style_border); + } + else + { + /* Since boxed style properties can't have default values ... */ + border.left = 2; + border.right = 2; + } + + border.top = (cell_area->height - requisition.height) / 2; + border.bottom = (cell_area->height - requisition.height) / 2; + gtk_entry_set_inner_border (GTK_ENTRY (priv->entry), &border); + } + + priv->in_entry_menu = FALSE; + if (priv->entry_menu_popdown_timeout) + { + g_source_remove (priv->entry_menu_popdown_timeout); + priv->entry_menu_popdown_timeout = 0; + } + + g_signal_connect (priv->entry, + "key-press-event", + G_CALLBACK (ghb_cell_renderer_text_keypress), + celltext); + g_signal_connect (priv->entry, + "editing_done", + G_CALLBACK (ghb_cell_renderer_text_editing_done), + celltext); + priv->focus_out_id = g_signal_connect_after (priv->entry, "focus_out_event", + G_CALLBACK (ghb_cell_renderer_text_focus_out_event), + celltext); + priv->populate_popup_id = + g_signal_connect (priv->entry, "populate_popup", + G_CALLBACK (ghb_cell_renderer_text_populate_popup), + celltext); + + gtk_widget_show (priv->entry); + + return GTK_CELL_EDITABLE (priv->entry); +} + +/** + * ghb_cell_renderer_text_set_fixed_height_from_font: + * @renderer: A #GhbCellRendererText + * @number_of_rows: Number of rows of text each cell renderer is allocated, or -1 + * + * Sets the height of a renderer to explicitly be determined by the "font" and + * "y_pad" property set on it. Further changes in these properties do not + * affect the height, so they must be accompanied by a subsequent call to this + * function. Using this function is unflexible, and should really only be used + * if calculating the size of a cell is too slow (ie, a massive number of cells + * displayed). If @number_of_rows is -1, then the fixed height is unset, and + * the height is determined by the properties again. + **/ +void +ghb_cell_renderer_text_set_fixed_height_from_font (GhbCellRendererText *renderer, + gint number_of_rows) +{ + g_return_if_fail (GHB_IS_CELL_RENDERER_TEXT (renderer)); + g_return_if_fail (number_of_rows == -1 || number_of_rows > 0); + + if (number_of_rows == -1) + { + gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (renderer), + GTK_CELL_RENDERER (renderer)->width, + -1); + } + else + { + renderer->fixed_height_rows = number_of_rows; + renderer->calc_fixed_height = TRUE; + } +} + diff --git a/gtk/src/ghbcellrenderertext.h b/gtk/src/ghbcellrenderertext.h new file mode 100644 index 000000000..0e12ba17a --- /dev/null +++ b/gtk/src/ghbcellrenderertext.h @@ -0,0 +1,105 @@ +/* gtkcellrenderertext.h + * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GHB_CELL_RENDERER_TEXT_H__ +#define __GHB_CELL_RENDERER_TEXT_H__ + +#include <pango/pango.h> +#include <gtk/gtkcellrenderer.h> + + +G_BEGIN_DECLS + + +#define GHB_TYPE_CELL_RENDERER_TEXT (ghb_cell_renderer_text_get_type ()) +#define GHB_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererText)) +#define GHB_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererTextClass)) +#define GHB_IS_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHB_TYPE_CELL_RENDERER_TEXT)) +#define GHB_IS_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHB_TYPE_CELL_RENDERER_TEXT)) +#define GHB_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererTextClass)) + +typedef struct _GhbCellRendererText GhbCellRendererText; +typedef struct _GhbCellRendererTextClass GhbCellRendererTextClass; + +struct _GhbCellRendererText +{ + GtkCellRenderer parent; + + /*< private >*/ + gchar *text; + PangoFontDescription *font; + gdouble font_scale; + PangoColor foreground; + PangoColor background; + + PangoAttrList *extra_attrs; + + PangoUnderline underline_style; + + gint rise; + gint fixed_height_rows; + + guint strikethrough : 1; + + guint editable : 1; + + guint scale_set : 1; + + guint foreground_set : 1; + guint background_set : 1; + + guint underline_set : 1; + + guint rise_set : 1; + + guint strikethrough_set : 1; + + guint editable_set : 1; + guint calc_fixed_height : 1; +}; + +struct _GhbCellRendererTextClass +{ + GtkCellRendererClass parent_class; + + void (* edited) (GhbCellRendererText *cell_renderer_text, + const gchar *path, + const gchar *new_text); + + gboolean (* keypress) (GhbCellRendererText *cell_renderer_text, + GdkEventKey *event); + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +GType ghb_cell_renderer_text_get_type (void) G_GNUC_CONST; +GtkCellRenderer *ghb_cell_renderer_text_new (void); + +void ghb_cell_renderer_text_set_fixed_height_from_font (GhbCellRendererText *renderer, + gint number_of_rows); + + +G_END_DECLS + + +#endif /* __GHB_CELL_RENDERER_TEXT_H__ */ diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index 7edeb4ca5..eb60ad95a 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -46,7 +46,6 @@ typedef struct { gint count; options_map_t *map; - gint *grey_options; } combo_opts_t; static options_map_t d_container_opts[] = @@ -445,21 +444,120 @@ ghb_vquality_range(signal_user_data_t *ud, gint *min, gint *max) } gint +ghb_lookup_vrate(const GValue *vrate) +{ + gint ii; + gchar *str; + gint result = 0; + + str = ghb_value_string(vrate); + for (ii = 0; ii < hb_video_rates_count; ii++) + { + if (strcmp(hb_video_rates[ii].string, str) == 0) + { + result = hb_video_rates[ii].rate; + } + } + g_free(str); + // Default to "same as source" + return result; +} + +gint +ghb_lookup_vcodec(const GValue *vcodec) +{ + gint ii; + gchar *str; + gint result = HB_VCODEC_FFMPEG; + + str = ghb_value_string(vcodec); + for (ii = 0; ii < vcodec_opts.count; ii++) + { + if (strcmp(vcodec_opts.map[ii].shortOpt, str) == 0) + { + result = vcodec_opts.map[ii].ivalue; + } + } + g_free(str); + return result; +} + +gint +ghb_lookup_denoise(const GValue *denoise) +{ + gint ii; + gchar *str; + gint result = -1; + + str = ghb_value_string(denoise); + for (ii = 0; ii < denoise_opts.count; ii++) + { + if (strcmp(denoise_opts.map[ii].shortOpt, str) == 0) + { + result = denoise_opts.map[ii].ivalue; + } + } + g_free(str); + // Custom + return result; +} + +gint +ghb_lookup_deint(const GValue *deint) +{ + gint ii; + gchar *str; + gint result = -1; + + str = ghb_value_string(deint); + for (ii = 0; ii < deint_opts.count; ii++) + { + if (strcmp(deint_opts.map[ii].shortOpt, str) == 0) + { + result = deint_opts.map[ii].ivalue; + } + } + g_free(str); + // Custom + return result; +} + +gint +ghb_lookup_mux(const GValue *mux) +{ + gint ii; + gchar *str; + gint result = HB_MUX_MKV; + + str = ghb_value_string(mux); + for (ii = 0; ii < container_opts.count; ii++) + { + if (strcmp(container_opts.map[ii].shortOpt, str) == 0) + { + result = container_opts.map[ii].ivalue; + } + } + g_free(str); + return result; +} + +gint ghb_lookup_acodec(const GValue *acodec) { gint ii; gchar *str; + gint result = HB_ACODEC_FAAC; str = ghb_value_string(acodec); for (ii = 0; ii < acodec_opts.count; ii++) { if (strcmp(acodec_opts.map[ii].shortOpt, str) == 0) { - return acodec_opts.map[ii].ivalue; + result = acodec_opts.map[ii].ivalue; } } g_free(str); - return HB_ACODEC_FAAC; + return result; } gint @@ -467,17 +565,19 @@ ghb_lookup_mix(const GValue *mix) { gint ii; gchar *str; + gint result = HB_AMIXDOWN_DOLBYPLII; + str = ghb_value_string(mix); for (ii = 0; ii < hb_audio_mixdowns_count; ii++) { if (strcmp(hb_audio_mixdowns[ii].short_name, str) == 0) { - return hb_audio_mixdowns[ii].amixdown; + result = hb_audio_mixdowns[ii].amixdown; } } g_free(str); - return HB_AMIXDOWN_DOLBYPLII; + return result; } #if 0 @@ -569,7 +669,8 @@ get_amix_value(gint val) } // Handle for libhb. Gets set by ghb_backend_init() -static hb_handle_t * h = NULL; +static hb_handle_t * h_scan = NULL; +static hb_handle_t * h_queue = NULL; extern void hb_get_tempory_directory(hb_handle_t *h, char path[512]); @@ -578,7 +679,7 @@ ghb_hb_cleanup(gboolean partial) { char dir[512]; - hb_get_tempory_directory(h, dir); + hb_get_tempory_directory(h_scan, dir); del_tree(dir, !partial); } @@ -589,8 +690,8 @@ get_hb_audio(gint titleindex, gint track) hb_title_t * title; hb_audio_config_t *audio = NULL; - if (h == NULL) return NULL; - list = hb_get_titles( h ); + if (h_scan == NULL) return NULL; + list = hb_get_titles( h_scan ); if( !hb_list_count( list ) ) { /* No valid title, stop right there */ @@ -1066,9 +1167,9 @@ title_opts_set(GtkBuilder *builder, const gchar *name) g_debug("title_opts_set ()\n"); store = get_combo_box_store(builder, name); gtk_list_store_clear(store); - if (h != NULL) + if (h_scan != NULL) { - list = hb_get_titles( h ); + list = hb_get_titles( h_scan ); count = hb_list_count( list ); if (count > 100) count = 100; } @@ -1170,9 +1271,9 @@ audio_track_opts_set(GtkBuilder *builder, const gchar *name, gint titleindex) g_debug("audio_track_opts_set ()\n"); store = get_combo_box_store(builder, name); gtk_list_store_clear(store); - if (h != NULL) + if (h_scan != NULL) { - list = hb_get_titles( h ); + list = hb_get_titles( h_scan ); title = (hb_title_t*)hb_list_item( list, titleindex ); if (title != NULL) { @@ -1221,9 +1322,9 @@ subtitle_opts_set(GtkBuilder *builder, const gchar *name, gint titleindex) g_debug("subtitle_opts_set ()\n"); store = get_combo_box_store(builder, name); gtk_list_store_clear(store); - if (h != NULL) + if (h_scan != NULL) { - list = hb_get_titles( h ); + list = hb_get_titles( h_scan ); title = (hb_title_t*)hb_list_item( list, titleindex ); if (title != NULL) { @@ -1287,8 +1388,8 @@ ghb_longest_title() gint titleindex = 0; g_debug("ghb_longest_title ()\n"); - if (h == NULL) return 0; - list = hb_get_titles( h ); + if (h_scan == NULL) return 0; + list = hb_get_titles( h_scan ); count = hb_list_count( list ); if (count > 100) count = 100; for (ii = 0; ii < count; ii++) @@ -1315,9 +1416,9 @@ ghb_find_audio_track(gint titleindex, const gchar *lang, gint index) gint match = 0; g_debug("find_audio_track ()\n"); - if (h != NULL) + if (h_scan != NULL) { - list = hb_get_titles( h ); + list = hb_get_titles( h_scan ); title = (hb_title_t*)hb_list_item( list, titleindex ); if (title != NULL) { @@ -1495,8 +1596,8 @@ ghb_get_chapters(gint titleindex) GValue *chapters = NULL; g_debug("ghb_get_chapters (title = %d)\n", titleindex); - if (h == NULL) return NULL; - list = hb_get_titles( h ); + if (h_scan == NULL) return NULL; + list = hb_get_titles( h_scan ); title = (hb_title_t*)hb_list_item( list, titleindex ); if (title == NULL) return NULL; count = hb_list_count( title->list_chapter ); @@ -1561,7 +1662,8 @@ void ghb_backend_init(GtkBuilder *builder, gint debug, gint update) { /* Init libhb */ - h = hb_init( debug, update ); + h_scan = hb_init( debug, update ); + h_queue = hb_init( debug, 0 ); // Set up the list model for the combos init_ui_combo_boxes(builder); // Populate all the combos @@ -1571,7 +1673,7 @@ ghb_backend_init(GtkBuilder *builder, gint debug, gint update) void ghb_backend_scan(const gchar *path, gint titleindex) { - hb_scan( h, path, titleindex ); + hb_scan( h_scan, path, titleindex ); hb_status.state |= GHB_STATE_SCANNING; // initialize count and cur to something that won't cause FPE // when computing progress @@ -1579,12 +1681,26 @@ ghb_backend_scan(const gchar *path, gint titleindex) hb_status.title_cur = 0; } +void +ghb_backend_queue_scan(const gchar *path, gint titleindex) +{ + g_debug("ghb_backend_queue_scan()"); + hb_scan( h_queue, path, titleindex ); + hb_status.queue_state |= GHB_STATE_SCANNING; +} + gint ghb_get_state() { return hb_status.state; } +gint +ghb_get_queue_state() +{ + return hb_status.queue_state; +} + void ghb_clear_state(gint state) { @@ -1592,12 +1708,24 @@ ghb_clear_state(gint state) } void +ghb_clear_queue_state(gint state) +{ + hb_status.queue_state &= ~state; +} + +void ghb_set_state(gint state) { hb_status.state |= state; } void +ghb_set_queue_state(gint state) +{ + hb_status.queue_state |= state; +} + +void ghb_get_status(ghb_status_t *status) { memcpy(status, &hb_status, sizeof(ghb_status_t)); @@ -1607,24 +1735,12 @@ void ghb_track_status() { hb_state_t s; - static gint scan_complete_count = 0; - gint scans; + hb_state_t s_queue; - if (h == NULL) return; - hb_get_state( h, &s ); - scans = hb_get_scancount(h); - if (scans > scan_complete_count) - { - hb_status.state &= ~GHB_STATE_SCANNING; - hb_status.state |= GHB_STATE_SCANDONE; - scan_complete_count = hb_get_scancount(h); - } + if (h_scan == NULL) return; + hb_get_state( h_scan, &s ); switch( s.state ) { - case HB_STATE_IDLE: - /* Nothing to do */ - break; - #define p s.param.scanning case HB_STATE_SCANNING: { @@ -1640,10 +1756,25 @@ ghb_track_status() hb_status.state |= GHB_STATE_SCANDONE; } break; -#define p s.param.working + } + hb_get_state( h_queue, &s_queue ); + switch( s_queue.state ) + { + case HB_STATE_SCANNING: + { + hb_status.queue_state |= GHB_STATE_SCANNING; + } break; + + case HB_STATE_SCANDONE: + { + hb_status.queue_state &= ~GHB_STATE_SCANNING; + hb_status.queue_state |= GHB_STATE_SCANDONE; + } break; + +#define p s_queue.param.working case HB_STATE_WORKING: - hb_status.state |= GHB_STATE_WORKING; - hb_status.state &= ~GHB_STATE_PAUSED; + hb_status.queue_state |= GHB_STATE_WORKING; + hb_status.queue_state &= ~GHB_STATE_PAUSED; hb_status.job_cur = p.job_cur; hb_status.job_count = p.job_count; hb_status.progress = p.progress; @@ -1657,25 +1788,23 @@ ghb_track_status() #undef p case HB_STATE_PAUSED: - hb_status.state |= GHB_STATE_PAUSED; + hb_status.queue_state |= GHB_STATE_PAUSED; break; -#define p s.param.muxing case HB_STATE_MUXING: { - hb_status.state |= GHB_STATE_MUXING; + hb_status.queue_state |= GHB_STATE_MUXING; } break; -#undef p -#define p s.param.workdone +#define p s_queue.param.workdone case HB_STATE_WORKDONE: { hb_job_t *job; - hb_status.state |= GHB_STATE_WORKDONE; - hb_status.state &= ~GHB_STATE_MUXING; - hb_status.state &= ~GHB_STATE_PAUSED; - hb_status.state &= ~GHB_STATE_WORKING; + hb_status.queue_state |= GHB_STATE_WORKDONE; + hb_status.queue_state &= ~GHB_STATE_MUXING; + hb_status.queue_state &= ~GHB_STATE_PAUSED; + hb_status.queue_state &= ~GHB_STATE_WORKING; switch (p.error) { case HB_ERROR_NONE: @@ -1691,8 +1820,8 @@ ghb_track_status() // When a job is stopped, libhb removes it from the job list, // but does not remove other jobs that may be associated with it. // Associated jobs are taged in the sequence id. - while (((job = hb_job(h, 0)) != NULL) && ((job->sequence_id >> 24) != 0) ) - hb_rem( h, job ); + while ((job = hb_job(h_queue, 0)) != NULL) + hb_rem( h_queue, job ); } break; #undef p } @@ -1704,8 +1833,8 @@ ghb_get_title_info(ghb_title_info_t *tinfo, gint titleindex) hb_list_t * list; hb_title_t * title; - if (h == NULL) return FALSE; - list = hb_get_titles( h ); + if (h_scan == NULL) return FALSE; + list = hb_get_titles( h_scan ); if( !hb_list_count( list ) ) { /* No valid title, stop right there */ @@ -1784,8 +1913,8 @@ ghb_set_scale(signal_user_data_t *ud, gint mode) g_debug("ghb_set_scale ()\n"); - if (h == NULL) return; - list = hb_get_titles( h ); + if (h_scan == NULL) return; + list = hb_get_titles( h_scan ); if( !hb_list_count( list ) ) { /* No valid title, stop right there */ @@ -2015,8 +2144,8 @@ ghb_calculate_target_bitrate(GValue *settings, gint titleindex) hb_job_t * job; gint size; - if (h == NULL) return 2000; - list = hb_get_titles( h ); + if (h_scan == NULL) return 2000; + list = hb_get_titles( h_scan ); title = hb_list_item( list, titleindex ); if (title == NULL) return 2000; job = title->job; @@ -2070,26 +2199,28 @@ gboolean ghb_validate_filter_string(const gchar *str, gint max_fields) { gint fields = 0; - gboolean in_field = FALSE; + gchar *end; + gdouble val; + if (str == NULL || *str == 0) return TRUE; while (*str) { - if (*str >= '0' && *str <= '9') - { - if (!in_field) - { - fields++; - // negative max_fields means infinate - if (max_fields >= 0 && fields > max_fields) return FALSE; - in_field = TRUE; - } + val = g_strtod(str, &end); + if (str != end) + { // Found a numeric value + fields++; + // negative max_fields means infinate + if (max_fields >= 0 && fields > max_fields) return FALSE; + if (*end == 0) + return TRUE; + if (*end != ':') + return FALSE; + str = end + 1; } - else if (!in_field) return FALSE; - else if (*str != ':') return FALSE; - else in_field = FALSE; - str++; + else + return FALSE; } - return TRUE; + return FALSE; } gboolean @@ -2136,7 +2267,7 @@ ghb_validate_filters(signal_user_data_t *ud) index = ghb_settings_get_combo_index(ud->settings, "tweak_deinterlace"); if (index < 0) { - str = ghb_settings_get_combo_string(ud->settings, "tweak_deinterlace"); + str = ghb_settings_get_string(ud->settings, "tweak_deinterlace"); if (!ghb_validate_filter_string(str, 4)) { message = g_strdup_printf( @@ -2167,7 +2298,7 @@ ghb_validate_filters(signal_user_data_t *ud) index = ghb_settings_get_combo_index(ud->settings, "tweak_denoise"); if (index < 0) { - str = ghb_settings_get_combo_string(ud->settings, "tweak_denoise"); + str = ghb_settings_get_string(ud->settings, "tweak_denoise"); if (!ghb_validate_filter_string(str, 4)) { message = g_strdup_printf( @@ -2284,8 +2415,8 @@ ghb_validate_audio(signal_user_data_t *ud) gchar *message; GValue *value; - if (h == NULL) return FALSE; - list = hb_get_titles( h ); + if (h_scan == NULL) return FALSE; + list = hb_get_titles( h_scan ); if( !hb_list_count( list ) ) { /* No valid title, stop right there */ @@ -2542,8 +2673,8 @@ ghb_add_job(GValue *js, gint unique_id) gchar *dest_str = NULL; g_debug("ghb_add_job()\n"); - if (h == NULL) return; - list = hb_get_titles( h ); + if (h_queue == NULL) return; + list = hb_get_titles( h_queue ); if( !hb_list_count( list ) ) { /* No valid title, stop right there */ @@ -2551,7 +2682,10 @@ ghb_add_job(GValue *js, gint unique_id) return; } - gint titleindex = ghb_settings_get_int(js, "title"); + // Since I'm doing a scan of the single title I want just prior + // to adding the job, there is only the one title to choose from. + //gint titleindex = ghb_settings_get_int(js, "title"); + gint titleindex = 0; title = hb_list_item( list, titleindex ); if (title == NULL) return; @@ -2560,7 +2694,7 @@ ghb_add_job(GValue *js, gint unique_id) if (job == NULL) return; tweaks = ghb_settings_get_int(js, "allow_tweaks"); - job->mux = ghb_settings_get_int(js, "container"); + job->mux = ghb_lookup_mux(ghb_settings_get_value(js, "container")); if (job->mux == HB_MUX_MP4) { job->largeFileSize = ghb_settings_get_boolean(js, "large_mp4"); @@ -2616,10 +2750,10 @@ ghb_add_job(GValue *js, gint unique_id) gboolean decomb = ghb_settings_get_boolean(js, "decomb"); - gint deint = ghb_settings_get_int( - js, tweaks ? "tweak_deinterlace":"deinterlace"); + gint deint = ghb_lookup_deint( + ghb_settings_get_value(js, tweaks ? "tweak_deinterlace":"deinterlace")); if (!decomb) - job->deinterlace = (deint == 0) ? 0 : 1; + job->deinterlace = (deint != 0) ? 1 : 0; else job->deinterlace = 0; job->grayscale = ghb_settings_get_boolean(js, "grayscale"); @@ -2677,7 +2811,10 @@ ghb_add_job(GValue *js, gint unique_id) } if( job->deinterlace ) { - deint_str = ghb_settings_get_combo_string(js, + if (deint > 0) + deint_str = g_strdup(deint_opts.map[deint].svalue); + else + deint_str = ghb_settings_get_string(js, tweaks ? "tweak_deinterlace" : "deinterlace"); hb_filter_deinterlace.settings = deint_str; hb_list_add( job->filters, &hb_filter_deinterlace ); @@ -2695,19 +2832,22 @@ ghb_add_job(GValue *js, gint unique_id) } hb_list_add( job->filters, &hb_filter_deblock ); } - gint denoise = ghb_settings_get_int( - js, tweaks ? "tweak_denoise" : "denoise"); + gint denoise = ghb_lookup_denoise( + ghb_settings_get_value(js, tweaks ? "tweak_denoise" : "denoise")); if( denoise != 0 ) { - denoise_str = (gchar*)ghb_settings_get_combo_string( - js, tweaks ? "tweak_denoise" : "denoise"); + if (denoise > 0) + denoise_str = g_strdup(denoise_opts.map[denoise].svalue); + else + denoise_str = (gchar*)ghb_settings_get_string( + js, tweaks ? "tweak_denoise" : "denoise"); hb_filter_denoise.settings = denoise_str; - hb_list_add( job->filters, &hb_filter_deblock ); + hb_list_add( job->filters, &hb_filter_denoise ); } job->width = ghb_settings_get_int(js, "scale_width"); job->height = ghb_settings_get_int(js, "scale_height"); - job->vcodec = ghb_settings_get_int(js, "video_codec"); + job->vcodec = ghb_lookup_vcodec(ghb_settings_get_value(js, "video_codec")); if ((job->mux == HB_MUX_MP4 || job->mux == HB_MUX_AVI) && (job->vcodec == HB_VCODEC_THEORA)) { @@ -2762,7 +2902,7 @@ ghb_add_job(GValue *js, gint unique_id) job->vfr = FALSE; } - gint vrate = ghb_settings_get_int(js, "framerate"); + gint vrate = ghb_lookup_vrate(ghb_settings_get_value(js, "framerate")); if( vrate == 0 || job->vfr ) { job->vrate = title->rate; @@ -2801,7 +2941,8 @@ ghb_add_job(GValue *js, gint unique_id) asettings = ghb_array_get_nth(audio_list, ii); audio.in.track = ghb_settings_get_int(asettings, "audio_track"); audio.out.track = tcount; - audio.out.codec = ghb_settings_get_int(asettings, "audio_codec"); + audio.out.codec = ghb_lookup_acodec( + ghb_settings_get_value(asettings, "audio_codec")); taudio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, audio.in.track ); if ((taudio->in.codec != HB_ACODEC_AC3) && (audio.out.codec == HB_ACODEC_AC3)) { @@ -2845,11 +2986,13 @@ ghb_add_job(GValue *js, gint unique_id) } else { - audio.out.mixdown = ghb_settings_get_int (asettings, "audio_mix"); + audio.out.mixdown = ghb_lookup_mix( + ghb_settings_get_value (asettings, "audio_mix")); // Make sure the mixdown is valid and pick a new one if not. - audio.out.mixdown = ghb_get_best_mix(titleindex, audio.in.track, audio.out.codec, - audio.out.mixdown); - audio.out.bitrate = ghb_settings_get_int(asettings, "audio_bitrate") / 1000; + audio.out.mixdown = ghb_get_best_mix(titleindex, + audio.in.track, audio.out.codec, audio.out.mixdown); + audio.out.bitrate = + ghb_settings_get_int(asettings, "audio_bitrate") / 1000; gint srate = ghb_settings_get_int(asettings, "audio_rate"); if (srate == 0) // 0 is same as source audio.out.samplerate = taudio->in.samplerate; @@ -2892,6 +3035,28 @@ ghb_add_job(GValue *js, gint unique_id) job->x264opts = NULL; } gint subtitle = ghb_settings_get_int(js, "subtitle_lang"); + gchar *slang = ghb_settings_get_string(js, "subtitle_lang"); + subtitle = -2; // default to none + if (strcmp(slang, "auto") == 0) + { + subtitle = -1; + } + else + { + gint scount; + hb_subtitle_t * subt; + + scount = hb_list_count(title->list_subtitle); + for (ii = 0; ii < scount; ii++) + { + subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, ii); + if (strcmp(slang, subt->iso639_2) == 0) + { + subtitle = ii; + break; + } + } + } gboolean forced_subtitles = ghb_settings_get_boolean(js, "forced_subtitles"); job->subtitle_force = forced_subtitles; if (subtitle >= 0) @@ -2920,7 +3085,7 @@ ghb_add_job(GValue *js, gint unique_id) * Add the pre-scan job */ job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); - hb_add( h, job ); + hb_add( h_queue, job ); //if (job->x264opts != NULL) // g_free(job->x264opts); @@ -2973,7 +3138,7 @@ ghb_add_job(GValue *js, gint unique_id) job->x264opts = x264opts; } job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); - hb_add( h, job ); + hb_add( h_queue, job ); //if (job->x264opts != NULL) // g_free(job->x264opts); @@ -2988,7 +3153,7 @@ ghb_add_job(GValue *js, gint unique_id) job->indepth_scan = 0; job->x264opts = x264opts2; job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); - hb_add( h, job ); + hb_add( h_queue, job ); //if (job->x264opts != NULL) // g_free(job->x264opts); } @@ -2997,7 +3162,7 @@ ghb_add_job(GValue *js, gint unique_id) job->indepth_scan = 0; job->pass = 0; job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); - hb_add( h, job ); + hb_add( h_queue, job ); //if (job->x264opts != NULL) // g_free(job->x264opts); } @@ -3018,39 +3183,39 @@ ghb_remove_job(gint unique_id) // Multiples passes all get the same id // remove them all. // Go backwards through list, so reordering doesn't screw me. - ii = hb_count(h) - 1; - while ((job = hb_job(h, ii--)) != NULL) + ii = hb_count(h_queue) - 1; + while ((job = hb_job(h_queue, ii--)) != NULL) { if ((job->sequence_id & 0xFFFFFF) == unique_id) - hb_rem(h, job); + hb_rem(h_queue, job); } } void ghb_start_queue() { - hb_start( h ); + hb_start( h_queue ); } void ghb_stop_queue() { - hb_stop( h ); + hb_stop( h_queue ); } void ghb_pause_queue() { hb_state_t s; - hb_get_state2( h, &s ); + hb_get_state2( h_queue, &s ); if( s.state == HB_STATE_PAUSED ) { - hb_resume( h ); + hb_resume( h_queue ); } else { - hb_pause( h ); + hb_pause( h_queue ); } } @@ -3064,7 +3229,7 @@ ghb_get_preview_image( hb_title_t *title; hb_list_t *list; - list = hb_get_titles( h ); + list = hb_get_titles( h_scan ); if( !hb_list_count( list ) ) { /* No valid title, stop right there */ @@ -3144,7 +3309,7 @@ ghb_get_preview_image( bufferSize = newSize; buffer = (guint8*) g_realloc( buffer, bufferSize ); } - hb_get_preview( h, title, index, buffer ); + hb_get_preview( h_scan, title, index, buffer ); // Create an GdkPixbuf and copy the libhb image into it, converting it from // libhb's format something suitable. Along the way, we'll strip off the diff --git a/gtk/src/hb-backend.h b/gtk/src/hb-backend.h index 3e171f1cf..19ba4205b 100644 --- a/gtk/src/hb-backend.h +++ b/gtk/src/hb-backend.h @@ -29,6 +29,7 @@ enum typedef struct ghb_status_s { gint state; + gint queue_state; // SCANNING gint title_count; @@ -90,10 +91,14 @@ void ghb_pause_queue(void); gint ghb_get_state(void); void ghb_clear_state(gint state); +void ghb_clear_queue_state(gint state); + void ghb_set_state(gint state); +gint ghb_get_queue_state(); void ghb_get_status(ghb_status_t *status); void ghb_track_status(void); void ghb_backend_scan(const gchar *path, gint titleindex); +void ghb_backend_queue_scan(const gchar *path, gint titleindex); gboolean ghb_get_title_info(ghb_title_info_t *tinfo, gint titleindex); void ghb_set_scale(signal_user_data_t *ud, gint mode); GValue* ghb_get_chapters(gint titleindex); diff --git a/gtk/src/main.c b/gtk/src/main.c index cc5c20a2e..6cf76ae1c 100644 --- a/gtk/src/main.c +++ b/gtk/src/main.c @@ -38,6 +38,7 @@ #include "renderer_button.h" #include "hb-backend.h" #include "ghb-dvd.h" +#include "ghbcellrenderertext.h" /* @@ -219,6 +220,7 @@ change_font(GtkWidget *widget, gpointer data) extern void chapter_list_selection_changed_cb(void); extern void chapter_edited_cb(void); +extern void chapter_keypress_cb(void); // Create and bind the tree model to the tree view for the chapter list // Also, connect up the signal that lets us know the selection has changed @@ -237,17 +239,18 @@ bind_chapter_tree_model (signal_user_data_t *ud) treestore = gtk_list_store_new(3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(treestore)); - cell = gtk_cell_renderer_text_new(); + cell = ghb_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Chapter No."), cell, "text", 0, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); - cell = gtk_cell_renderer_text_new(); + cell = ghb_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Chapter Title"), cell, "text", 1, "editable", 2, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); - g_signal_connect(cell, "edited", chapter_edited_cb, ud); + g_signal_connect(cell, "key-press-event", chapter_keypress_cb, ud); + g_signal_connect(cell, "edited", chapter_edited_cb, ud); g_signal_connect(selection, "changed", chapter_list_selection_changed_cb, ud); g_debug("Done\n"); } @@ -504,9 +507,11 @@ main (int argc, char *argv[]) ud = g_malloc(sizeof(signal_user_data_t)); ud->debug = FALSE; + ud->cancel_encode = FALSE; g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, debug_log_handler, ud); ud->settings = ghb_settings_new(); ud->queue = NULL; + ud->current_job = NULL; ud->current_dvd_device = NULL; // Redirect stderr to the activity window IoRedirect(ud); @@ -600,6 +605,8 @@ main (int argc, char *argv[]) // Source overridden from command line option ghb_settings_set_string(ud->settings, "source", dvd_device); } + // Reload and check status of the last saved queue + g_idle_add((GSourceFunc)ghb_reload_queue, ud); // Start timer for monitoring libhb status, 500ms g_timeout_add (500, ghb_timer_cb, (gpointer)ud); // Everything should be go-to-go. Lets rock! diff --git a/gtk/src/marshalers.c b/gtk/src/marshalers.c new file mode 100644 index 000000000..0f234ec3d --- /dev/null +++ b/gtk/src/marshalers.c @@ -0,0 +1,125 @@ + +#include <glib-object.h> + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:STRING,STRING (marshalers.list:1) */ +void +ghb_marshal_VOID__STRING_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_STRING) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__STRING_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); +} + +/* BOOLEAN:BOXED (marshalers.list:2) */ +void +ghb_marshal_BOOLEAN__BOXED (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer data1, + gpointer arg_1, + gpointer data2); + register GMarshalFunc_BOOLEAN__BOXED callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 2); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_boxed (param_values + 1), + data2); + + g_value_set_boolean (return_value, v_return); +} + diff --git a/gtk/src/marshalers.h b/gtk/src/marshalers.h new file mode 100644 index 000000000..1034f926d --- /dev/null +++ b/gtk/src/marshalers.h @@ -0,0 +1,28 @@ + +#ifndef __ghb_marshal_MARSHAL_H__ +#define __ghb_marshal_MARSHAL_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/* VOID:STRING,STRING (marshalers.list:1) */ +extern void ghb_marshal_VOID__STRING_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* BOOLEAN:BOXED (marshalers.list:2) */ +extern void ghb_marshal_BOOLEAN__BOXED (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __ghb_marshal_MARSHAL_H__ */ + diff --git a/gtk/src/plist.c b/gtk/src/plist.c index da7bc55e7..93178b370 100644 --- a/gtk/src/plist.c +++ b/gtk/src/plist.c @@ -382,14 +382,12 @@ ghb_plist_parse(const gchar *buf, gssize len) } GValue* -ghb_plist_parse_file(const gchar *filename) +ghb_plist_parse_file(FILE *fd) { - FILE *fd; gchar *buffer; size_t size; GValue *gval; - fd = fopen(filename, "r"); if (fd == NULL) return NULL; fseek(fd, 0, SEEK_END); @@ -577,11 +575,13 @@ main(gint argc, gchar *argv[]) g_type_init(); - gval = ghb_plist_parse_file(argv[1]); + file = g_fopen(argv[1], "r"); + gval = ghb_plist_parse_file(file); if (argc > 2) ghb_plist_write_file(argv[2], gval); else ghb_plist_write(stdout, gval); + if (file) fclose (file); return 0; } #endif diff --git a/gtk/src/plist.h b/gtk/src/plist.h index 8b2498826..4ea06780a 100644 --- a/gtk/src/plist.h +++ b/gtk/src/plist.h @@ -6,7 +6,7 @@ #include <glib-object.h> GValue* ghb_plist_parse(const gchar *buf, gssize len); -GValue* ghb_plist_parse_file(const gchar *filename); +GValue* ghb_plist_parse_file(FILE *fd); void ghb_plist_write(FILE *file, GValue *gval); void ghb_plist_write_file(const gchar *filename, GValue *gval); diff --git a/gtk/src/presets.c b/gtk/src/presets.c index 75ef74538..8f032d819 100644 --- a/gtk/src/presets.c +++ b/gtk/src/presets.c @@ -372,6 +372,40 @@ store_plist(GValue *plist, const gchar *name) fclose(file); } +static GValue* +load_plist(const gchar *name) +{ + const gchar *dir; + gchar *config; + FILE *file; + GValue *plist = NULL; + + dir = g_get_user_config_dir(); + config = g_strdup_printf ("%s/ghb/%s", dir, name); + if (g_file_test(config, G_FILE_TEST_IS_REGULAR)) + { + file = g_fopen(config, "r"); + plist = ghb_plist_parse_file(file); + } + g_free(config); + return plist; +} + +static void +remove_plist(const gchar *name) +{ + const gchar *dir; + gchar *config; + + dir = g_get_user_config_dir(); + config = g_strdup_printf ("%s/ghb/%s", dir, name); + if (g_file_test(config, G_FILE_TEST_IS_REGULAR)) + { + g_unlink(config); + } + g_free(config); +} + static gboolean prefs_initializing = FALSE; void @@ -535,28 +569,15 @@ ghb_settings_init(signal_user_data_t *ud) void ghb_prefs_load(signal_user_data_t *ud) { - const gchar *dir; - gchar *config; GValue *dict, *internal; GHashTableIter iter; gchar *key; GValue *gval; g_debug("ghb_prefs_load"); - dir = g_get_user_config_dir(); - config = g_strdup_printf ("%s/ghb/preferences", dir); - if (g_file_test(config, G_FILE_TEST_IS_REGULAR)) - { - prefsPlist = ghb_plist_parse_file(config); - if (prefsPlist == NULL) - prefsPlist = ghb_dict_value_new(); - } - else - { - // Make an empty plist + prefsPlist = load_plist("preferences"); + if (prefsPlist == NULL) prefsPlist = ghb_dict_value_new(); - } - g_free(config); dict = plist_get_dict(prefsPlist, "Preferences"); internal = plist_get_dict(internalPlist, "Preferences"); if (dict == NULL && internal) @@ -575,7 +596,6 @@ ghb_prefs_load(signal_user_data_t *ud) g_strdup("destination_dir"), ghb_value_dup(ghb_string_value(dir))); store_plist(prefsPlist, "preferences"); } - } void @@ -619,29 +639,33 @@ presets_store() } void -ghb_presets_load() +ghb_save_queue(GValue *queue) { - const gchar *dir; - gchar *config; + store_plist(queue, "queue"); +} - dir = g_get_user_config_dir(); - config = g_strdup_printf ("%s/ghb/presets", dir); - if (g_file_test(config, G_FILE_TEST_IS_REGULAR)) - { - presetsPlist = ghb_plist_parse_file(config); - if (presetsPlist == NULL) - { - presetsPlist = ghb_plist_parse( - standardPresets, sizeof(standardPresets)-1); - presets_store(); - } - } - else +GValue* +ghb_load_queue() +{ + return load_plist("queue"); +} + +void +ghb_remove_queue_file() +{ + remove_plist("queue"); +} + +void +ghb_presets_load() +{ + presetsPlist = load_plist("presets"); + if (presetsPlist == NULL) { presetsPlist = ghb_plist_parse( standardPresets, sizeof(standardPresets)-1); + presets_store(); } - g_free(config); } void diff --git a/gtk/src/presets.h b/gtk/src/presets.h index 44f9cdd9b..b216c5580 100644 --- a/gtk/src/presets.h +++ b/gtk/src/presets.h @@ -33,5 +33,8 @@ void ghb_prefs_to_ui(signal_user_data_t *ud); void ghb_prefs_save(GValue *settings); void ghb_pref_save(GValue *settings, const gchar *key); void ghb_set_preset_default(GValue *settings); +void ghb_save_queue(GValue *queue); +GValue* ghb_load_queue(); +void ghb_remove_queue_file(void);; #endif // _GHB_PRESETS_H_ diff --git a/gtk/src/settings.c b/gtk/src/settings.c index 4f2cacbb8..1fad691ce 100644 --- a/gtk/src/settings.c +++ b/gtk/src/settings.c @@ -193,19 +193,6 @@ ghb_settings_get_combo_option(GValue *settings, const gchar *key) return g_strdup(cd->option); } -gchar* -ghb_settings_get_combo_string(GValue *settings, const gchar *key) -{ - const GValue* value; - value = ghb_settings_get_value(settings, key); - if (value == NULL) return g_strdup(""); - ghb_combodata_t *cd; - if (G_VALUE_TYPE(value) != ghb_combodata_get_type()) - return g_strdup(""); - cd = g_value_get_boxed(value); - return g_strdup(cd->svalue); -} - // Map widget names to setting keys // Widgets that map to settings have names // of this format: s_<setting key> diff --git a/gtk/src/settings.h b/gtk/src/settings.h index 99d8e6866..cc89f1f7f 100644 --- a/gtk/src/settings.h +++ b/gtk/src/settings.h @@ -44,9 +44,11 @@ typedef struct gchar *current_dvd_device; gboolean debug; gboolean dont_clear_presets; + gboolean cancel_encode; GtkBuilder *builder; GValue *settings; GValue *queue; + GValue *current_job; GIOChannel *activity_log; } signal_user_data_t; @@ -83,7 +85,6 @@ gdouble ghb_settings_get_double(GValue *settings, const gchar *key); gchar* ghb_settings_get_string(GValue *settings, const gchar *key); gint ghb_settings_get_combo_index(GValue *settings, const gchar *key); gchar* ghb_settings_get_combo_option(GValue *settings, const gchar *name); -gchar* ghb_settings_get_combo_string(GValue *settings, const gchar *key); GValue* ghb_widget_value(GtkWidget *widget); gchar* ghb_widget_string(GtkWidget *widget); |