/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* * GData Client * Copyright (C) Joe Cortes 2010 * * GData Client is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * GData Client 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GData Client. If not, see . */ /* * This is an extremely simple example program to query youtube videos * and picasa pictures and add them to a grid. * It was coded as part of the 2010 Google Code-In. * Click on Properties to change the window's title, or authenticate * yourself (it's assumed that your google and picasa ids are the same) * It's also possible to upload files to picasa. * Since this is only an example, intended to serve as documentation, * it's probably full of bugs and a couple of awful programming practices. * The program itself is of no practical use, but it does what it is * meant to do and doesn't segfault randomly (I hope so :p). */ #include "scrapbook.h" static void open_in_web_browser (GtkWidget *widget, gchar *uri) /* quicky wrapper for gtk_show_uri */ { gtk_show_uri_on_window (GTK_WINDOW (gtk_widget_get_toplevel (widget)), uri, GDK_CURRENT_TIME, NULL); } static void picture_selected (GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, ScrapPicSearch *self) { GtkTreeModel *model; GtkTreeIter iter; GDataEntry *pic; GdkPixbuf *thumbnail; GtkWidget *button; GtkWidget *image; pic = g_slice_new (GDataEntry); model = gtk_tree_view_get_model (tree); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, COL_PIXBUF, &(thumbnail), P_COL_PIC, &(pic), -1); gtk_widget_destroy (self->search_data->window); gtk_list_store_append (self->main_data->lStore, &iter); gtk_list_store_set (self->main_data->lStore, &iter, ORIG_COL_PIXBUF, thumbnail, ORIG_COL_TITLE, gdata_entry_get_title (pic), ORIG_COL_ENTRY, pic, -1); if (self->main_data->currentRow[self->main_data->currentCol] > self->main_data->max_rows) { self->main_data->currentCol++; self->main_data->currentRow[self->main_data->currentCol] = 0; } image = gtk_image_new_from_pixbuf (thumbnail); button = gtk_button_new (); self->file = GDATA_PICASAWEB_FILE (pic); gtk_widget_show (image); gtk_container_add (GTK_CONTAINER (button), image); /*g_signal_connect (button, "clicked", G_CALLBACK (open_in_web_browser), gdata_entry_get_id (pic)); * commented out, I can't seem to find anything that will give me a picture's URI */ gtk_grid_attach (GTK_GRID (self->main_data->grid), button, self->main_data->currentCol, self->main_data->currentRow[self->main_data->currentCol], 1, 1); gtk_widget_show (button); self->main_data->currentRow[self->main_data->currentCol]++; } static void video_selected (GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, ScrapYTSearch *self) { GtkTreeModel *model; GtkTreeIter iter; GDataEntry *video; GdkPixbuf *thumbnail; GtkWidget *button; GtkWidget *image; video = g_slice_new (GDataEntry); model = gtk_tree_view_get_model (tree); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, COL_PIXBUF, &(thumbnail), COL_VIDEO, &(video), -1); gtk_widget_destroy (self->window); gtk_list_store_append (self->main_data->lStore, &iter); gtk_list_store_set (self->main_data->lStore, &iter, ORIG_COL_PIXBUF, thumbnail, ORIG_COL_TITLE, gdata_entry_get_title (video), ORIG_COL_ENTRY, video, -1); if (self->main_data->currentRow[self->main_data->currentCol] > self->main_data->max_rows) { self->main_data->currentCol++; self->main_data->currentRow[self->main_data->currentCol] = 0; } image = gtk_image_new_from_pixbuf (thumbnail); button = gtk_button_new (); gtk_widget_show (image); gtk_container_add (GTK_CONTAINER (button), image); g_signal_connect (button, "clicked", G_CALLBACK (open_in_web_browser), (gpointer) gdata_youtube_video_get_player_uri (GDATA_YOUTUBE_VIDEO (video))); gtk_grid_attach (GTK_GRID (self->main_data->grid), button, self->main_data->currentCol, self->main_data->currentRow[self->main_data->currentCol], 1, 1); gtk_widget_show (button); self->main_data->currentRow[self->main_data->currentCol]++; } static void p_display_tree (ScrapPicSearch *self) { GtkCellRenderer *renderer; GtkWidget *scrollWin; scrollWin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrollWin), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); renderer = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (self->tView), -1, "", renderer, "pixbuf", P_COL_PIXBUF, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (self->tView), -1, "", renderer, "text", P_COL_USER, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (self->tView), -1, "", renderer, "text", P_COL_TITLE, NULL); gtk_tree_view_set_model (GTK_TREE_VIEW (self->tView), GTK_TREE_MODEL (self->lStore)); gtk_container_add (GTK_CONTAINER (scrollWin), self->tView); g_signal_connect (self->tView, "row-activated", G_CALLBACK (picture_selected), self); gtk_widget_show (self->tView); gtk_widget_show (scrollWin); gtk_box_pack_start (GTK_BOX(self->search_data->box1), scrollWin, TRUE, TRUE, 2); } static void yt_display_tree (ScrapYTSearch *self) { GtkCellRenderer *renderer; GtkWidget *scrollWin; scrollWin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrollWin), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); renderer = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (self->tView), -1, "", renderer, "pixbuf", COL_PIXBUF, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (self->tView), -1, "", renderer, "text", COL_TITLE, NULL); gtk_tree_view_set_model (GTK_TREE_VIEW (self->tView), GTK_TREE_MODEL (self->lStore)); gtk_container_add (GTK_CONTAINER (scrollWin), self->tView); g_signal_connect (self->tView, "row-activated", G_CALLBACK (video_selected), self); gtk_widget_show (self->tView); gtk_widget_show (scrollWin); gtk_box_pack_start (GTK_BOX(self->box1), scrollWin, TRUE, TRUE, 2); } GDataMediaThumbnail * choose_best_thumbnail (GList *thumbnails, gint ideal_size) { gint delta = G_MININT; GDataMediaThumbnail *thumbnail = NULL; for (; thumbnails != NULL; thumbnails = thumbnails->next) { gint new_delta; GDataMediaThumbnail *current = (GDataMediaThumbnail *) thumbnails->data; new_delta = gdata_media_thumbnail_get_width (current) - ideal_size; if (delta == 0) { break; } else if ((delta == G_MININT) || (delta < 0 && new_delta > delta) || (delta > 0 && new_delta < delta)) { delta = new_delta; thumbnail = current; } } return thumbnail; } static void find_pictures (GDataEntry *entry, guint entry_key, guint entry_count, ScrapPicSearch *self) { GtkTreeIter iter; GList *thumbnails; GDataMediaThumbnail *thumbnail; GFileInputStream *input_stream; gtk_list_store_append (self->lStore, &iter); gtk_list_store_set (self->lStore, &iter, P_COL_PIXBUF, NULL, P_COL_TITLE, self->title, P_COL_USER, self->user, P_COL_PIC, entry, -1); thumbnails = gdata_picasaweb_file_get_thumbnails (GDATA_PICASAWEB_FILE (entry)); thumbnail = choose_best_thumbnail (thumbnails, THUMBNAIL_WIDTH); if (thumbnail != NULL) { GFile *thumbnail_file; thumbnail_file = g_file_new_for_uri (gdata_media_thumbnail_get_uri (thumbnail)); input_stream = g_file_read (thumbnail_file, NULL, NULL); self->thumbnail = gdk_pixbuf_new_from_stream_at_scale (G_INPUT_STREAM (input_stream), THUMBNAIL_WIDTH, -1, TRUE, NULL, NULL); gtk_list_store_set (self->lStore, &iter, P_COL_PIXBUF, self->thumbnail, -1); /* we can now set the thumbnail ;) */ g_object_unref (thumbnail_file); } gdata_query_set_q (self->query, NULL); } static void p_query_element (GDataEntry *entry, guint entry_key, guint entry_count, ScrapPSearch *self) { GError *error=NULL; ScrapPicSearch *picture; GDataFeed *feed; picture = self->pic; picture->title = gdata_entry_get_title (entry); picture->query = picture->search_data->query; picture->user = self->user; gdata_query_set_q (picture->query, picture->title); feed = gdata_picasaweb_service_query_files (self->main_data->picasaweb_service, GDATA_PICASAWEB_ALBUM (entry), picture->query, NULL, (GDataQueryProgressCallback) find_pictures, picture, &error); if (error != NULL) { g_print ("whoops, somebody raised an error!\n%s", error->message); g_error_free (error); } if (feed != NULL) { g_object_unref (feed); } } static void p_text_callback (GtkWidget *widget, ScrapPSearch *self) { GError *error = NULL; GDataFeed *feed; self->user = gtk_entry_get_text (GTK_ENTRY (self->user_entry)); self->pic->tView = gtk_tree_view_new (); feed = gdata_picasaweb_service_query_all_albums (self->main_data->picasaweb_service, self->query, self->user, NULL, (GDataQueryProgressCallback) p_query_element, self, &error); if (error != NULL) { g_print ("someone raised an error\n%s\n",error->message); g_error_free (error); } if (feed != NULL) { g_object_unref (feed); } p_display_tree (self->pic); } /* ran as a callback for each individual element queried * it takes the video found, a unique entry_key and entry_count and the data structure as arguments * parts of this function were inspired (or simply taken) from the totem youtube plugin */ static void yt_query_element (GDataEntry *entry, guint entry_key, guint entry_count, ScrapYTSearch *self) { GtkTreeIter iter; GList *thumbnails; GDataMediaThumbnail *thumbnail; const gchar *title; /* the video's title */ const gchar *uri; /* the video's URI */ GFileInputStream *input_stream; /* this will be used to make a pixbuf to store the thumbnail */ title = gdata_entry_get_title (entry); /* self-explanatory, I hope */ uri = gdata_youtube_video_get_player_uri (GDATA_YOUTUBE_VIDEO (entry)); /* ditto */ g_print ("%s %s", title, uri); gtk_list_store_append (self->lStore, &iter); /* make a new entry for this vid */ gtk_list_store_set (self->lStore, &iter, COL_PIXBUF, NULL, /* this will be set in a few moments */ COL_TITLE, title, COL_VIDEO, entry, -1); /* get a GList of thumbnails for the vid */ thumbnails = gdata_youtube_video_get_thumbnails (GDATA_YOUTUBE_VIDEO (entry)); thumbnail = choose_best_thumbnail (thumbnails, THUMBNAIL_WIDTH); if (thumbnail != NULL) { GFile *thumbnail_file; thumbnail_file = g_file_new_for_uri (gdata_media_thumbnail_get_uri (thumbnail)); input_stream = g_file_read (thumbnail_file, NULL, NULL); self->thumbnail = gdk_pixbuf_new_from_stream_at_scale (G_INPUT_STREAM (input_stream), THUMBNAIL_WIDTH, -1, TRUE, NULL, NULL); gtk_list_store_set (self->lStore, &iter, COL_PIXBUF, self->thumbnail, -1); /* we can now set the thumbnail ;) */ g_object_unref (thumbnail_file); } } static void yt_text_callback (GtkWidget *widget, ScrapYTSearch *self) { GDataFeed *feed; self->txt = gtk_entry_get_text (GTK_ENTRY (self->txt_entry)); gdata_query_set_q (self->query, self->txt); /* set the string we'll be searching for in youtube */ /* do the actual query, running yt_query_element for each object found */ feed = gdata_youtube_service_query_videos (self->main_data->youtube_service, self->query, NULL, (GDataQueryProgressCallback) yt_query_element, self, NULL); if (feed != NULL) { g_object_unref (feed); } yt_display_tree (self); /* run yt_display_tree to show the results */ } static void start_new_picasa_search (GtkWidget *widget, ScrapData *first) { ScrapPSearch *self; ScrapPicSearch *picture; GtkWidget *button, *box2; self = first->p_search; picture = first->pic_search; picture->search_data = self; picture->search_data->pic = picture; picture->main_data = self->main_data; g_assert (GDATA_IS_PICASAWEB_SERVICE (first->picasaweb_service)); gtk_list_store_clear (self->pic->lStore); self->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_resize (GTK_WINDOW (self->window), 400, 400); g_signal_connect (self->window, "destroy", G_CALLBACK (gtk_widget_destroy), NULL); g_signal_connect_swapped (self->window, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); /* our two boxes */ self->box1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); /* this box contains everything in our window */ gtk_container_add (GTK_CONTAINER (self->window), self->box1); box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); /* search bar */ self->user_entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (self->user_entry), "User to search for"); g_signal_connect (self->user_entry, "activate", (GCallback) p_text_callback, self); gtk_box_pack_start (GTK_BOX(box2), self->user_entry, TRUE, TRUE, 0); gtk_widget_show (self->user_entry); /* Search button */ button = gtk_button_new_with_mnemonic ("_Search"); g_signal_connect (button, "clicked", (GCallback) p_text_callback, self); gtk_box_pack_start (GTK_BOX (box2), button, FALSE, FALSE, 0); gtk_widget_show (button); gtk_box_pack_end (GTK_BOX (self->box1), box2, FALSE, FALSE, 0); /* pack the box with the button and search bar */ gtk_widget_show (box2); gtk_widget_show (self->box1); gtk_widget_show (self->window); } static void start_new_youtube_search (GtkWidget *widget, ScrapData *first) /* *first is a pointer we use to talk to the main window */ { ScrapYTSearch *self; /* this struct will be used for all the data in the search, if there's time I'll make it into a GObject */ GtkWidget *button, *box2; self = first->yt_search; gtk_list_store_clear (self->lStore); /* clear it out */ self->tView = gtk_tree_view_new (); /* window stuff */ self->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_resize (GTK_WINDOW (self->window), 400, 400); g_signal_connect (self->window, "destroy", G_CALLBACK (gtk_widget_destroy), NULL); g_signal_connect_swapped (self->window, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); /* our two boxes */ self->box1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); /* this box contains everything in our window */ gtk_container_add (GTK_CONTAINER (self->window), self->box1); box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); /* search bar */ self->txt_entry = gtk_entry_new (); g_signal_connect (self->txt_entry, "activate", G_CALLBACK (yt_text_callback), self); gtk_box_pack_start (GTK_BOX (box2), self->txt_entry, TRUE, TRUE, 0); gtk_widget_show (self->txt_entry); /* Search button */ button = gtk_button_new_with_mnemonic ("_Search"); g_signal_connect (button, "clicked", (GCallback) yt_text_callback, self); gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0); gtk_widget_show (button); gtk_box_pack_end (GTK_BOX (self->box1), box2, FALSE, FALSE, 0); /* pack the box with the button and search bar */ gtk_widget_show (box2); gtk_widget_show (self->box1); gtk_widget_show (self->window); /* everything else is implemented somewhere else */ } static void properties_set (GtkWidget *widget, ScrapProps *self) { GDataClientLoginAuthorizer *authorizer; GList *domains = NULL; /* list of GDataAuthorizationDomains */ GError *error = NULL; /* Get the username and password to use */ self->main_data->username = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->username_entry))); self->main_data->password = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->password_entry))); /* Domains we need to be authorised for */ domains = g_list_prepend (domains, gdata_youtube_service_get_primary_authorization_domain ()); domains = g_list_prepend (domains, gdata_picasaweb_service_get_primary_authorization_domain ()); /* Authenticate */ authorizer = gdata_client_login_authorizer_new_for_authorization_domains (CLIENT_ID, domains); gdata_client_login_authorizer_authenticate (authorizer, self->main_data->username, self->main_data->password, NULL, &error); if (error != NULL) { /* we show this to the user in case they mistyped their password */ GtkWidget *label; label = gtk_label_new (error->message); gtk_widget_show (label); gtk_box_pack_end (GTK_BOX (self->box1), label, FALSE, FALSE, 0); g_print ("error\n%s\n", error->message); g_error_free (error); } gdata_service_set_authorizer (GDATA_SERVICE (self->main_data->youtube_service), GDATA_AUTHORIZER (authorizer)); gdata_service_set_authorizer (GDATA_SERVICE (self->main_data->picasaweb_service), GDATA_AUTHORIZER (authorizer)); gtk_widget_destroy (self->window); g_object_unref (authorizer); } static void properties_show (GtkWidget *widget, ScrapData *first) { ScrapProps *self; GtkWidget *label, *button, *box2; self = g_slice_new (struct _ScrapProps); self->main_data = first; self->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (self->window, "destroy", G_CALLBACK (gtk_widget_destroy), NULL); g_signal_connect (self->window, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); self->box1 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3); /* Username/Password labels box */ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); label = gtk_label_new ("Username"); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (box2), label, TRUE, TRUE, 0); label = gtk_label_new ("Password"); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (box2), label, TRUE, TRUE, 0); gtk_widget_show (box2); gtk_box_pack_start (GTK_BOX (self->box1), box2, FALSE, FALSE, 0); /* Username/Password entries box */ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); self->username_entry = gtk_entry_new (); if (self->main_data->username != NULL) gtk_entry_set_text (GTK_ENTRY(self->username_entry), self->main_data->username); gtk_widget_show (self->username_entry); gtk_box_pack_start (GTK_BOX (box2), self->username_entry, TRUE, TRUE, 0); self->password_entry = gtk_entry_new (); gtk_entry_set_visibility (GTK_ENTRY (self->password_entry), FALSE); if (self->main_data->password != NULL) gtk_entry_set_text (GTK_ENTRY(self->password_entry), self->main_data->password); gtk_widget_show (self->password_entry); gtk_box_pack_start (GTK_BOX (box2), self->password_entry, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (self->box1), box2, FALSE, FALSE, 0); gtk_widget_show (box2); /* OK button */ button = gtk_button_new_with_label ("_OK"); g_signal_connect (button, "clicked", (GCallback) properties_set, self); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (self->box1), button, FALSE, FALSE, 0); gtk_widget_show (self->box1); gtk_container_add (GTK_CONTAINER (self->window), self->box1); gtk_widget_show (self->window); } static void select_file (ScrapPUpload *self, GtkFileChooser *file_chooser) { GFile *file; GError *error = NULL; GFileInfo *file_info; GDataUploadStream *upload_stream; GFileInputStream *file_stream; file = gtk_file_chooser_get_file (file_chooser); file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); /* upload our file, using the service we've set up, and metadata * set up in upload () * no album is specified, but that should be easy to add */ upload_stream = gdata_picasaweb_service_upload_file (self->main_data->picasaweb_service, NULL /* for now uploading to drop box */, self->file, g_file_info_get_display_name (file_info), g_file_info_get_content_type (file_info), NULL, &error); g_object_unref (file_info); g_object_unref (self->file); self->file = NULL; if (error != NULL) { g_print ("Error: %s\n", error->message); g_error_free (error); g_object_unref (file); return; } file_stream = g_file_read (file, NULL, NULL); g_object_unref (file); g_output_stream_splice (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (file_stream), G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, NULL); self->file = gdata_picasaweb_service_finish_file_upload (self->main_data->picasaweb_service, upload_stream, NULL); g_object_unref (file_stream); g_object_unref (upload_stream); } static void got_name (GtkWidget *widget, ScrapData *scrap_data) { ScrapPUpload *self; GtkWidget *file_dialog; self = scrap_data->p_upload; gdata_entry_set_title (GDATA_ENTRY (self->file), gtk_entry_get_text (GTK_ENTRY (self->name))); gdata_entry_set_summary (GDATA_ENTRY (self->file), gtk_entry_get_text (GTK_ENTRY (self->description))); gtk_widget_destroy (self->dialog); file_dialog = gtk_file_chooser_dialog_new ("Upload Photo", GTK_WINDOW (scrap_data->window), GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); if (gtk_dialog_run (GTK_DIALOG (file_dialog)) == GTK_RESPONSE_ACCEPT) { select_file (self, GTK_FILE_CHOOSER (file_dialog)); } /* since the upload blocks, it's safe to assume the widget won't * be destroyed until we're done */ gtk_widget_destroy (file_dialog); } static void upload (GtkWidget *widget, ScrapData *first) { ScrapPUpload *self; GtkWidget *label, *content_area; label = gtk_label_new ("Enter photo name and description"); self = first->p_upload; /* we make a new file, without an id (it will be assigned automatically later on) */ self->file = gdata_picasaweb_file_new (NULL); /* dialog to get the file's name and description */ self->dialog = gtk_dialog_new(); content_area = gtk_dialog_get_content_area (GTK_DIALOG (self->dialog)); gtk_widget_show (label); gtk_box_pack_start (GTK_BOX (content_area), label, FALSE, FALSE, 0); self->name = gtk_entry_new (); g_signal_connect (self->name, "activate", G_CALLBACK (got_name), self); gtk_widget_show (self->name); gtk_box_pack_start (GTK_BOX (content_area), self->name, TRUE, TRUE, 0); self->description = gtk_entry_new (); g_signal_connect (self->description, "activate", G_CALLBACK (got_name), first); gtk_widget_show (self->description); gtk_box_pack_start (GTK_BOX (content_area), self->description, TRUE, TRUE, 0); gtk_widget_show (self->dialog); } int main(int argc, char **argv) { ScrapData *scrapbook; ScrapPSearch *picasaSearch; ScrapYTSearch *youtubeSearch; ScrapPicSearch *photoSearch; ScrapPUpload *fUpload; GtkWidget *button; GtkWidget *vbox; scrapbook = g_slice_new (struct _ScrapData); scrapbook->max_rows = 5; gtk_init (&argc, &argv); scrapbook->currentCol = 0; scrapbook->currentRow[scrapbook->currentCol] = 0; scrapbook->lStore = gtk_list_store_new (ORIG_N_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDATA_TYPE_ENTRY); scrapbook->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (scrapbook->window), "Scrapbook"); g_signal_connect (scrapbook->window, "destroy", G_CALLBACK (gtk_main_quit), NULL); g_signal_connect (scrapbook->window, "delete-event", G_CALLBACK (gtk_main_quit), NULL); youtubeSearch = g_slice_new (struct _ScrapYTSearch); /* get some memory for the structure */ youtubeSearch->txt = NULL; scrapbook->yt_search = youtubeSearch; youtubeSearch->main_data = scrapbook; /* create a new query, without any search text, starting at 0, and search only MAX_RESULTS results */ youtubeSearch->query = gdata_query_new_with_limits (NULL, 0, MAX_RESULTS); /* create a new youtube service, giving it our developer key; google no longer uses client ids so we send in an empty string (NULL gives an error) */ scrapbook->youtube_service = gdata_youtube_service_new (DEVELOPER_KEY, NULL); /* create a new list store and tree to show the user the results * it has three columns (two of which are displayed): a pixbuf for the thumbnail, the title, and the video data itself (as a gdata generic entry) */ youtubeSearch->lStore = gtk_list_store_new (N_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDATA_TYPE_ENTRY); picasaSearch = g_slice_new (struct _ScrapPSearch); scrapbook->p_search = picasaSearch; picasaSearch->main_data = scrapbook; picasaSearch->query = gdata_query_new_with_limits (NULL, 0, MAX_RESULTS); scrapbook->picasaweb_service = gdata_picasaweb_service_new (NULL); photoSearch = g_slice_new (struct _ScrapPicSearch); scrapbook->p_search->pic = photoSearch; scrapbook->pic_search = photoSearch; photoSearch->lStore = gtk_list_store_new (P_N_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, GDATA_TYPE_ENTRY); fUpload = g_slice_new (struct _ScrapPUpload); scrapbook->p_upload = fUpload; fUpload->main_data = scrapbook; scrapbook->box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); scrapbook->grid = gtk_grid_new (); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); /* Add buttons to the main window */ button = gtk_button_new_with_mnemonic ("Add YouTube _Video"); g_signal_connect (button, "clicked", (GCallback) start_new_youtube_search, scrapbook); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); gtk_widget_show (button); button = gtk_button_new_with_mnemonic ("Add PicasaWeb _Photo"); g_signal_connect (button, "clicked", (GCallback) start_new_picasa_search, scrapbook); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); gtk_widget_show (button); button = gtk_button_new_with_mnemonic ("P_roperties"); g_signal_connect (button, "clicked", (GCallback) properties_show, scrapbook); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); gtk_widget_show (button); button = gtk_button_new_with_mnemonic ("_Upload Photo to PicasaWeb"); g_signal_connect (button, "clicked", (GCallback) upload, scrapbook); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); gtk_widget_show (button); gtk_widget_show (vbox); gtk_box_pack_start (GTK_BOX (scrapbook->box1), vbox, FALSE, FALSE, 5); gtk_box_pack_start (GTK_BOX (scrapbook->box1), scrapbook->grid, TRUE, TRUE, 0); gtk_widget_show (scrapbook->grid); gtk_widget_show (scrapbook->box1); gtk_container_add (GTK_CONTAINER (scrapbook->window), scrapbook->box1); gtk_widget_show (scrapbook->window); gtk_main (); return 0; }