Esempio n. 1
0
/* Get a new thumbnail region of GtkTreeView widget in which event
falls.  It is an error to call this function if !in_thumbnail
(widget, event).  */
static GdkRegion *
thumbnail_region (GtkWidget *widget, GdkEventMotion *event)
{
    g_assert (GTK_IS_TREE_VIEW (widget));

    g_assert (in_thumbnail (widget, event));

    GtkTreePath *tp = thumbnail_path (widget, event);
    g_assert (tp != NULL);

    /* Rectangle of region to be returned.  */
    GdkRectangle tn_rect;

    /* If over the input image thumbnail, return the input thumbnail region, */
    GtkTreeViewColumn *tc = NULL;	/* Thumbnail column we are over.  */
    if ( in_input_thumbnail (widget, event) ) {
        tc = input_thumbnail_column (widget, event);
    }

    gtk_tree_view_get_cell_area (GTK_TREE_VIEW (widget), tp, tc, &tn_rect);

    /* Here we depend on the fact that the thumbnails are packed at the
    beginning of the cell horizontally, and centered in the cell
    vertically (FIXME: find a way to verify this with assertions).  */
    GdkRectangle itn_rect;		/* Image thumbnail rectangle.  */
    /* FIXME: fix this border hackery to be precise somehow.  */
    itn_rect.x = tn_rect.x + 1;	/* There is probably a small border so +1.  */
    itn_rect.y = tn_rect.y + 1;
    itn_rect.width = THUMB_SIZE;
    itn_rect.height = THUMB_SIZE;

    return gdk_region_rectangle (&itn_rect);
}
Esempio n. 2
0
static gboolean
in_input_thumbnail (GtkWidget *widget, GdkEventMotion *event)
{
    g_assert (GTK_IS_TREE_VIEW (widget));

    gboolean result = FALSE;	/* Result to be returned. */

    GtkTreePath *tp = thumbnail_path (widget, event);

    if ( tp == NULL ) {
        return FALSE;		/* Pointer is not over a filled in row.  */
    }

    /* Check if we are over the input image thumbnail.  */
    GtkTreeViewColumn *itc = input_thumbnail_column (widget, event);
    GdkRectangle itnc_rect;	/* Input thumbnail cell rectangle.  */
    gtk_tree_view_get_cell_area (GTK_TREE_VIEW (widget), tp, itc, &itnc_rect);
    /* Here we depend on the fact that the input thumbnail is packed at
    the beginning of the cell horizontally, and centered in the cell
    vertically (FIXME: find a way to verify this with assertions).  */
    GdkRectangle itn_rect;	/* Input thumbnail rectangle.  */
    /* FIXME: fix this border hackery to be precise somehow.  */
    itn_rect.x = itnc_rect.x + 1;	/* There is probably a small border so +1.  */
    itn_rect.y = itnc_rect.y + 1;
    itn_rect.width = THUMB_SIZE;
    itn_rect.height = THUMB_SIZE;
    GdkRegion *itn_region = gdk_region_rectangle (&itn_rect);
    if ( gdk_region_point_in (itn_region, (int) event->x, (int) event->y) ) {
        result = TRUE;
        //    g_message ("Over input thumbnail!");
    }
    gdk_region_destroy (itn_region);

    return result;
}
Esempio n. 3
0
static void
update_thumbnail_popup_process (GtkWidget *widget, GdkEventMotion *event)
{
    //g_message ("Doing %s, event = %p", __func__, event);

    static gboolean witn = FALSE;	/* "Was in thumbnail".  */
    static GtkTreePath *otp = NULL;
    static GtkTreeViewColumn *otc = NULL;
    static GTimer *hover_timer = NULL;
    if ( hover_timer == NULL ) {
        hover_timer = g_timer_new ();
    }

    /* Fos some crazy reason, drawing the popup image changes the result
    of in_thumbnail (I suspect because gdk is reusing event
    structures in some strange way).  So memorize in_thumbnail up
    front.  */
    // Also, this assertion should pass, but doesn't... synthesizing
    // motion events is apparently a bit harder than I realized.
    // g_assert (event->type == GDK_MOTION_NOTIFY);
    //g_message ("event x: %lf, event y: %lf", event->x, event->y);
    gboolean event_in_thumbnail = in_thumbnail (widget, event);

    /* Hover time in milliseconds required before a popup image is
    displayed.  */
    const gint hover_time = 70;

    // FIXME: It would be nice to allow popup images of both the input
    // and output thumbnails, but it requires more hassle keeping track
    // of where we were, where we are now, etc., so its not going to
    // happen until someone asks.
    if ( !witn && in_input_thumbnail (widget, event) ) {
        witn = TRUE;
        otp = thumbnail_path (widget, event);
        g_assert (gtk_tree_path_get_depth (otp) == 1);
        otc = thumbnail_column (widget, event);
        g_timer_start (hover_timer);
        //g_message ("Adding timeout from !with && in_input_thumbnail");
        fake_motion_signal_args_t *fmsa = g_new (fake_motion_signal_args_t, 1);
        fmsa->widget = widget;
        fmsa->event = event;
        fmsa->is_valid = TRUE;
        g_timeout_add (hover_time, (GSourceFunc) emit_fake_motion_signal, fmsa);
    }

    if ( witn && in_input_thumbnail (widget, event) ) {
        //g_message ("in_thumbnail: %d", in_thumbnail (widget, event));
        GtkTreePath *ctp = thumbnail_path (widget, event);
        g_assert (gtk_tree_path_get_depth (ctp) == 1);
        GtkTreeViewColumn *ctc = thumbnail_column (widget, event);
        if ( gtk_tree_path_compare (ctp, otp) == 0 && ctc == otc ) {
            /* Sometimes the timeout handler seems to go off a bit before
            timer has measured the correct amount of time, so we add a
            small margin here.  */
            const gint slop_time = 5;
            const gint seconds_to_mseconds_factor = 1000;
            /* If we hover for this long or longer, we should show the popup.  */
            if ( g_timer_elapsed (hover_timer, NULL) * seconds_to_mseconds_factor
                >= hover_time - slop_time) {
                    //g_message ("elapsed time: %lf\n", g_timer_elapsed (hover_timer, NULL));
                    //g_message ("Do popup!!!");
                    //g_message ("in_thumbnail: %d", in_thumbnail (widget, event));
                    GdkRegion *tr = thumbnail_region (widget, event);
                    //g_message ("bpw in_thumbnail: %d", in_thumbnail (widget, event));
                    //g_message ("widget: %p", widget);
                    GdkWindow *popup_window = draw_popup_image (widget, ctp, ctc, tr);
                    //g_message ("apw in_thumbnail: %d", in_thumbnail (widget, event));
                    //g_message ("widget: %p", widget);
                    /* We don't want continuous redrawing of the popup, so we
                    disable the handler that triggers it until the popup is
                    removed.  */

                    if (popup_window)
                    {
                        guint signal_id = g_signal_lookup ("motion-notify-event",
                            GTK_WIDGET_TYPE (widget));
                        gulong handler_id
                            = g_signal_handler_find (widget,
                            G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC
                            | G_SIGNAL_MATCH_DATA,
                            signal_id,
                            (GQuark) 0,
                            NULL,
                            files_list_motion_notify_event_handler,
                            NULL);
                        g_assert (handler_id != 0);
                        g_signal_handler_block (widget, handler_id);
                        /* We want to get rid of the popup window as soon as the user
                        moves the mouse outside of the original thumbnail
                        space.  */
                        maybe_clear_popup_image_args.popup = popup_window;
                        maybe_clear_popup_image_args.tree_view = GTK_TREE_VIEW (widget);
                        if ( maybe_clear_popup_image_args.thumbnail_region != NULL ) {
                            gdk_region_destroy (maybe_clear_popup_image_args.thumbnail_region);
                        }
                        //g_message ("in_thumbnail: %d", in_thumbnail (widget, event));
                        maybe_clear_popup_image_args.thumbnail_region = tr;
                        g_signal_connect (widget, "motion-notify-event",
                            G_CALLBACK (maybe_clear_popup_image),
                            &maybe_clear_popup_image_args);
                        //g_message ("in_thumbnail: %d", in_thumbnail (widget, event));
                    }
                }
            else {
                gtk_tree_path_free (ctp);
            }
        }
        else {
            gtk_tree_path_free (otp);
            otp = ctp;
            otc = ctc;
            g_timer_start (hover_timer);
            //g_message ("Adding timeout from 'different thumbnails'");
            fake_motion_signal_args_t *fmsa = g_new (fake_motion_signal_args_t, 1);
            fmsa->widget = widget;
            fmsa->event = event;
            fmsa->is_valid = TRUE;
            g_timeout_add (hover_time, (GSourceFunc) emit_fake_motion_signal, fmsa);
        }
    }

    if ( witn && !event_in_thumbnail ) {
        //g_message ("in_thumbnail: %d", in_thumbnail (widget, event));
        //g_message ("Setting witn false");
        witn = FALSE;
    }
}
Esempio n. 4
0
/* Load image 'path' in the background and insert into pixmap_cache.
 * Call callback(data, path) when done (path is NULL => error).
 * If the image is already uptodate, or being created already, calls the
 * callback right away.
 */
