560 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			560 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Change Display
 | |
|  *
 | |
|  * Demonstrates migrating a window between different displays.
 | |
|  * A display is a mouse and keyboard with some number of
 | |
|  * associated monitors. The neat thing about having multiple
 | |
|  * displays is that they can be on a completely separate
 | |
|  * computers, as long as there is a network connection to the
 | |
|  * computer where the application is running.
 | |
|  *
 | |
|  * Only some of the windowing systems where GTK+ runs have the
 | |
|  * concept of multiple displays. (The X Window System is the
 | |
|  * main example.) Other windowing systems can only handle one
 | |
|  * keyboard and mouse, and combine all monitors into
 | |
|  * a single display.
 | |
|  *
 | |
|  * This is a moderately complex example, and demonstrates:
 | |
|  *
 | |
|  *  - Tracking the currently open displays
 | |
|  *
 | |
|  *  - Changing the display for a window
 | |
|  *
 | |
|  *  - Letting the user choose a window by clicking on it
 | |
|  *
 | |
|  *  - Using GtkListStore and GtkTreeView
 | |
|  *
 | |
|  *  - Using GtkDialog
 | |
|  */
 | |
| #include <string.h>
 | |
| #include <glib/gi18n.h>
 | |
| #include <gtk/gtk.h>
 | |
| 
 | |
| /* The ChangeDisplayInfo structure corresponds to a toplevel window and
 | |
|  * holds pointers to widgets inside the toplevel window along with other
 | |
|  * information about the contents of the window.
 | |
|  * This is a common organizational structure in real applications.
 | |
|  */
 | |
| typedef struct _ChangeDisplayInfo ChangeDisplayInfo;
 | |
| 
 | |
| struct _ChangeDisplayInfo
 | |
| {
 | |
|   GtkWidget *window;
 | |
|   GtkSizeGroup *size_group;
 | |
| 
 | |
|   GtkTreeModel *display_model;
 | |
| 
 | |
|   GdkDisplay *current_display;
 | |
| };
 | |
| 
 | |
| /* These enumerations provide symbolic names for the columns
 | |
|  * in the two GtkListStore models.
 | |
|  */
 | |
| enum
 | |
| {
 | |
|   DISPLAY_COLUMN_NAME,
 | |
|   DISPLAY_COLUMN_DISPLAY,
 | |
|   DISPLAY_NUM_COLUMNS
 | |
| };
 | |
| 
 | |
| enum
 | |
| {
 | |
|   SCREEN_COLUMN_NUMBER,
 | |
|   SCREEN_COLUMN_SCREEN,
 | |
|   SCREEN_NUM_COLUMNS
 | |
| };
 | |
| 
 | |
| /* Finds the toplevel window under the mouse pointer, if any.
 | |
|  */
 | |
| static GtkWidget *
 | |
| find_toplevel_at_pointer (GdkDisplay *display)
 | |
