The code in Listing A.1 is the C source for a basic multimedia player application. The user interface is largely constructed using Glade; see Listing A.2 for the contents of the MiniPlayer Glade file.
Listing A.1 MiniPlayer sample code
/****************************************************************************** * @file main.c * * @brief Source for MiniPlayer program in gtk+/C/glade  *****************************************************************************/ /* Includes */ #include <stdlib.h> #include <string.h> #include <assert.h> #include <gtk/gtk.h> #include <glade/glade.h> #include <alp/alp.h> #include <alp/media_session.h> #define GLADE_XML_FILE "player.glade" // These define the area of the screen set aside for the video display #define VIDEO_WIDTH 320 #define VIDEO_HEIGHT 210   // max 230 with just title bar and button bar #define VIDEO_X 0 #define VIDEO_Y 50   // just below the title bar static AlpMMRectangle fullSized = {VIDEO_X, VIDEO_Y, VIDEO_WIDTH, VIDEO_HEIGHT}; static AlpMMRectangle invisible = {VIDEO_X, VIDEO_Y, 0, 0}; #define NANOSECOND 1000000000LL // Globals GladeXML *gXML; // pointer to the Glade XML data AlpMMSessionID gSessionID = 0; // multimedia session ID. Zero indicates that                                 // we don't have a valid session. AlpMMDestID gLCDDestID = 0;  // the ID of the screen; we need this in                              // various spots when showing/hiding the video                              // image. char gFilename[256] = "";  // absolute path to the audio or video file to be                            // played. Empty string if nothing selected yet. int32_t gDurationSecs = 0; // length of selected audio/video track, in seconds. int64_t gCurrentPosition = 0; // current position within the track, in seconds. static void StatusStringForState(const AlpMMSessionState state,                                  char *statusString){ switch(state){ case ALP_MM_SESSION_NOT_INITIALIZED: strcpy(statusString, "Not initialized"); break; case ALP_MM_SESSION_READY: strcpy(statusString, "Ready"); break; case ALP_MM_SESSION_PREFETCHING: strcpy(statusString, "Buffering"); break; case ALP_MM_SESSION_PAUSED: strcpy(statusString, "Paused"); break; case ALP_MM_SESSION_STOPPED: strcpy(statusString, "Stopped"); break; case ALP_MM_SESSION_FINISHED: strcpy(statusString, "Playback complete"); break; case ALP_MM_SESSION_RUNNING: strcpy(statusString, "Playing"); break; default: sprintf(statusString, "State?: %x", state); break; } } static void ShowMessage(char *string){ GtkWidget *controlLabel = glade_xml_get_widget(gXML, "control_status"); gtk_label_set_text(GTK_LABEL(controlLabel), string); } static void ShowMessageWithStatus(char *string, alp_status_t status){ char buffer[80]; sprintf(buffer, "%s\n(%x)", string, status); ShowMessage(buffer); } static char *FilenameInPath(char *path){ char *filename; filename = strrchr(path, '/'); filename = filename ? filename + 1 : path; // advance to just past the slash,                                             // or start-of-string if no slashes return(filename); } static void SetButtonImage(char *buttonImageWidget, char *resourceImage){ // set the image on a button gtk_image_set_from_file(GTK_IMAGE(glade_xml_get_widget(gXML, buttonImageWidget)), alp_bundle_ref_ro_pathname(alp_bundle_ref_me(), resourceImage)); } static void UpdateTimeDisplay(int64_t position, int64_t duration){ char buffer[32]; int32_t positionSecs; gdouble dblPosition, dblDuration, fraction; if(gDurationSecs == 0){ gDurationSecs = duration/NANOSECOND; // convert to seconds sprintf(buffer, "0:%d", gDurationSecs); gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gXML, "duration_label")), buffer); } dblDuration = gDurationSecs; positionSecs = position/NANOSECOND; // convert to seconds sprintf(buffer, "0:%d", positionSecs); gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gXML, "position_label")), buffer); dblPosition = positionSecs; fraction = (dblPosition > 0 && dblDuration > 0) ? dblPosition / dblDuration : 0.0; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(glade_xml_get_widget(gXML, "progressbar")), fraction); } static alp_status_t SetImageDimensions(AlpMMRectangle *rect){ alp_status_t status; int length = sizeof(AlpMMRectangle); status = alp_media_property_set(gLCDDestID, ALP_MM_SESSION_DEST_RECT, ALP_MM_PROPERTY_CODE_RAW, rect, &length); return status; } void doSessionCallback(AlpMMSessionEvent *event, void *userData){ alp_status_t status; switch(event->event_code){ case ALP_MM_SESSION_EVENT_STATE_CHANGED: { AlpMMSessionState state; if(event->event_cause == ALP_MM_SESSION_EVENT_CAUSE_END_OF_STREAM){ // Use this to detect the end of playback, since elapsed time may // not equal track length // Force the stop, which cleans up the session. GtkWidget *stop_button = glade_xml_get_widget(gXML, "StopButton"); gtk_button_clicked(GTK_BUTTON(stop_button)); // simulate pressing                                                           //the Stop button } else { // event->session_ref is zero... so use global instead status = alp_media_session_get_state(gSessionID, &state); if(status == ALP_STATUS_OK){ char statusString[40]; StatusStringForState(state, statusString); ShowMessage(statusString); } else { ShowMessageWithStatus("state error", status); } } } break; case ALP_MM_SESSION_EVENT_WARNING: case ALP_MM_SESSION_EVENT_ERROR: { ShowMessage("Error/Warning"); } break; case ALP_MM_SESSION_EVENT_CURRENT_TIME: { gCurrentPosition = event->position; UpdateTimeDisplay(event->position, event->duration); } break; default: { char buffer[40]; sprintf(buffer, "Default case (%d, %d)", event->event_code, event->event_cause); ShowMessage(buffer); } break; } } static alp_status_t CreatePlaybackSession(char *resourcePath){ AlpMMSourceID sourceID = 0; AlpMMDestID dspDestID = 0; alp_status_t status; int32_t internalErrorCode = 0; int64_t endPoint; if((resourcePath == NULL) || (*resourcePath == '\0')) return(ALP_STATUS_SYS_ERR_PARAM); // create a session internalErrorCode = 1; status = alp_media_session_create(ALP_MM_SESSION_CLASS_PLAYBACK, &gSessionID); // add a source - a file, in this particular case if(status == ALP_MM_STATUS_OK){ internalErrorCode = 2; status = alp_media_session_register_callback(gSessionID, (AlpMMSessionCallbackFn)doSessionCallback, NULL); if(status == ALP_MM_STATUS_OK){ internalErrorCode = 3; status = alp_media_session_add_source(gSessionID, ALP_MM_TYPE_FILE, resourcePath, &sourceID); if(status == ALP_MM_STATUS_OK){ internalErrorCode = 4; status = alp_media_session_add_dest(gSessionID, LCDDEV, &gLCDDestID); if(status == ALP_MM_STATUS_OK){ internalErrorCode = 5; status = alp_media_session_add_dest(gSessionID, DSPDEV, &dspDestID); if(status == ALP_MM_STATUS_OK){ internalErrorCode = 6; status = alp_media_session_finalize(gSessionID); if(status == ALP_MM_STATUS_OK){ internalErrorCode = 7; // scale the video to the designated screen area status = SetImageDimensions(&fullSized); } } } } } if(status != ALP_MM_STATUS_OK){ // something failed along the way, but after we created a session alp_media_session_destroy(gSessionID); gSessionID = 0; } } // display the length of the track status = alp_media_property_get(gSessionID, ALP_MM_PROPERTY_TYPE_DEFAULT, ALP_MM_SESSION_END_TIME, ALP_MM_PROPERTY_CODE_INT64, &endPoint, NULL); // The following line is commented out because the above function currently // returns nonsense here // if(status != ALP_STATUS_OK) endPoint = 0LL; UpdateTimeDisplay(0LL, endPoint); return (status == ALP_MM_STATUS_OK) ? status : status + internalErrorCode; } void onMenubarShow(GtkWidget *menu, gpointer data){ if(gSessionID){ gboolean hasVideo; alp_media_property_get(gSessionID, ALP_MM_PROPERTY_TYPE_DEFAULT, ALP_MM_SOURCE_FILE_HAS_VIDEO, ALP_MM_PROPERTY_CODE_BOOL, &hasVideo, NULL); if(hasVideo){ AlpMMSessionState current_state; GtkWidget *play_pause_button = glade_xml_get_widget(gXML, "PlayPauseButton"); alp_media_session_get_state(gSessionID, ¤t_state); if(current_state == ALP_MM_SESSION_RUNNING){ alp_media_session_control(gSessionID, ALP_MM_SESSION_CTL_PAUSE); SetButtonImage("PlayPauseButtonImage", "Play.png"); } } } } void onPlayPauseButtonClicked(GtkWidget *button, gpointer data){ alp_status_t status = ALP_MM_STATUS_OK; GtkWidget *titleBar = glade_xml_get_widget(gXML, "titlebar"); char *filename; if(!gSessionID) status = CreatePlaybackSession(gFilename); if(status == ALP_MM_STATUS_OK){ AlpMMSessionState current_state; alp_media_session_get_state(gSessionID, ¤t_state); if(current_state != ALP_MM_SESSION_RUNNING){ alp_title_bar_set_label(titleBar, FilenameInPath(gFilename)); // show                                                       // them what is playing SetButtonImage("PlayPauseButtonImage", "Pause.png"); status = alp_media_session_control(gSessionID, ALP_MM_SESSION_CTL_RUN); } else { status = alp_media_session_control(gSessionID, ALP_MM_SESSION_CTL_PAUSE); SetButtonImage("PlayPauseButtonImage", "Play.png"); } } else { // toss up an alert telling the user that they need to select a file GtkWidget *alert = gtk_message_dialog_new( GTK_WINDOW(glade_xml_get_widget(gXML, "MainWindow")), GTK_DIALOG_MODAL+GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Select an audio or video file before playing."); gint result = gtk_dialog_run (GTK_DIALOG (alert)); gtk_widget_destroy (alert); } } void onFFButtonClicked(GtkWidget *button, gpointer data){ alp_status_t status; int64_t newPos; // get the current position newPos = gCurrentPosition + 10*NANOSECOND; status = alp_media_session_seek(gSessionID, ALP_MM_SESSION_SEEK_ORIGIN_BEGIN, newPos); // absolute position } void onRewindButtonClicked(GtkWidget *button, gpointer data){ alp_status_t status; int64_t newPos; newPos = gCurrentPosition - 10*NANOSECOND; if(newPos < 0LL) newPos = 0LL; status = alp_media_session_seek(gSessionID, ALP_MM_SESSION_SEEK_ORIGIN_BEGIN, newPos); // absolute position } void onStopButtonClicked(GtkWidget *button, gpointer data){ alp_status_t status; GtkWidget *titleBar = glade_xml_get_widget(gXML, "titlebar"); char statusString[32]; if(gSessionID){ char *appName; // pointer to the name of this app, obtained from the                      // bundle. status = alp_media_session_control(gSessionID, ALP_MM_SESSION_CTL_STOP); alp_media_session_destroy(gSessionID); gSessionID = 0; // grab the application's name from the bundle - it is the value of // the /application/name property appName = alp_bundle_property_value(alp_bundle_application(), "application", 0, "name"); alp_title_bar_set_label(titleBar, appName); // erase the name of what                                                  // was playing g_free(appName); SetButtonImage("PlayPauseButtonImage", "Play.png"); StatusStringForState(ALP_MM_SESSION_NOT_INITIALIZED, statusString); ShowMessage(statusString); UpdateTimeDisplay(0LL, 0LL); } } void onAboutMenuItemActivated(GtkMenuItem *menuItem, gpointer userData){ alp_about_dialog_run(GTK_WINDOW(gtk_widget_get_toplevel( GTK_WIDGET(menuItem)))); } // let them select from the list void onOpenMenuItemActivated(GtkMenuItem *menuItem, gpointer userData){ GtkWidget *dialog; gint result; dialog = alp_media_selector_dialog_new_with_buttons("Select one", GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(menuItem))), GTK_DIALOG_MODAL+GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); result = gtk_dialog_run (GTK_DIALOG (dialog)); switch (result){ case GTK_RESPONSE_OK: { char *filename; char buffer[40]; GtkWidget *stop_button = glade_xml_get_widget(gXML, "StopButton"); gchar **selections; // press "STOP" in case they are currently playing or paused gtk_button_clicked(GTK_BUTTON(stop_button)); // get their selection. Multiple selection isn't allowed; only the // first is of interest. selections = alp_media_selector_dialog_get_selections( ALP_MEDIA_SELECTOR_DIALOG(dialog)); strcpy(gFilename, *selections); sprintf(buffer, "Selected: %s", FilenameInPath(gFilename)); ShowMessage(buffer); } break; case GTK_RESPONSE_CANCEL: ShowMessage("Cancelled"); break; default: //do nothing break; } gtk_widget_destroy (dialog); } static void check_launch_codes(int argc, char *argv[], bool *isPrimary){ int i; *isPrimary = false; for(i = 1; i < argc; i++){ if(strcmp(argv[i], ALP_APP_PRIMARY) == 0){ *isPrimary = true; } } } static void CloseTheApp(GtkWidget* widget, gpointer user_data) { gtk_main_quit(); } void onAppExit(gpointer cbData) { // Destroy the session, if there is one if(gSessionID){ alp_media_session_destroy(gSessionID); gSessionID = 0; } gtk_main_quit(); // causes the app's main loop to exit } int alp_main(int argc, char *argv[]) { bool isPrimary; char **value; alp_status_t status; check_launch_codes(argc, argv, &isPrimary); if(isPrimary){ // init the system gtk_init(&argc, &argv); // create the main window gchar *resource_name = alp_bundle_ref_ro_pathname(alp_bundle_ref_me(), GLADE_XML_FILE); gXML = glade_xml_new(resource_name, NULL, NULL); g_free(resource_name); assert( gXML != NULL ); // hook all handlers glade_xml_signal_autoconnect(gXML); // hook the ALP exit handler alp_app_add_exit_handler(onAppExit, NULL); SetButtonImage("PlayPauseButtonImage", "Play.png"); SetButtonImage("StopButtonImage", "Stop.png"); SetButtonImage("FFButtonImage", "FF.png"); SetButtonImage("RewindButtonImage", "Rewind.png"); // point the Cataloger at the directories that contain our stuff status = alp_ms_cat_register_directory("/var/home/audio"); status = alp_ms_cat_register_directory("/var/home/video"); if(!gSessionID) CreatePlaybackSession(gFilename); // run gtk_main(); } return 0; }
Listing A.2 Glade file for MiniPlayer application
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> <!--Generated with glade3 3.2.1 on Tue Oct 2 16:59:18 2007 by gwilson@svgwilsonlnx--> <glade-interface> <requires lib="alp_max_glade"/> <widget class="GtkWindow" id="MainWindow"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <child> <widget class="GtkVBox" id="vbox1"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <child> <widget class="AlpTitleBar" id="titlebar"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="menu">maxmenubar1</property> <property name="label" translatable="yes">MiniPlayer</property> <child> <placeholder/> </child> </widget> <packing> <property name="expand">False</property> </packing> </child> <child> <widget class="GtkLabel" id="control_status"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> </widget> <packing> <property name="position">1</property> </packing> </child> <child> <widget class="GtkHBox" id="hbox1"> <property name="height_request">20</property> <property name="visible">True</property> <property name="sensitive">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <child> <widget class="GtkLabel" id="position_label"> <property name="height_request">20</property> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> </widget> </child> <child> <widget class="GtkProgressBar" id="progressbar"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="text" translatable="yes"></property> </widget> <packing> <property name="position">1</property> </packing> </child> <child> <widget class="GtkLabel" id="duration_label"> <property name="height_request">20</property> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> </widget> <packing> <property name="position">2</property> </packing> </child> </widget> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">2</property> </packing> </child> <child> <widget class="GtkHButtonBox" id="hbuttonbox1"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="border_width">4</property> <property name="spacing">4</property> <child> <widget class="GtkButton" id="PlayPauseButton"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <signal name="clicked" handler="onPlayPauseButtonClicked"/> <child> <widget class="GtkImage" id="PlayPauseButtonImage"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="stock">gtk-missing-image</property> </widget> </child> </widget> </child> <child> <widget class="GtkButton" id="StopButton"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <signal name="clicked" handler="onStopButtonClicked"/> <child> <widget class="GtkImage" id="StopButtonImage"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="stock">gtk-missing-image</property> </widget> </child> </widget> <packing> <property name="position">1</property> </packing> </child> <child> <widget class="GtkButton" id="RewindButton"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <signal name="clicked" handler="onRewindButtonClicked"/> <child> <widget class="GtkImage" id="RewindButtonImage"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="stock">gtk-missing-image</property> </widget> </child> </widget> <packing> <property name="position">2</property> </packing> </child> <child> <widget class="GtkButton" id="FFButton"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <signal name="clicked" handler="onFFButtonClicked"/> <child> <widget class="GtkImage" id="FFButtonImage"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="stock">gtk-missing-image</property> </widget> </child> </widget> <packing> <property name="position">3</property> </packing> </child> </widget> <packing> <property name="expand">False</property> <property name="position">3</property> </packing> </child> </widget> </child> </widget> <widget class="AlpMenuBar" id="maxmenubar1"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <signal name="show" handler="onMenubarShow"/> <child> <widget class="GtkMenuItem" id="menuitem1"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="label" translatable="yes">File</property> <property name="use_underline">True</property> <child> <widget class="GtkMenu" id="menu1"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <child> <widget class="GtkMenuItem" id="openmenuitem"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="label" translatable="yes">Open</property> <property name="use_underline">True</property> <signal name="activate" handler="onOpenMenuItemActivated"/> </widget> </child> </widget> </child> </widget> </child> <child> <widget class="GtkMenuItem" id="menuitem4"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="label" translatable="yes">Help</property> <property name="use_underline">True</property> <child> <widget class="GtkMenu" id="menu3"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <child> <widget class="GtkMenuItem" id="aboutmenuitem"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="label" translatable="yes">About</property> <property name="use_underline">True</property> <signal name="activate" handler="onAboutMenuItemActivated"/> </widget> </child> </widget> </child> </widget> </child> </widget> </glade-interface>