void
pixmap_background_thumb(const gchar *path, GFunc callback, gpointer data)
{
	gboolean	found = FALSE;

	MaskedPixmap* image = g_fscache_lookup_full(pixmap_cache, path, FSCACHE_LOOKUP_ONLY_NEW, &found);

	if (found)
	{
		dbg(0, "found");
		// Thumbnail is known, or being created
		if (image) g_object_unref(image);
		callback(data, NULL);
		return;
	}

	dbg(0, "FIXME not found");
#if 0
	pid_t		child;
	ChildThumbnail	*info;
	g_return_if_fail(image == NULL);

	GdkPixbuf* pixbuf = get_thumbnail_for(path);
	
	if (!pixbuf)
	{
		struct stat info1, info2;
		char *dir;

		dir = g_path_get_dirname(path);

		// If the image itself is in ~/.thumbnails, load it now (ie, don't create thumbnails for thumbnails!).
		if (stat(dir, &info1) != 0)
		{
			callback(data, NULL);
			g_free(dir);
			return;
		}
		g_free(dir);

		if (stat(make_path(home_dir, ".thumbnails/normal"),
			    &info2) == 0 &&
			    info1.st_dev == info2.st_dev &&
			    info1.st_ino == info2.st_ino)
		{
			pixbuf = rox_pixbuf_new_from_file_at_scale(path, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE, TRUE, NULL);
			if (!pixbuf)
			{
				g_fscache_insert(pixmap_cache, path, NULL, TRUE);
				callback(data, NULL);
				return;
			}
		}
	}
		
	if (pixbuf)
	{
		MaskedPixmap *image;

		image = masked_pixmap_new(pixbuf);
		gdk_pixbuf_unref(pixbuf);
		g_fscache_insert(pixmap_cache, path, image, TRUE);
		callback(data, (gchar *) path);
		g_object_unref(G_OBJECT(image));
		return;
	}

	MIME_type* type = type_from_path(path);
	if (!type) type = text_plain;

	// Add an entry, set to NULL, so no-one else tries to load this image.
	g_fscache_insert(pixmap_cache, path, NULL, TRUE);

	gchar* thumb_prog = thumbnail_program(type);

	// Only attempt to load 'images' types ourselves
	if (thumb_prog == NULL && strcmp(type->media_type, "image") != 0)
	{
		callback(data, NULL);
		return;		// Don't know how to handle this type
	}

	child = fork();

	if (child == -1)
	{
		g_free(thumb_prog);
		delayed_error("fork(): %s", g_strerror(errno));
		callback(data, NULL);
		return;
	}

	if (child == 0)
	{
		// We are the child process.  (We are sloppy with freeing memory, but since we go away very quickly, that's ok.)
		if (thumb_prog)
		{
			DirItem *item;
			
			item = diritem_new(g_basename(thumb_prog));

			diritem_restat(thumb_prog, item, NULL);
			if (item->flags & ITEM_FLAG_APPDIR)
				thumb_prog = g_strconcat(thumb_prog, "/AppRun",
						       NULL);

			execl(thumb_prog, thumb_prog, path,
			      thumbnail_path(path),
			      g_strdup_printf("%d", PIXMAP_THUMB_SIZE),
			      NULL);
			_exit(1);
		}

		child_create_thumbnail(path);
		_exit(0);
	}

	g_free(thumb_prog);

	info = g_new(ChildThumbnail, 1);
	info->path = g_strdup(path);
	info->callback = callback;
	info->data = data;
	on_child_death(child, (CallbackFn) thumbnail_child_done, info);
#endif
}