| {
 | |
|   GdkWindow *pointer_window;
 | |
|   GtkWidget *widget = NULL;
 | |
| 
 | |
|   pointer_window = gdk_device_get_window_at_position (gtk_get_current_event_device (),
 | |
|                                                       NULL, NULL);
 | |
| 
 | |
|   /* The user data field of a GdkWindow is used to store a pointer
 | |
|    * to the widget that created it.
 | |
|    */
 | |
|   if (pointer_window)
 | |
|     {
 | |
|       gpointer widget_ptr;
 | |
|       gdk_window_get_user_data (pointer_window, &widget_ptr);
 | |
|       widget = widget_ptr;
 | |
|     }
 | |
| 
 | |
|   return widget ? gtk_widget_get_toplevel (widget) : NULL;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| button_release_event_cb (GtkWidget       *widget,
 | |
|                          GdkEventButton  *event,
 | |
|                          gboolean        *clicked)
 | |
| {
 | |
|   *clicked = TRUE;
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /* Asks the user to click on a window, then waits for them click
 | |
|  * the mouse. When the mouse is released, returns the toplevel
 | |
|  * window under the pointer, or NULL, if there is none.
 | |
|  */
 | |
| static GtkWidget *
 | |
| query_for_toplevel (GdkScreen  *screen,
 | |
|                     const char *prompt)
 | |
| {
 | |
|   GdkDisplay *display = gdk_screen_get_display (screen);
 | |
|   GtkWidget *popup, *label, *frame;
 | |
|   GdkCursor *cursor;
 | |
|   GtkWidget *toplevel = NULL;
 | |
| 
 | |
|   popup = gtk_window_new (GTK_WINDOW_POPUP);
 | |
|   gtk_window_set_screen (GTK_WINDOW (popup), screen);
 | |
|   gtk_window_set_modal (GTK_WINDOW (popup), TRUE);
 | |
|   gtk_window_set_position (GTK_WINDOW (popup), GTK_WIN_POS_CENTER);
 | |
| 
 | |
|   frame = gtk_frame_new (NULL);
 | |
|   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
 | |
|   gtk_container_add (GTK_CONTAINER (popup), frame);
 | |
| 
 | |
|   label = gtk_label_new (prompt);
 | |
|   g_object_set (label, "margin", 10, NULL);
 | |
|   gtk_container_add (GTK_CONTAINER (frame), label);
 | |
| 
 | |
|   gtk_widget_show_all (popup);
 | |
|   cursor = gdk_cursor_new_from_name (display, "crosshair");
 | |
| 
 | |
|   if (gdk_device_grab (gtk_get_current_event_device (),
 | |
|                        gtk_widget_get_window (popup),
 | |
|                        GDK_OWNERSHIP_NONE,
 | |
|                        FALSE,
 | |
|                        GDK_BUTTON_RELEASE_MASK,
 | |
|                        cursor,
 | |
|                        GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS)
 | |
|     {
 | |
|       gboolean clicked = FALSE;
 | |
| 
 | |
|       g_signal_connect (popup, "button-release-event",
 | |
|                         G_CALLBACK (button_release_event_cb), &clicked);
 | |
| 
 | |
|       /* Process events until clicked is set by button_release_event_cb.
 | |
|        * We pass in may_block=TRUE since we want to wait if there
 | |
|        * are no events currently.
 | |
|        */
 | |
|       while (!clicked)
 | |
|         g_main_context_iteration (NULL, TRUE);
 | |
| 
 | |
|       toplevel = find_toplevel_at_pointer (gdk_screen_get_display (screen));
 | |
|       if (toplevel == popup)
 | |
|         toplevel = NULL;
 | |
|     }
 | |
| 
 | |
|   g_object_unref (cursor);
 | |
|   gtk_widget_destroy (popup);
 | |
|   gdk_flush ();                 /* Really release the grab */
 | |
| 
 | |
|   return toplevel;
 | |
| }
 | |
| 
 | |
| /* Prompts the user for a toplevel window to move, and then moves
 | |
|  * that window to the currently selected display
 | |
|  */
 | |
| static void
 | |
| query_change_display (ChangeDisplayInfo *info)
 | |
| {
 | |
|   GdkScreen *screen = gtk_widget_get_screen (info->window);
 | |
|   GtkWidget *toplevel;
 | |
| 
 | |
|   toplevel = query_for_toplevel (screen,
 | |
|                                  "Please select the toplevel\n"
 | |
|                                  "to move to the new screen");
 | |
| 
 | |
|   if (toplevel)
 | |
|     gtk_window_set_screen (GTK_WINDOW (toplevel), gdk_display_get_default_screen (info->current_display));
 | |
|   else
 | |
|     gdk_display_beep (gdk_screen_get_display (screen));
 | |
| }
 | |
| 
 | |
| /* Called when the user clicks on a button in our dialog or
 | |
|  * closes the dialog through the window manager. Unless the
 | |
|  * "Change" button was clicked, we destroy the dialog.
 | |
|  */
 | |
| static void
 | |
| response_cb (GtkDialog         *dialog,
 | |
|              gint               response_id,
 | |
|              ChangeDisplayInfo *info)
 | |
| {
 | |
|   if (response_id == GTK_RESPONSE_OK)
 | |
|     query_change_display (info);
 | |
|   else
 | |
|     gtk_widget_destroy (GTK_WIDGET (dialog));
 | |
| }
 | |
| 
 | |
| /* Called when the user clicks on "Open..." in the display
 | |
|  * frame. Prompts for a new display, and then opens a connection
 | |
|  * to that display.
 | |
|  */
 | |
| static void
 | |
| open_display_cb (GtkWidget         *button,
 | |
|                  ChangeDisplayInfo *info)
 | |
| {
 | |
|   GtkWidget *content_area;
 | |
|   GtkWidget *dialog;
 | |
|   GtkWidget *display_entry;
 | |
|   GtkWidget *dialog_label;
 | |
|   gchar *new_screen_name = NULL;
 | |
|   GdkDisplay *result = NULL;
 | |
| 
 | |
|   dialog = gtk_dialog_new_with_buttons ("Open Display",
 | |
|                                         GTK_WINDOW (info->window),
 | |
|                                         GTK_DIALOG_MODAL,
 | |
|                                         _("_Cancel"), GTK_RESPONSE_CANCEL,
 | |
|                                         _("_OK"), GTK_RESPONSE_OK,
 | |
|                                         NULL);
 | |
| 
 | |
|   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 | |
|   display_entry = gtk_entry_new ();
 | |
|   gtk_entry_set_activates_default (GTK_ENTRY (display_entry), TRUE);
 | |
|   dialog_label =
 | |
|     gtk_label_new ("Please enter the name of\nthe new display\n");
 | |
| 
 | |
|   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
 | |
| 
 | |
|   gtk_container_add (GTK_CONTAINER (content_area), dialog_label);
 | |
|   gtk_container_add (GTK_CONTAINER (content_area), display_entry);
 | |
| 
 | |
|   gtk_widget_grab_focus (display_entry);
 | |
|   gtk_widget_show_all (gtk_bin_get_child (GTK_BIN (dialog)));
 | |
| 
 | |
|   while (!result)
 | |
|     {
 | |
|       gint response_id = gtk_dialog_run (GTK_DIALOG (dialog));
 | |
|       if (response_id != GTK_RESPONSE_OK)
 | |
|         break;
 | |
| 
 | |
|       new_screen_name = gtk_editable_get_chars (GTK_EDITABLE (display_entry),
 | |
|                                                 0, -1);
 | |
| 
 | |
|       if (strcmp (new_screen_name, "") != 0)
 | |
|         {
 | |
|           result = gdk_display_open (new_screen_name);
 | |
|           if (!result)
 | |
|             {
 | |
|               gchar *error_msg =
 | |
|                 g_strdup_printf  ("Can't open display:\n\t%s\nplease try another one\n",
 | |
|                                   new_screen_name);
 | |
|               gtk_label_set_text (GTK_LABEL (dialog_label), error_msg);
 | |
|               g_free (error_msg);
 | |
|             }
 | |
| 
 | |
|           g_free (new_screen_name);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   gtk_widget_destroy (dialog);
 | |
| }
 | |
| 
 | |
| /* Called when the user clicks on the "Close" button in the
 | |
|  * "Display" frame. Closes the selected display.
 | |
|  */
 | |
| static void
 | |
| close_display_cb (GtkWidget         *button,
 | |
|                   ChangeDisplayInfo *info)
 | |
| {
 | |
|   if (info->current_display)
 | |
|     gdk_display_close (info->current_display);
 | |
| }
 | |
| 
 | |
| /* Called when the selected row in the display list changes.
 | |
|  * Updates info->current_display, then refills the list of
 | |
|  * screens.
 | |
|  */
 | |
| static void
 | |
| display_changed_cb (GtkTreeSelection  *selection,
 | |
|                     ChangeDisplayInfo *info)
 | |
| {
 | |
|   GtkTreeModel *model;
 | |
|   GtkTreeIter iter;
 | |
| 
 | |
|   if (info->current_display)
 | |
|     g_object_unref (info->current_display);
 | |
|   if (gtk_tree_selection_get_selected (selection, &model, &iter))
 | |
|     gtk_tree_model_get (model, &iter,
 | |
|                         DISPLAY_COLUMN_DISPLAY, &info->current_display,
 | |
|                         -1);
 | |
|   else
 | |
|     info->current_display = NULL;
 | |
| }
 | |
| 
 | |
| /* This function is used both for creating the "Display" and
 | |
|  * "Screen" frames, since they have a similar structure. The
 | |
|  * caller hooks up the right context for the value returned
 | |
|  * in tree_view, and packs any relevant buttons into button_vbox.
 | |
|  */
 | |
| static void
 | |
| create_frame (ChangeDisplayInfo *info,
 | |
|               const char        *title,
 | |
|               GtkWidget        **frame,
 | |
|               GtkWidget        **tree_view,
 | |
|               GtkWidget        **button_vbox)
 | |
| {
 | |
|   GtkTreeSelection *selection;
 | |
|   GtkWidget *scrollwin;
 | |
|   GtkWidget *hbox;
 | |
| 
 | |
|   *frame = gtk_frame_new (title);
 | |
| 
 | |
|   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
 | |
|   gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
 | |
|   gtk_container_add (GTK_CONTAINER (*frame), hbox);
 | |
| 
 | |
|   scrollwin = gtk_scrolled_window_new (NULL, NULL);
 | |
|   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin),
 | |
|                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 | |
|   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin),
 | |
|                                        GTK_SHADOW_IN);
 | |
|   gtk_box_pack_start (GTK_BOX (hbox), scrollwin, TRUE, TRUE, 0);
 | |
| 
 | |
|   *tree_view = gtk_tree_view_new ();
 | |
|   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (*tree_view), FALSE);
 | |
|   gtk_container_add (GTK_CONTAINER (scrollwin), *tree_view);
 | |
| 
 | |
|   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (*tree_view));
 | |
|   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
 | |
| 
 | |
|   *button_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
 | |
|   gtk_box_pack_start (GTK_BOX (hbox), *button_vbox, FALSE, FALSE, 0);
 | |
| 
 | |
|   if (!info->size_group)
 | |
|     info->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 | |
| 
 | |
|   gtk_size_group_add_widget (GTK_SIZE_GROUP (info->size_group), *button_vbox);
 | |
| }
 | |
