/** * hildon_helloworld-8.c * * This maemo code example is licensed under a MIT-style license, * that can be found in the file called "License" in the same * directory as this file. * Copyright (c) 2007-2009 Nokia Corporation. All rights reserved. * * We add file loading support using GnomeVFS. Saving files using * GnomeVFS is left as an exercise. We also add a small notification * widget (HildonBanner). * * Look for lines with "NEW" or "MODIFIED" in them. */ #include #include #include #include #include /* A small notification window widget (NEW). */ #include /* Pull in the GnomeVFS headers (NEW). */ #include /* Declare the two slant styles. */ enum { STYLE_SLANT_NORMAL = 0, STYLE_SLANT_ITALIC }; /** * The application state. */ typedef struct { gboolean styleUseUnderline; gboolean styleSlant; GdkColor currentColor; /* Pointer to the label so that we can modify its contents when a file is loaded by the user (NEW). */ GtkWidget* textLabel; gboolean fullScreen; GtkWidget* findToolbar; GtkWidget* mainToolbar; gboolean findToolbarIsVisible; gboolean mainToolbarIsVisible; HildonProgram* program; HildonWindow* window; } ApplicationState; static gboolean cbEventDelete(GtkWidget* widget, GdkEvent* event, ApplicationState* app) { return FALSE; } static void cbActionTopDestroy(GtkWidget* widget, ApplicationState* app) { gtk_main_quit(); } /** * Ask user to pick a file and return the filename to caller. */ static gchar* runFileChooser(ApplicationState* app, GtkFileChooserAction style) { GtkWidget* dialog = NULL; gchar* filename = NULL; g_assert(app != NULL); g_print("runFilechooser: invoked\n"); dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(app->window), style); gtk_widget_show_all(GTK_WIDGET(dialog)); g_print(" running dialog\n"); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) { filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); } g_print(" dialog completed\n"); gtk_widget_destroy(dialog); if (filename != NULL) { g_print(" user selected filename '%s'\n", filename); } else { g_print(" user didn't select any filename\n"); } return filename; } /** * Utility function to print a GnomeVFS I/O related error message to * standard error (not seen by the user in graphical mode) (NEW). */ static void dbgFileError(GnomeVFSResult errCode, const gchar* uri) { g_printerr("Error while accessing '%s': %s\n", uri, gnome_vfs_result_to_string(errCode)); } /** * MODIFIED (A LOT) * * We read in the file selected by the user if possible and set the * contents of the file as the new Label content. * * If reading the file will fail, we leave the label unchanged. */ static void cbActionOpen(GtkWidget* widget, ApplicationState* app) { gchar* filename = NULL; /* We need to use URIs with GnomeVFS, so declare one here. */ gchar* uri = NULL; g_assert(app != NULL); /* Bad things will happen if these two widgets don't exist. */ g_assert(GTK_IS_LABEL(app->textLabel)); g_assert(GTK_IS_WINDOW(app->window)); g_print("cbActionOpen invoked\n"); /* Ask the user to select a file to open. */ filename = runFileChooser(app, GTK_FILE_CHOOSER_ACTION_OPEN); if (filename) { /* This will point to loaded data buffer. */ gchar* buffer = NULL; /* Pointer to a structure describing an open GnomeVFS "file". */ GnomeVFSHandle* fileHandle = NULL; /* Structure to hold information about a "file", initialized to zero. */ GnomeVFSFileInfo fileInfo = {}; /* Result code from the GnomeVFS operations. */ GnomeVFSResult result; /* Size of the file (in bytes) that we'll read in. */ GnomeVFSFileSize fileSize = 0; /* Number of bytes that were read in successfully. */ GnomeVFSFileSize readCount = 0; g_print(" you chose to load file '%s'\n", filename); /* Convert the filename into an GnomeVFS URI. */ uri = gnome_vfs_get_uri_from_local_path(filename); /* We don't need the original filename anymore. */ g_free(filename); filename = NULL; /* Should not happen since we got a filename before. */ g_assert(uri != NULL); /* Attempt to get file size first. We need to get information about the file and aren't interested in other than the very basic information, so we'll use the INFO_DEFAULT setting. */ result = gnome_vfs_get_file_info(uri, &fileInfo, GNOME_VFS_FILE_INFO_DEFAULT); if (result != GNOME_VFS_OK) { /* There was a failure. Print a debug error message and break out into error handling. */ dbgFileError(result, uri); goto error; } /* We got the information (maybe). Let's check whether it contains the data that we need. */ if (fileInfo.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) { /* Yes, we got the file size. */ fileSize = fileInfo.size; } else { g_printerr("Couldn't get the size of file!\n"); goto error; } /* By now we have the file size to read in. Check for some limits first. */ if (fileSize > 1024*100) { g_printerr("Loading over 100KiB files is not supported!\n"); goto error; } /* Refuse to load empty files. */ if (fileSize == 0) { g_printerr("Refusing to load an empty file\n"); goto error; } /* Allocate memory for the contents and fill it with zeroes. NOTE: We leave space for the terminating zero so that we can pass this buffer as gchar to string functions and it is guaranteed to be terminated, even if the file doesn't end with binary zero (odds of that happening are small). */ buffer = g_malloc0(fileSize+1); if (buffer == NULL) { g_printerr("Failed to allocate %u bytes for buffer\n", (guint)fileSize); goto error; } /* Open the file. Parameters: - A pointer to the location where to store the address of the new GnomeVFS file handle (created internally in open). - uri: What to open (needs to be GnomeVFS URI). - open-flags: Flags that tell what we plan to use the handle for. This will affect how permissions are checked by the Linux kernel. */ result = gnome_vfs_open(&fileHandle, uri, GNOME_VFS_OPEN_READ); if (result != GNOME_VFS_OK) { dbgFileError(result, uri); goto error; } /* File opened succesfully, read its contents in. */ result = gnome_vfs_read(fileHandle, buffer, fileSize, &readCount); if (result != GNOME_VFS_OK) { dbgFileError(result, uri); goto error; } /* Verify that we got the amount of data that we requested. NOTE: With URIs it won't be an error to get less bytes than you requested. Getting zero bytes will however signify an End-of-File condition. */ if (fileSize != readCount) { g_printerr("Failed to load the requested amount\n"); /* We could also attempt to read the missing data until we have filled our buffer, but for simplicity, we'll flag this condition as an error. */ goto error; } /* Whew, if we got this far, it means that we actually managed to load the file into memory. Let's set the buffer contents as the new label now. */ gtk_label_set_markup(GTK_LABEL(app->textLabel), buffer); /* That's it! Display a message of great joy. For this we'll use a dialog (non-modal) designed for displaying short informational messages. It will linger around on the screen for a while and then disappear (in parallel to our program continuing). */ hildon_banner_show_information(GTK_WIDGET(app->window), NULL, /* Use the default icon (info). */ "File loaded successfully"); /* Jump to the resource releasing phase. */ goto release; error: /* Display a failure message with a stock icon. Please see http://maemo.org/api_refs/4.0/gtk/gtk-Stock-Items.html for a full listing of stock items. */ hildon_banner_show_information(GTK_WIDGET(app->window), GTK_STOCK_DIALOG_ERROR, /* Use the stock error icon. */ "Failed to load the file"); release: /* Close and free all resources that were allocated. */ if (fileHandle) gnome_vfs_close(fileHandle); if (filename) g_free(filename); if (uri) g_free(uri); if (buffer) g_free(buffer); /* Zero them all out to prevent stack-reuse-bugs. */ fileHandle = NULL; filename = NULL; uri = NULL; buffer = NULL; return; } else { g_print(" you didn't choose any file to open\n"); } } /** * MODIFIED (kind of) * * Function to save the contents of the label (although it doesn't * actually save the contents, on purpose). Use gtk_label_get_label * to get a gchar pointer into the application label contents * (including current markup), then use gnome_vfs_create and * gnome_vfs_write to create the file (left as an exercise). */ static void cbActionSave(GtkWidget* widget, ApplicationState* app) { gchar* filename = NULL; g_assert(app != NULL); g_print("cbActionSave invoked\n"); filename = runFileChooser(app, GTK_FILE_CHOOSER_ACTION_SAVE); if (filename) { g_print(" you chose to save into '%s'\n", filename); /* Process saving .. */ g_free(filename); filename = NULL; } else { g_print(" you didn't choose a filename to save to\n"); } } static void cbActionQuit(GtkWidget* widget, ApplicationState* app) { g_print("cbActionQuit invoked. Terminating using gtk_main_quit\n"); gtk_main_quit(); } static void cbActionUnderlineToggled(GtkCheckMenuItem* item, ApplicationState* app) { g_assert(app != NULL); g_print("cbActionUnderlineToggled invoked\n"); app->styleUseUnderline = gtk_check_menu_item_get_active(item); g_print(" underlining is now %s\n", app->styleUseUnderline?"on":"off"); } static void cbActionStyleNormalToggled(GtkCheckMenuItem* item, ApplicationState* app) { g_assert(app != NULL); g_print("cbActionStyleNormalToggled invoked\n"); if (gtk_check_menu_item_get_active(item)) { app->styleSlant = STYLE_SLANT_NORMAL; g_print(" selected slanting for text is now Normal\n"); } } static void cbActionStyleItalicToggled(GtkCheckMenuItem* item, ApplicationState* app) { g_assert(app != NULL); g_print("cbActionStyleItalicToggled invoked\n"); if (gtk_check_menu_item_get_active(item)) { app->styleSlant = STYLE_SLANT_ITALIC; g_print(" selected slanting for text is now Italic\n"); } } static void cbActionColorChanged(GtkColorButton* colorButton, ApplicationState* app) { g_assert(app != NULL); g_print("cbActionColorChanged invoked\n"); gtk_color_button_get_color(colorButton, &app->currentColor); g_print(" New color is r=%d g=%d b=%d\n", app->currentColor.red, app->currentColor.green, app->currentColor.blue); } static void cbActionFindToolbarToggle(GtkWidget* widget, ApplicationState* app) { gboolean newVisibilityState = FALSE; g_assert(app != NULL); g_assert(GTK_IS_TOOLBAR(app->findToolbar)); g_print("cbActionFindToolbarToggle invoked\n"); /* Toggle the visibility flag. */ newVisibilityState = ~app->findToolbarIsVisible; if (newVisibilityState) { g_print(" showing find-toolbar\n"); gtk_widget_show(app->findToolbar); } else { g_print(" hiding find-toolbar\n"); gtk_widget_hide(app->findToolbar); } app->findToolbarIsVisible = newVisibilityState; } static void cbActionMainToolbarToggle(GtkCheckMenuItem* item, ApplicationState* app) { gboolean newVisibilityState = FALSE; g_assert(app != NULL); g_assert(GTK_IS_TOOLBAR(app->mainToolbar)); g_print("cbActionMainToolbarToggle invoked\n"); newVisibilityState = gtk_check_menu_item_get_active(item); if (app->mainToolbarIsVisible != newVisibilityState) { if (newVisibilityState) { gtk_widget_show(app->mainToolbar); } else { gtk_widget_hide(app->mainToolbar); } app->mainToolbarIsVisible = newVisibilityState; } } static void cbActionFindToolbarSearch(HildonFindToolbar* fToolbar, ApplicationState* app) { gchar* findText = NULL; g_assert(app != NULL); g_print("cbActionFindToolbarSearch invoked\n"); g_object_get(G_OBJECT(fToolbar), "prefix", &findText, NULL); if (findText != NULL) { g_print(" would search for '%s' if would know how to\n", findText); } } static void cbActionFindToolbarClosed(HildonFindToolbar* fToolbar, ApplicationState* app) { g_assert(app != NULL); g_print("cbActionFindToolbarClosed invoked\n"); gtk_widget_hide(GTK_WIDGET(fToolbar)); app->findToolbarIsVisible = FALSE; } /** * Utility to switch the application to fullscreen mode. */ static void cbActionGoFullscreen(GtkMenuItem* mi, ApplicationState* app) { g_assert(app != NULL); g_print("cbActionGoFullscreen invoked. Going fullscreen.\n"); gtk_window_fullscreen(GTK_WINDOW(app->window)); app->fullScreen = TRUE; } /** * Key press event handler. */ static gboolean cbKeyPressed(GtkWidget* widget, GdkEventKey* ev, ApplicationState* app) { g_assert(app != NULL); g_print("cbKeyPress invoked\n"); /* if pressed ctrl-f */ if (ev->keyval == GDK_f && (ev->state & GDK_CONTROL_MASK)) { g_print(" pressed ctrl-f (fullscreen)\n"); if (app->fullScreen) { gtk_window_unfullscreen(GTK_WINDOW(app->window)); app->fullScreen = FALSE; } else { gtk_window_fullscreen(GTK_WINDOW(app->window)); app->fullScreen = TRUE; } /* We recognized the key, do not process it any further. */ return TRUE; } else { g_print(" not ctrl-f (something else)\n"); } /* We didn't process the event. */ return FALSE; } /** * Create the toolbar. */ static GtkWidget* buildToolbar(ApplicationState* app) { GtkToolbar* toolbar = NULL; GtkToolItem* tbOpen = NULL; GtkToolItem* tbSave = NULL; GtkToolItem* tbSep = NULL; GtkToolItem* tbFind = NULL; GtkToolItem* tbColorButton = NULL; GtkWidget* colorButton = NULL; g_assert(app != NULL); tbOpen = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN); tbSave = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE); tbSep = gtk_separator_tool_item_new(); tbFind = gtk_tool_button_new_from_stock(GTK_STOCK_FIND); tbColorButton = gtk_tool_item_new(); colorButton = gtk_color_button_new(); gtk_container_add(GTK_CONTAINER(tbColorButton), colorButton); toolbar = GTK_TOOLBAR(gtk_toolbar_new()); gtk_toolbar_insert(toolbar, tbOpen, -1); gtk_toolbar_insert(toolbar, tbSave, -1); gtk_toolbar_insert(toolbar, tbSep, -1); gtk_toolbar_insert(toolbar, tbFind, -1); gtk_toolbar_insert(toolbar, tbColorButton, -1); gtk_widget_show_all(GTK_WIDGET(toolbar)); if (!app->mainToolbarIsVisible) { gtk_widget_hide(GTK_WIDGET(toolbar)); } g_signal_connect(G_OBJECT(tbOpen), "clicked", G_CALLBACK(cbActionOpen), app); g_signal_connect(G_OBJECT(tbSave), "clicked", G_CALLBACK(cbActionSave), app); g_signal_connect(G_OBJECT(tbFind), "clicked", G_CALLBACK(cbActionFindToolbarToggle), app); g_signal_connect(G_OBJECT(colorButton), "color-set", G_CALLBACK(cbActionColorChanged), app); /* Return the toolbar as a GtkWidget*. */ return GTK_WIDGET(toolbar); } /** * Create the Find toolbar. */ static GtkWidget* buildFindToolbar(ApplicationState* app) { GtkWidget* findToolbar = NULL; g_assert(app != NULL); findToolbar = hildon_find_toolbar_new("Find "); g_signal_connect(G_OBJECT(findToolbar), "search", G_CALLBACK(cbActionFindToolbarSearch), app); g_signal_connect(G_OBJECT(findToolbar), "close", G_CALLBACK(cbActionFindToolbarClosed), app); gtk_widget_show_all(findToolbar); if (!app->findToolbarIsVisible) { gtk_widget_hide(findToolbar); } return findToolbar; } /** * Build the Style sub menu and return it. */ static GtkWidget* buildSubMenu(ApplicationState* app) { GtkWidget* subMenu = NULL; GtkWidget* mciUnderline = NULL; GtkWidget* miSep = NULL; GtkWidget* mriNormal = NULL; GtkWidget* mriItalic = NULL; g_assert(app != NULL); mciUnderline = gtk_check_menu_item_new_with_label("Underline"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mciUnderline), app->styleUseUnderline); { GSList* group = NULL; mriItalic = gtk_radio_menu_item_new_with_label(NULL, "Italic"); group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(mriItalic)); mriNormal = gtk_radio_menu_item_new_with_label(group, "Normal"); } if (app->styleSlant == STYLE_SLANT_NORMAL) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mriNormal), TRUE); } else { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mriItalic), TRUE); } miSep = gtk_separator_menu_item_new(); subMenu = gtk_menu_new(); gtk_menu_shell_append(GTK_MENU_SHELL(subMenu), mciUnderline); gtk_menu_shell_append(GTK_MENU_SHELL(subMenu), miSep); gtk_menu_shell_append(GTK_MENU_SHELL(subMenu), mriNormal); gtk_menu_shell_append(GTK_MENU_SHELL(subMenu), mriItalic); g_signal_connect(G_OBJECT(mciUnderline), "toggled", G_CALLBACK(cbActionUnderlineToggled), app); g_signal_connect(G_OBJECT(mriNormal), "toggled", G_CALLBACK(cbActionStyleNormalToggled), app); g_signal_connect(G_OBJECT(mriItalic), "toggled", G_CALLBACK(cbActionStyleItalicToggled), app); return subMenu; } /** * Build the main level menu (and submenu) and attach to the * HildonProgram. */ static void buildMenu(ApplicationState* app) { GtkMenu* menu = NULL; GtkWidget* miOpen = NULL; GtkWidget* miSave = NULL; GtkWidget* miSep1 = NULL; GtkWidget* miStyle = NULL; GtkWidget* subMenu = NULL; GtkWidget* mciShowToolbar = NULL; GtkWidget* miFullscreen = NULL; GtkWidget* miSep2 = NULL; GtkWidget* miQuit = NULL; miOpen = gtk_menu_item_new_with_label("Open"); miSave = gtk_menu_item_new_with_label("Save"); miSep1 = gtk_separator_menu_item_new(); miStyle = gtk_menu_item_new_with_label("Style"); mciShowToolbar = gtk_check_menu_item_new_with_label("Show toolbar"); miFullscreen = gtk_menu_item_new_with_label("Fullscreen"); miSep2 = gtk_separator_menu_item_new(); miQuit = gtk_menu_item_new_with_label("Quit"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mciShowToolbar), app->mainToolbarIsVisible); subMenu = buildSubMenu(app); gtk_menu_item_set_submenu(GTK_MENU_ITEM(miStyle), subMenu); menu = GTK_MENU(gtk_menu_new()); hildon_program_set_common_menu(app->program, menu); gtk_container_add(GTK_CONTAINER(menu), miOpen); gtk_container_add(GTK_CONTAINER(menu), miSave); gtk_container_add(GTK_CONTAINER(menu), miSep1); gtk_container_add(GTK_CONTAINER(menu), miStyle); gtk_container_add(GTK_CONTAINER(menu), mciShowToolbar); gtk_container_add(GTK_CONTAINER(menu), miFullscreen); gtk_container_add(GTK_CONTAINER(menu), miSep2); gtk_container_add(GTK_CONTAINER(menu), miQuit); g_signal_connect(G_OBJECT(miOpen), "activate", G_CALLBACK(cbActionOpen), app); g_signal_connect(G_OBJECT(miSave), "activate", G_CALLBACK(cbActionSave), app); g_signal_connect(G_OBJECT(miQuit), "activate", G_CALLBACK(cbActionQuit), app); g_signal_connect(G_OBJECT(mciShowToolbar), "toggled", G_CALLBACK(cbActionMainToolbarToggle), app); g_signal_connect(G_OBJECT(miFullscreen), "activate", G_CALLBACK(cbActionGoFullscreen), app); gtk_widget_show_all(GTK_WIDGET(menu)); } /** * MODIFIED * * Add support for GnomeVFS (it needs to be initialized before use) * and add support for the Pango markup feature of the GtkLabel * widget. */ int main(int argc, char** argv) { ApplicationState aState = {}; GtkWidget* label = NULL; GtkWidget* vbox = NULL; GtkWidget* mainToolbar = NULL; GtkWidget* findToolbar = NULL; /* Initialize the GnomeVFS (NEW). */ if(!gnome_vfs_init()) { g_error("Failed to initialize GnomeVFS-libraries, exiting\n"); } /* Initialize the GTK+ */ gtk_init(&argc, &argv); /* Setup the HildonProgram, HildonWindow and application name. */ aState.program = HILDON_PROGRAM(hildon_program_get_instance()); g_set_application_name("Hello Hildon!"); aState.window = HILDON_WINDOW(hildon_window_new()); hildon_program_add_window(aState.program, HILDON_WINDOW(aState.window)); /* Create the label widget, with Pango marked up content (NEW). */ label = gtk_label_new("Hello Hildon (with Hildon" "search and GnomeVFS " "and other trickstm)!"); /* Allow lines to wrap (NEW). */ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); /* Tell the GtkLabel widget to support the Pango markup (NEW). */ gtk_label_set_use_markup(GTK_LABEL(label), TRUE); /* Store the widget pointer into the application state so that the contents can be replaced when a file will be loaded (NEW). */ aState.textLabel = label; buildMenu(&aState); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(aState.window), vbox); gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0); mainToolbar = buildToolbar(&aState); findToolbar = buildFindToolbar(&aState); aState.mainToolbar = mainToolbar; aState.findToolbar = findToolbar; /* Connect the termination signals. */ g_signal_connect(G_OBJECT(aState.window), "delete-event", G_CALLBACK(cbEventDelete), &aState); g_signal_connect(G_OBJECT(aState.window), "destroy", G_CALLBACK(cbActionTopDestroy), &aState); /* Show all widgets that are contained by the Window. */ gtk_widget_show_all(GTK_WIDGET(aState.window)); /* Add the toolbars to the Hildon Window. */ hildon_window_add_toolbar(HILDON_WINDOW(aState.window), GTK_TOOLBAR(mainToolbar)); hildon_window_add_toolbar(HILDON_WINDOW(aState.window), GTK_TOOLBAR(findToolbar)); /* Register a callback to handle key presses. */ g_signal_connect(G_OBJECT(aState.window), "key_press_event", G_CALLBACK(cbKeyPressed), &aState); g_print("main: calling gtk_main\n"); gtk_main(); g_print("main: returned from gtk_main and exiting with success\n"); return EXIT_SUCCESS; }