+static void
+set_copy_action (struct psppire_output_view *view,
+ gboolean state)
+{
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view->output));
+ GAction *copy_action = g_action_map_lookup_action (G_ACTION_MAP (toplevel),
+ "copy");
+ g_object_set (copy_action,
+ "enabled", state,
+ NULL);
+}
+
+static void
+clear_selection (struct psppire_output_view *view)
+{
+ if (view == NULL)
+ return;
+ struct output_view_item *item = find_selected_item (view);
+ if (item == NULL)
+ return;
+ set_copy_action (view, FALSE);
+ GtkWidget *widget = GTK_WIDGET (item->drawing_area);
+ if (GTK_IS_WIDGET (widget))
+ {
+ gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_SELECTED);
+ gtk_widget_queue_draw (widget);
+ }
+}
+
+static gboolean
+off_item_button_press_event_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ struct psppire_output_view *view)
+{
+ /* buttontime is set by button_press_event_cb
+ If our event->time is equal to the time from the
+ button_press_event_cb, then we handle the same event.
+ In that case we must not clear the selection because
+ it was just set by button_press_event_cb from the item */
+ if (event->time != view->buttontime)
+ clear_selection (view);
+ return FALSE; /* Forward the event -> DragNDrop */
+}
+
+static gboolean
+button_press_event_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ struct psppire_output_view *view)
+{
+ view->buttontime = event->time;
+ clear_selection (view);
+ set_copy_action (view, TRUE);
+ gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
+ gtk_widget_queue_draw (widget);
+ return FALSE; /* Forward Event -> off_item will trigger */
+}
+
+static void
+drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint target_type, guint time,
+ struct psppire_output_view *view)
+{
+ view->selected_item = find_selected_item (view);
+ clipboard_get_cb (NULL, selection_data, target_type, view);
+}
+
+static void
+create_drawing_area (struct psppire_output_view *view,
+ GtkWidget *drawing_area, struct xr_fsm *r,
+ int tw, int th, const struct output_item *item)
+{
+ g_object_set_data_full (G_OBJECT (drawing_area),
+ "fsm", r, free_fsm);
+ g_signal_connect (drawing_area, "button-press-event",
+ G_CALLBACK (button_press_event_cb), view);
+ gtk_widget_add_events (drawing_area, GDK_BUTTON_PRESS_MASK);
+
+ { /* Drag and Drop */
+ GtkTargetList *tl = build_target_list (item);
+ g_assert (tl);
+ gtk_drag_source_set (drawing_area, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
+ gtk_drag_source_set_target_list (drawing_area, tl);
+ gtk_target_list_unref (tl);
+ g_signal_connect (drawing_area, "drag-data-get",
+ G_CALLBACK (drag_data_get_cb), view);
+ }
+ GtkStyleContext *context = gtk_widget_get_style_context (drawing_area);
+ gtk_style_context_add_class (context,
+ GTK_STYLE_CLASS_VIEW);
+ g_signal_connect (drawing_area, "draw",
+ G_CALLBACK (draw_callback), view);
+
+ gtk_widget_set_size_request (drawing_area, tw, th);
+ gint xpos = get_xpos (view, tw);
+
+ gtk_layout_put (view->output, drawing_area, xpos, view->y);
+
+ gtk_widget_show (drawing_area);
+}
+
+static void
+rerender (struct psppire_output_view *view)
+{
+ struct output_view_item *item;
+ GdkWindow *gdkw = gtk_widget_get_window (GTK_WIDGET (view->output));
+
+ if (!view->n_items || ! gdkw)
+ return;
+
+ if (!view->style)
+ view->style = get_xr_fsm_style (view);
+
+ GdkWindow *win = gtk_layout_get_bin_window (view->output);
+ cairo_region_t *region = gdk_window_get_visible_region (win);
+ GdkDrawingContext *ctx = gdk_window_begin_draw_frame (win, region);
+ cairo_t *cr = gdk_drawing_context_get_cairo_context (ctx);
+
+ view->y = 0;
+ view->max_width = 0;
+ for (item = view->items; item < &view->items[view->n_items]; item++)
+ {
+ struct xr_fsm *r;
+ GtkAllocation alloc;
+ int tw, th;
+
+ if (view->y > 0)
+ view->y += view->object_spacing;
+
+ if (is_group_open_item (item->item))
+ continue;
+
+ r = xr_fsm_create (item->item, view->style, cr);
+ if (r == NULL)
+ {
+ g_warn_if_reached ();
+ continue;
+ }
+
+ xr_fsm_measure (r, cr, &tw, &th);
+
+ gint xpos = get_xpos (view, tw);
+
+ if (!item->drawing_area)
+ {
+ item->drawing_area = gtk_drawing_area_new ();
+ create_drawing_area (view, item->drawing_area, r, tw, th, item->item);
+ }
+ else
+ {
+ g_object_set_data_full (G_OBJECT (item->drawing_area),
+ "fsm", r, free_fsm);
+ gtk_widget_set_size_request (item->drawing_area, tw, th);
+ gtk_layout_move (view->output, item->drawing_area, xpos, view->y);
+ }
+
+ {
+ gint minw;
+ gint minh;
+ /* This code probably doesn't bring us anthing, but Gtk
+ shows warnings if get_preferred_width/height is not
+ called before the size_allocate below is called. */
+ gtk_widget_get_preferred_width (item->drawing_area, &minw, NULL);
+ gtk_widget_get_preferred_height (item->drawing_area, &minh, NULL);
+ if (th > minh) th = minh;
+ if (tw > minw) tw = minw;
+ }
+ alloc.x = xpos;
+ alloc.y = view->y;
+ alloc.width = tw;
+ alloc.height = th;
+
+ gtk_widget_size_allocate (item->drawing_area, &alloc);
+
+ if (view->max_width < tw)
+ view->max_width = tw;
+ view->y += th;
+ }
+
+ gtk_layout_set_size (view->output,
+ view->max_width + view->object_spacing,
+ view->y + view->object_spacing);
+
+ gdk_window_end_draw_frame (win, ctx);
+ cairo_region_destroy (region);