| 
 | |
| /* If we have a stack of buttons, it often looks better if their contents
 | |
|  * are left-aligned, rather than centered. This function creates a button
 | |
|  * and left-aligns it contents.
 | |
|  */
 | |
| GtkWidget *
 | |
| left_align_button_new (const char *label)
 | |
| {
 | |
|   GtkWidget *button = gtk_button_new_with_mnemonic (label);
 | |
|   GtkWidget *child = gtk_bin_get_child (GTK_BIN (button));
 | |
| 
 | |
|   gtk_widget_set_halign (child, GTK_ALIGN_START);
 | |
|   gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
 | |
| 
 | |
|   return button;
 | |
| }
 | |
| 
 | |
| /* Creates the "Display" frame in the main window.
 | |
|  */
 | |
| GtkWidget *
 | |
| create_display_frame (ChangeDisplayInfo *info)
 | |
| {
 | |
|   GtkWidget *frame;
 | |
|   GtkWidget *tree_view;
 | |
|   GtkWidget *button_vbox;
 | |
|   GtkTreeViewColumn *column;
 | |
|   GtkTreeSelection *selection;
 | |
|   GtkWidget *button;
 | |
| 
 | |
|   create_frame (info, "Display", &frame, &tree_view, &button_vbox);
 | |
| 
 | |
|   button = left_align_button_new ("_Open...");
 | |
|   g_signal_connect (button, "clicked",  G_CALLBACK (open_display_cb), info);
 | |
|   gtk_box_pack_start (GTK_BOX (button_vbox), button, FALSE, FALSE, 0);
 | |
| 
 | |
|   button = left_align_button_new ("_Close");
 | |
|   g_signal_connect (button, "clicked",  G_CALLBACK (close_display_cb), info);
 | |
|   gtk_box_pack_start (GTK_BOX (button_vbox), button, FALSE, FALSE, 0);
 | |
| 
 | |
|   info->display_model = (GtkTreeModel *)gtk_list_store_new (DISPLAY_NUM_COLUMNS,
 | |
|                                                             G_TYPE_STRING,
 | |
|                                                             GDK_TYPE_DISPLAY);
 | |
| 
 | |
|   gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), info->display_model);
 | |
| 
 | |
|   column = gtk_tree_view_column_new_with_attributes ("Name",
 | |
|                                                      gtk_cell_renderer_text_new (),
 | |
|                                                      "text", DISPLAY_COLUMN_NAME,
 | |
|                                                      NULL);
 | |
|   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
 | |
| 
 | |
|   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
 | |
|   g_signal_connect (selection, "changed",
 | |
|                     G_CALLBACK (display_changed_cb), info);
 | |
| 
 | |
|   return frame;
 | |
| }
 | |
| 
 | |
| /* Called when one of the currently open displays is closed.
 | |
|  * Remove it from our list of displays.
 | |
|  */
 | |
| static void
 | |
| display_closed_cb (GdkDisplay        *display,
 | |
|                    gboolean           is_error,
 | |
|                    ChangeDisplayInfo *info)
 | |
| {
 | |
|   GtkTreeIter iter;
 | |
|   gboolean valid;
 | |
| 
 | |
|   for (valid = gtk_tree_model_get_iter_first (info->display_model, &iter);
 | |
|        valid;
 | |
|        valid = gtk_tree_model_iter_next (info->display_model, &iter))
 | |
|     {
 | |
|       GdkDisplay *tmp_display;
 | |
| 
 | |
|       gtk_tree_model_get (info->display_model, &iter,
 | |
|                           DISPLAY_COLUMN_DISPLAY, &tmp_display,
 | |
|                           -1);
 | |
|       if (tmp_display == display)
 | |
|         {
 | |
|           gtk_list_store_remove (GTK_LIST_STORE (info->display_model), &iter);
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Adds a new display to our list of displays, and connects
 | |
|  * to the "closed" signal so that we can remove it from the
 | |
|  * list of displays again.
 | |
|  */
 | |
| static void
 | |
| add_display (ChangeDisplayInfo *info,
 | |
|              GdkDisplay        *display)
 | |
| {
 | |
|   const gchar *name = gdk_display_get_name (display);
 | |
|   GtkTreeIter iter;
 | |
| 
 | |
|   gtk_list_store_append (GTK_LIST_STORE (info->display_model), &iter);
 | |
|   gtk_list_store_set (GTK_LIST_STORE (info->display_model), &iter,
 | |
|                       DISPLAY_COLUMN_NAME, name,
 | |
|                       DISPLAY_COLUMN_DISPLAY, display,
 | |
|                       -1);
 | |
| 
 | |
|   g_signal_connect (display, "closed",
 | |
|                     G_CALLBACK (display_closed_cb), info);
 | |
| }
 | |
| 
 | |
| /* Called when a new display is opened
 | |
|  */
 | |
| static void
 | |
| display_opened_cb (GdkDisplayManager *manager,
 | |
|                    GdkDisplay        *display,
 | |
|                    ChangeDisplayInfo *info)
 | |
| {
 | |
|   add_display (info, display);
 | |
| }
 | |
| 
 | |
| /* Adds all currently open displays to our list of displays,
 | |
|  * and set up a signal connection so that we'll be notified
 | |
|  * when displays are opened in the future as well.
 | |
|  */
 | |
| static void
 | |
| initialize_displays (ChangeDisplayInfo *info)
 | |
| {
 | |
|   GdkDisplayManager *manager = gdk_display_manager_get ();
 | |
|   GSList *displays = gdk_display_manager_list_displays (manager);
 | |
|   GSList *tmp_list;
 | |
| 
 | |
|   for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next)
 | |
|     add_display (info, tmp_list->data);
 | |
| 
 | |
|   g_slist_free (tmp_list);
 | |
| 
 | |
|   g_signal_connect (manager, "display-opened",
 | |
|                     G_CALLBACK (display_opened_cb), info);
 | |
| }
 | |
| 
 | |
| /* Cleans up when the toplevel is destroyed; we remove the
 | |
|  * connections we use to track currently open displays, then
 | |
|  * free the ChangeDisplayInfo structure.
 | |
|  */
 | |
| static void
 | |
| destroy_info (ChangeDisplayInfo *info)
 | |
| {
 | |
|   GdkDisplayManager *manager = gdk_display_manager_get ();
 | |
|   GSList *displays = gdk_display_manager_list_displays (manager);
 | |
|   GSList *tmp_list;
 | |
| 
 | |
|   g_signal_handlers_disconnect_by_func (manager,
 | |
|                                         display_opened_cb,
 | |
|                                         info);
 | |
| 
 | |
|   for (tmp_list = displays; tmp_list; tmp_list = tmp_list->next)
 | |
|     g_signal_handlers_disconnect_by_func (tmp_list->data,
 | |
|                                           display_closed_cb,
 | |
|                                           info);
 | |
| 
 | |
|   g_slist_free (tmp_list);
 | |
| 
 | |
|   g_object_unref (info->size_group);
 | |
|   g_object_unref (info->display_model);
 | |
| 
 | |
|   if (info->current_display)
 | |
|     g_object_unref (info->current_display);
 | |
| 
 | |
|   g_free (info);
 | |
| }
 | |
| 
 | |
| static void
 | |
| destroy_cb (GObject            *object,
 | |
|             ChangeDisplayInfo **info)
 | |
| {
 | |
|   destroy_info (*info);
 | |
|   *info = NULL;
 | |
| }
 | |
| 
 | |
| /* Main entry point. If the dialog for this demo doesn't yet exist, creates
 | |
|  * it. Otherwise, destroys it.
 | |
|  */
 | |
| GtkWidget *
 | |
| do_changedisplay (GtkWidget *do_widget)
 | |
| {
 | |
|   static ChangeDisplayInfo *info = NULL;
 | |
| 
 | |
|   if (!info)
 | |
|     {
 | |
|       GtkWidget *content_area;
 | |
|       GtkWidget *vbox;
 | |
|       GtkWidget *frame;
 | |
| 
 | |
|       info = g_new0 (ChangeDisplayInfo, 1);
 | |
| 
 | |
|       info->window = gtk_dialog_new_with_buttons ("Change Display",
 | |
|                                                   GTK_WINDOW (do_widget),
 | |
|                                                   0,
 | |
|                                                   "Close", GTK_RESPONSE_CLOSE,
 | |
|                                                   "Change", GTK_RESPONSE_OK,
 | |
|                                                   NULL);
 | |
| 
 | |
|       gtk_window_set_default_size (GTK_WINDOW (info->window), 300, 400);
 | |
| 
 | |
|       g_signal_connect (info->window, "response",
 | |
|                         G_CALLBACK (response_cb), info);
 | |
|       g_signal_connect (info->window, "destroy",
 | |
|                         G_CALLBACK (destroy_cb), &info);
 | |
| 
 | |
|       content_area = gtk_dialog_get_content_area (GTK_DIALOG (info->window));
 | |
| 
 | |
|       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
 | |
|       gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
 | |
|       gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
 | |
| 
 | |
|       frame = create_display_frame (info);
 | |
|       gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
 | |
| 
 | |
|       initialize_displays (info);
 | |
| 
 | |
|       gtk_widget_show_all (info->window);
 | |
|       return info->window;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       gtk_widget_destroy (info->window);
 | |
|       return NULL;
 | |
|     }
 | |
| }
 | 
